Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * rendering object for CSS display:block, inline-block, and list-item
9 : * boxes, also used for various anonymous boxes
10 : */
11 :
12 : #ifndef nsBlockFrame_h___
13 : #define nsBlockFrame_h___
14 :
15 : #include "nsContainerFrame.h"
16 : #include "nsHTMLParts.h"
17 : #include "nsLineBox.h"
18 : #include "nsCSSPseudoElements.h"
19 : #include "nsFloatManager.h"
20 :
21 : enum class LineReflowStatus {
22 : // The line was completely reflowed and fit in available width, and we should
23 : // try to pull up content from the next line if possible.
24 : OK,
25 : // The line was completely reflowed and fit in available width, but we should
26 : // not try to pull up content from the next line.
27 : Stop,
28 : // We need to reflow the line again at its current vertical position. The
29 : // new reflow should not try to pull up any frames from the next line.
30 : RedoNoPull,
31 : // We need to reflow the line again using the floats from its height
32 : // this reflow, since its height made it hit floats that were not
33 : // adjacent to its top.
34 : RedoMoreFloats,
35 : // We need to reflow the line again at a lower vertical postion where there
36 : // may be more horizontal space due to different float configuration.
37 : RedoNextBand,
38 : // The line did not fit in the available vertical space. Try pushing it to
39 : // the next page or column if it's not the first line on the current page/column.
40 : Truncated
41 : };
42 :
43 : class nsBlockInFlowLineIterator;
44 : class nsBulletFrame;
45 : namespace mozilla {
46 : class BlockReflowInput;
47 : class ServoRestyleState;
48 : class StyleSetHandle;
49 : } // namespace mozilla
50 :
51 : /**
52 : * Some invariants:
53 : * -- The overflow out-of-flows list contains the out-of-
54 : * flow frames whose placeholders are in the overflow list.
55 : * -- A given piece of content has at most one placeholder
56 : * frame in a block's normal child list.
57 : * -- While a block is being reflowed, and from then until
58 : * its next-in-flow is reflowed it may have a
59 : * PushedFloatProperty frame property that points to
60 : * an nsFrameList. This list contains continuations for
61 : * floats whose prev-in-flow is in the block's regular float
62 : * list and first-in-flows of floats that did not fit, but
63 : * whose placeholders are in the block or one of its
64 : * prev-in-flows.
65 : * -- In all these frame lists, if there are two frames for
66 : * the same content appearing in the list, then the frames
67 : * appear with the prev-in-flow before the next-in-flow.
68 : * -- While reflowing a block, its overflow line list
69 : * will usually be empty but in some cases will have lines
70 : * (while we reflow the block at its shrink-wrap width).
71 : * In this case any new overflowing content must be
72 : * prepended to the overflow lines.
73 : */
74 :
75 : /*
76 : * Base class for block and inline frames.
77 : * The block frame has an additional child list, kAbsoluteList, which
78 : * contains the absolutely positioned frames.
79 : */
80 : class nsBlockFrame : public nsContainerFrame
81 : {
82 : using BlockReflowInput = mozilla::BlockReflowInput;
83 :
84 : public:
85 214 : NS_DECL_FRAMEARENA_HELPERS(nsBlockFrame)
86 :
87 : typedef nsLineList::iterator LineIterator;
88 : typedef nsLineList::const_iterator ConstLineIterator;
89 : typedef nsLineList::reverse_iterator ReverseLineIterator;
90 : typedef nsLineList::const_reverse_iterator ConstReverseLineIterator;
91 :
92 992 : LineIterator LinesBegin() { return mLines.begin(); }
93 1140 : LineIterator LinesEnd() { return mLines.end(); }
94 35 : ConstLineIterator LinesBegin() const { return mLines.begin(); }
95 35 : ConstLineIterator LinesEnd() const { return mLines.end(); }
96 : ReverseLineIterator LinesRBegin() { return mLines.rbegin(); }
97 0 : ReverseLineIterator LinesREnd() { return mLines.rend(); }
98 30 : ConstReverseLineIterator LinesRBegin() const { return mLines.rbegin(); }
99 30 : ConstReverseLineIterator LinesREnd() const { return mLines.rend(); }
100 0 : LineIterator LinesBeginFrom(nsLineBox* aList) { return mLines.begin(aList); }
101 0 : ReverseLineIterator LinesRBeginFrom(nsLineBox* aList) { return mLines.rbegin(aList); }
102 :
103 : friend nsBlockFrame* NS_NewBlockFrame(nsIPresShell* aPresShell,
104 : nsStyleContext* aContext);
105 :
106 : // nsQueryFrame
107 : NS_DECL_QUERYFRAME
108 :
109 : // nsIFrame
110 : void Init(nsIContent* aContent,
111 : nsContainerFrame* aParent,
112 : nsIFrame* aPrevInFlow) override;
113 : void SetInitialChildList(ChildListID aListID,
114 : nsFrameList& aChildList) override;
115 : void AppendFrames(ChildListID aListID,
116 : nsFrameList& aFrameList) override;
117 : void InsertFrames(ChildListID aListID,
118 : nsIFrame* aPrevFrame,
119 : nsFrameList& aFrameList) override;
120 : void RemoveFrame(ChildListID aListID,
121 : nsIFrame* aOldFrame) override;
122 : const nsFrameList& GetChildList(ChildListID aListID) const override;
123 : void GetChildLists(nsTArray<ChildList>* aLists) const override;
124 : nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
125 0 : bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
126 : nscoord* aBaseline) const override
127 : {
128 : nscoord lastBaseline;
129 0 : if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
130 0 : *aBaseline = BSize() - lastBaseline;
131 0 : return true;
132 : }
133 0 : return false;
134 : }
135 : bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
136 : BaselineSharingGroup aBaselineGroup,
137 : nscoord* aBaseline) const override;
138 : nscoord GetCaretBaseline() const override;
139 : void DestroyFrom(nsIFrame* aDestructRoot) override;
140 : nsSplittableType GetSplittableType() const override;
141 : bool IsFloatContainingBlock() const override;
142 : void BuildDisplayList(nsDisplayListBuilder* aBuilder,
143 : const nsRect& aDirtyRect,
144 : const nsDisplayListSet& aLists) override;
145 2380 : bool IsFrameOfType(uint32_t aFlags) const override
146 : {
147 2380 : return nsContainerFrame::IsFrameOfType(aFlags &
148 : ~(nsIFrame::eCanContainOverflowContainers |
149 2380 : nsIFrame::eBlockFrame));
150 : }
151 :
152 : void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
153 : void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
154 :
155 : #ifdef DEBUG_FRAME_DUMP
156 : void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
157 : nsresult GetFrameName(nsAString& aResult) const override;
158 : #endif
159 :
160 : #ifdef DEBUG
161 : nsFrameState GetDebugStateBits() const override;
162 : const char* LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const;
163 : #endif
164 :
165 : #ifdef ACCESSIBILITY
166 : mozilla::a11y::AccType AccessibleType() override;
167 : #endif
168 :
169 : // Line cursor methods to speed up line searching in which one query
170 : // result is expected to be close to the next in general. This is
171 : // mainly for searching line(s) containing a point. It is also used
172 : // as a cache for local computation. Use AutoLineCursorSetup for the
173 : // latter case so that it wouldn't interact unexpectedly with the
174 : // former. The basic idea for the former is that we set the cursor
175 : // property if the lines' overflowArea.VisualOverflow().ys and
176 : // overflowArea.VisualOverflow().yMosts are non-decreasing
177 : // (considering only non-empty overflowArea.VisualOverflow()s; empty
178 : // overflowArea.VisualOverflow()s never participate in event handling
179 : // or painting), and the block has sufficient number of lines. The
180 : // cursor property points to a "recently used" line. If we get a
181 : // series of requests that work on lines
182 : // "near" the cursor, then we can find those nearby lines quickly by
183 : // starting our search at the cursor.
184 :
185 : // Clear out line cursor because we're disturbing the lines (i.e., Reflow)
186 : void ClearLineCursor();
187 : // Get the first line that might contain y-coord 'y', or nullptr if you must search
188 : // all lines. If nonnull is returned then we guarantee that the lines'
189 : // combinedArea.ys and combinedArea.yMosts are non-decreasing.
190 : // The actual line returned might not contain 'y', but if not, it is guaranteed
191 : // to be before any line which does contain 'y'.
192 : nsLineBox* GetFirstLineContaining(nscoord y);
193 : // Set the line cursor to our first line. Only call this if you
194 : // guarantee that either the lines' combinedArea.ys and combinedArea.
195 : // yMosts are non-decreasing, or the line cursor is cleared before
196 : // building the display list of this frame.
197 : void SetupLineCursor();
198 :
199 : /**
200 : * Helper RAII class for automatically set and clear line cursor for
201 : * temporary use. If the frame already has line cursor, this would be
202 : * a no-op.
203 : */
204 : class MOZ_STACK_CLASS AutoLineCursorSetup
205 : {
206 : public:
207 0 : explicit AutoLineCursorSetup(nsBlockFrame* aFrame)
208 0 : : mFrame(aFrame)
209 0 : , mOrigCursor(aFrame->GetLineCursor())
210 : {
211 0 : if (!mOrigCursor) {
212 0 : mFrame->SetupLineCursor();
213 : }
214 0 : }
215 0 : ~AutoLineCursorSetup()
216 0 : {
217 0 : if (mOrigCursor) {
218 0 : mFrame->SetProperty(LineCursorProperty(), mOrigCursor);
219 : } else {
220 0 : mFrame->ClearLineCursor();
221 : }
222 0 : }
223 :
224 : private:
225 : nsBlockFrame* mFrame;
226 : nsLineBox* mOrigCursor;
227 : };
228 :
229 : void ChildIsDirty(nsIFrame* aChild) override;
230 : bool IsVisibleInSelection(nsISelection* aSelection) override;
231 :
232 : bool IsEmpty() override;
233 : bool CachedIsEmpty() override;
234 : bool IsSelfEmpty() override;
235 :
236 : // Given that we have a bullet, does it actually draw something, i.e.,
237 : // do we have either a 'list-style-type' or 'list-style-image' that is
238 : // not 'none'?
239 : bool BulletIsEmpty() const;
240 :
241 : /**
242 : * Return the bullet text equivalent.
243 : */
244 : void GetSpokenBulletText(nsAString& aText) const;
245 :
246 : /**
247 : * Return true if there's a bullet.
248 : */
249 0 : bool HasBullet() const {
250 0 : return HasOutsideBullet() || HasInsideBullet();
251 : }
252 :
253 : /**
254 : * @return true if this frame has an inside bullet frame.
255 : */
256 33 : bool HasInsideBullet() const {
257 33 : return 0 != (mState & NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
258 : }
259 :
260 : /**
261 : * @return true if this frame has an outside bullet frame.
262 : */
263 1125 : bool HasOutsideBullet() const {
264 1125 : return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
265 : }
266 :
267 : /**
268 : * @return the bullet frame or nullptr if we don't have one.
269 : */
270 0 : nsBulletFrame* GetBullet() const {
271 0 : nsBulletFrame* outside = GetOutsideBullet();
272 0 : return outside ? outside : GetInsideBullet();
273 : }
274 :
275 : /**
276 : * @return the first-letter frame or nullptr if we don't have one.
277 : */
278 : nsIFrame* GetFirstLetter() const;
279 :
280 : void MarkIntrinsicISizesDirty() override;
281 : private:
282 : void CheckIntrinsicCacheAgainstShrinkWrapState();
283 : public:
284 : nscoord GetMinISize(gfxContext *aRenderingContext) override;
285 : nscoord GetPrefISize(gfxContext *aRenderingContext) override;
286 :
287 : nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
288 :
289 : nsresult GetPrefWidthTightBounds(gfxContext* aContext,
290 : nscoord* aX,
291 : nscoord* aXMost) override;
292 :
293 : /**
294 : * Compute the final block size of this frame.
295 : *
296 : * @param aReflowInput Data structure passed from parent during reflow.
297 : * @param aReflowStatus A pointer to the reflow status for when we're finished
298 : * doing reflow. this will get set appropriately if the block-size
299 : * causes us to exceed the current available (page) block-size.
300 : * @param aContentBSize The block-size of content, precomputed outside of this
301 : * function. The final block-size that is used in aMetrics will be set
302 : * to either this or the available block-size, whichever is larger, in
303 : * the case where our available block-size is constrained, and we
304 : * overflow that available block-size.
305 : * @param aBorderPadding The margins representing the border padding for block
306 : * frames. Can be 0.
307 : * @param aFinalSize Out parameter for final block-size.
308 : * @param aConsumed The block-size already consumed by our previous-in-flows.
309 : */
310 : void ComputeFinalBSize(const ReflowInput& aReflowInput,
311 : nsReflowStatus* aStatus,
312 : nscoord aContentBSize,
313 : const mozilla::LogicalMargin& aBorderPadding,
314 : mozilla::LogicalSize& aFinalSize,
315 : nscoord aConsumed);
316 :
317 : void Reflow(nsPresContext* aPresContext,
318 : ReflowOutput& aDesiredSize,
319 : const ReflowInput& aReflowInput,
320 : nsReflowStatus& aStatus) override;
321 :
322 : nsresult AttributeChanged(int32_t aNameSpaceID,
323 : nsIAtom* aAttribute,
324 : int32_t aModType) override;
325 :
326 : /**
327 : * Move any frames on our overflow list to the end of our principal list.
328 : * @return true if there were any overflow frames
329 : */
330 : bool DrainSelfOverflowList() override;
331 :
332 : nsresult StealFrame(nsIFrame* aChild) override;
333 :
334 : void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
335 : bool aDeletingEmptyFrames) override;
336 :
337 : /**
338 : * This is a special method that allows a child class of nsBlockFrame to
339 : * return a special, customized nsStyleText object to the nsLineLayout
340 : * constructor. It is used when the nsBlockFrame child needs to specify its
341 : * custom rendering style.
342 : */
343 : virtual const nsStyleText* StyleTextForLineLayout();
344 :
345 : /**
346 : * Determines whether the collapsed margin carried out of the last
347 : * line includes the margin-top of a line with clearance (in which
348 : * case we must avoid collapsing that margin with our bottom margin)
349 : */
350 : bool CheckForCollapsedBEndMarginFromClearanceLine();
351 :
352 : static nsresult GetCurrentLine(BlockReflowInput *aState, nsLineBox **aOutCurrentLine);
353 :
354 : /**
355 : * Determine if this block is a margin root at the top/bottom edges.
356 : */
357 : void IsMarginRoot(bool* aBStartMarginRoot, bool* aBEndMarginRoot);
358 :
359 : static bool BlockNeedsFloatManager(nsIFrame* aBlock);
360 :
361 : /**
362 : * Returns whether aFrame is a block frame that will wrap its contents
363 : * around floats intruding on it from the outside. (aFrame need not
364 : * be a block frame, but if it's not, the result will be false.)
365 : */
366 : static bool BlockCanIntersectFloats(nsIFrame* aFrame);
367 :
368 : /**
369 : * Returns the inline size that needs to be cleared past floats for
370 : * blocks that cannot intersect floats. aState must already have
371 : * GetAvailableSpace called on it for the block-dir position that we
372 : * care about (which need not be its current mBCoord)
373 : */
374 : struct ReplacedElementISizeToClear {
375 : // Note that we care about the inline-start margin but can ignore
376 : // the inline-end margin.
377 : nscoord marginIStart, borderBoxISize;
378 : };
379 : static ReplacedElementISizeToClear
380 : ISizeToClearPastFloats(const BlockReflowInput& aState,
381 : const mozilla::LogicalRect& aFloatAvailableSpace,
382 : nsIFrame* aFrame);
383 :
384 : /**
385 : * Creates a contination for aFloat and adds it to the list of overflow floats.
386 : * Also updates aState.mReflowStatus to include the float's incompleteness.
387 : * Must only be called while this block frame is in reflow.
388 : * aFloatStatus must be the float's true, unmodified reflow status.
389 : */
390 : nsresult SplitFloat(BlockReflowInput& aState,
391 : nsIFrame* aFloat,
392 : nsReflowStatus aFloatStatus);
393 :
394 : /**
395 : * Walks up the frame tree, starting with aCandidate, and returns the first
396 : * block frame that it encounters.
397 : */
398 : static nsBlockFrame* GetNearestAncestorBlock(nsIFrame* aCandidate);
399 :
400 0 : struct FrameLines {
401 : nsLineList mLines;
402 : nsFrameList mFrames;
403 : };
404 :
405 : /**
406 : * Update the styles of our various pseudo-elements (bullets, first-line,
407 : * etc, but _not_ first-letter).
408 : */
409 : void UpdatePseudoElementStyles(mozilla::ServoRestyleState& aRestyleState);
410 :
411 : // Update our first-letter styles during stylo post-traversal. This needs to
412 : // be done at a slightly different time than our other pseudo-elements.
413 : void UpdateFirstLetterStyle(mozilla::ServoRestyleState& aRestyleState);
414 :
415 : protected:
416 34 : explicit nsBlockFrame(nsStyleContext* aContext, ClassID aID = kClassID)
417 34 : : nsContainerFrame(aContext, aID)
418 : , mMinWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
419 34 : , mPrefWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
420 : {
421 : #ifdef DEBUG
422 34 : InitDebugFlags();
423 : #endif
424 34 : }
425 :
426 : virtual ~nsBlockFrame();
427 :
428 : #ifdef DEBUG
429 : already_AddRefed<nsStyleContext> GetFirstLetterStyle(nsPresContext* aPresContext);
430 : #endif
431 :
432 0 : NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorProperty, nsLineBox)
433 336 : bool HasLineCursor() { return GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR; }
434 336 : nsLineBox* GetLineCursor() {
435 336 : return HasLineCursor() ? GetProperty(LineCursorProperty()) : nullptr;
436 : }
437 :
438 33 : nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) {
439 33 : return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock);
440 : }
441 0 : nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) {
442 0 : return NS_NewLineBox(PresContext()->PresShell(), aFromLine, aFrame, aCount);
443 : }
444 6 : void FreeLineBox(nsLineBox* aLine) {
445 6 : if (aLine == GetLineCursor()) {
446 0 : ClearLineCursor();
447 : }
448 6 : aLine->Destroy(PresContext()->PresShell());
449 6 : }
450 : /**
451 : * Helper method for StealFrame.
452 : */
453 : void RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
454 : nsFrameList& aFrameList, nsLineList& aLineList);
455 :
456 : void TryAllLines(nsLineList::iterator* aIterator,
457 : nsLineList::iterator* aStartIterator,
458 : nsLineList::iterator* aEndIterator,
459 : bool* aInOverflowLines,
460 : FrameLines** aOverflowLines);
461 :
462 : /** move the frames contained by aLine by aDeltaBCoord
463 : * if aLine is a block, its child floats are added to the state manager
464 : */
465 : void SlideLine(BlockReflowInput& aState,
466 : nsLineBox* aLine, nscoord aDeltaBCoord);
467 :
468 : void UpdateLineContainerSize(nsLineBox* aLine,
469 : const nsSize& aNewContainerSize);
470 :
471 : // helper for SlideLine and UpdateLineContainerSize
472 : void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord);
473 :
474 : void ComputeFinalSize(const ReflowInput& aReflowInput,
475 : BlockReflowInput& aState,
476 : ReflowOutput& aMetrics,
477 : nscoord* aBottomEdgeOfChildren);
478 :
479 : void ComputeOverflowAreas(const nsRect& aBounds,
480 : const nsStyleDisplay* aDisplay,
481 : nscoord aBottomEdgeOfChildren,
482 : nsOverflowAreas& aOverflowAreas);
483 :
484 : /**
485 : * Add the frames in aFrameList to this block after aPrevSibling.
486 : * This block thinks in terms of lines, but the frame construction code
487 : * knows nothing about lines at all so we need to find the line that
488 : * contains aPrevSibling and add aFrameList after aPrevSibling on that line.
489 : * New lines are created as necessary to handle block data in aFrameList.
490 : * This function will clear aFrameList.
491 : */
492 : void AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling);
493 :
494 : /**
495 : * Perform Bidi resolution on this frame
496 : */
497 : nsresult ResolveBidi();
498 :
499 : /**
500 : * Test whether the frame is a form control in a visual Bidi page.
501 : * This is necessary for backwards-compatibility, because most visual
502 : * pages use logical order for form controls so that they will
503 : * display correctly on native widgets in OSs with Bidi support
504 : * @param aPresContext the pres context
505 : * @return whether the frame is a BIDI form control
506 : */
507 : bool IsVisualFormControl(nsPresContext* aPresContext);
508 :
509 : /**
510 : * Helper function to create bullet frame.
511 : * @param aCreateBulletList true to create bullet list; otherwise number list.
512 : * @param aListStylePositionInside true to put the list position inside;
513 : * otherwise outside.
514 : */
515 : void CreateBulletFrameForListItem(bool aCreateBulletList,
516 : bool aListStylePositionInside);
517 :
518 : public:
519 : /**
520 : * Does all the real work for removing aDeletedFrame
521 : * -- finds the line containing aDeletedFrame
522 : * -- removes all aDeletedFrame next-in-flows (or all continuations,
523 : * if REMOVE_FIXED_CONTINUATIONS is given)
524 : * -- marks lines dirty as needed
525 : * -- marks textruns dirty (unless FRAMES_ARE_EMPTY is given, in which
526 : * case textruns do not need to be dirtied)
527 : * -- destroys all removed frames
528 : */
529 : enum {
530 : REMOVE_FIXED_CONTINUATIONS = 0x02,
531 : FRAMES_ARE_EMPTY = 0x04
532 : };
533 : void DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags);
534 :
535 : void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
536 : bool aReparentSiblings);
537 :
538 : virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
539 :
540 : virtual void UnionChildOverflow(nsOverflowAreas& aOverflowAreas) override;
541 :
542 : /** Load all of aFrame's floats into the float manager iff aFrame is not a
543 : * block formatting context. Handles all necessary float manager translations;
544 : * assumes float manager is in aFrame's parent's coord system.
545 : * Safe to call on non-blocks (does nothing).
546 : */
547 : static void RecoverFloatsFor(nsIFrame* aFrame,
548 : nsFloatManager& aFloatManager,
549 : mozilla::WritingMode aWM,
550 : const nsSize& aContainerSize);
551 :
552 : /**
553 : * Determine if we have any pushed floats from a previous continuation.
554 : *
555 : * @returns true, if any of the floats at the beginning of our mFloats list
556 : * have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise.
557 : */
558 0 : bool HasPushedFloatsFromPrevContinuation() const {
559 0 : if (!mFloats.IsEmpty()) {
560 : // If we have pushed floats, then they should be at the beginning of our
561 : // float list.
562 0 : if (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
563 0 : return true;
564 : }
565 : }
566 :
567 : #ifdef DEBUG
568 : // Double-check the above assertion that pushed floats should be at the
569 : // beginning of our floats list.
570 0 : for (nsFrameList::Enumerator e(mFloats); !e.AtEnd(); e.Next()) {
571 0 : nsIFrame* f = e.get();
572 0 : NS_ASSERTION(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
573 : "pushed floats must be at the beginning of the float list");
574 : }
575 : #endif
576 0 : return false;
577 : }
578 :
579 : virtual bool RenumberChildFrames(int32_t* aOrdinal,
580 : int32_t aDepth,
581 : int32_t aIncrement,
582 : bool aForCounting) override;
583 : protected:
584 :
585 : /** grab overflow lines from this block's prevInFlow, and make them
586 : * part of this block's mLines list.
587 : * @return true if any lines were drained.
588 : */
589 : bool DrainOverflowLines();
590 :
591 : /**
592 : * @return false iff this block does not have a float on any child list.
593 : * This function is O(1).
594 : */
595 0 : bool MaybeHasFloats() const {
596 0 : if (!mFloats.IsEmpty()) {
597 0 : return true;
598 : }
599 : // XXX this could be replaced with HasPushedFloats() if we enforced
600 : // removing the property when the frame list becomes empty.
601 0 : nsFrameList* list = GetPushedFloats();
602 0 : if (list && !list->IsEmpty()) {
603 0 : return true;
604 : }
605 : // For the OverflowOutOfFlowsProperty I think we do enforce that, but it's
606 : // a mix of out-of-flow frames, so that's why the method name has "Maybe".
607 0 : return GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS;
608 : }
609 :
610 : /**
611 : * Moves frames from our PushedFloats list back into our mFloats list.
612 : */
613 : void DrainSelfPushedFloats();
614 :
615 : /**
616 : * First calls DrainSelfPushedFloats() then grabs pushed floats from this
617 : * block's prev-in-flow, and splice them into this block's mFloats list too.
618 : */
619 : void DrainPushedFloats();
620 :
621 : /** Load all our floats into the float manager (without reflowing them).
622 : * Assumes float manager is in our own coordinate system.
623 : */
624 : void RecoverFloats(nsFloatManager& aFloatManager,
625 : mozilla::WritingMode aWM,
626 : const nsSize& aContainerSize);
627 :
628 : /** Reflow pushed floats
629 : */
630 : void ReflowPushedFloats(BlockReflowInput& aState,
631 : nsOverflowAreas& aOverflowAreas,
632 : nsReflowStatus& aStatus);
633 :
634 : /** Find any trailing BR clear from the last line of the block (or its PIFs)
635 : */
636 : mozilla::StyleClear FindTrailingClear();
637 :
638 : /**
639 : * Remove a float from our float list.
640 : */
641 : void RemoveFloat(nsIFrame* aFloat);
642 : /**
643 : * Remove a float from the float cache for the line its placeholder is on.
644 : */
645 : void RemoveFloatFromFloatCache(nsIFrame* aFloat);
646 :
647 0 : void CollectFloats(nsIFrame* aFrame, nsFrameList& aList,
648 : bool aCollectFromSiblings) {
649 0 : if (MaybeHasFloats()) {
650 0 : DoCollectFloats(aFrame, aList, aCollectFromSiblings);
651 : }
652 0 : }
653 : void DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
654 : bool aCollectFromSiblings);
655 :
656 : // Remove a float, abs, rel positioned frame from the appropriate block's list
657 : static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame);
658 :
659 : /** set up the conditions necessary for an resize reflow
660 : * the primary task is to mark the minimumly sufficient lines dirty.
661 : */
662 : void PrepareResizeReflow(BlockReflowInput& aState);
663 :
664 : /** reflow all lines that have been marked dirty */
665 : void ReflowDirtyLines(BlockReflowInput& aState);
666 :
667 : /** Mark a given line dirty due to reflow being interrupted on or before it */
668 : void MarkLineDirtyForInterrupt(nsLineBox* aLine);
669 :
670 : //----------------------------------------
671 : // Methods for line reflow
672 : /**
673 : * Reflow a line.
674 : * @param aState the current reflow state
675 : * @param aLine the line to reflow. can contain a single block frame
676 : * or contain 1 or more inline frames.
677 : * @param aKeepReflowGoing [OUT] indicates whether the caller should continue to reflow more lines
678 : */
679 : void ReflowLine(BlockReflowInput& aState,
680 : LineIterator aLine,
681 : bool* aKeepReflowGoing);
682 :
683 : // Return false if it needs another reflow because of reduced space
684 : // between floats that are next to it (but not next to its top), and
685 : // return true otherwise.
686 : bool PlaceLine(BlockReflowInput& aState,
687 : nsLineLayout& aLineLayout,
688 : LineIterator aLine,
689 : nsFloatManager::SavedState* aFloatStateBeforeLine,
690 : mozilla::LogicalRect& aFloatAvailableSpace, //in-out
691 : nscoord& aAvailableSpaceBSize, // in-out
692 : bool* aKeepReflowGoing);
693 :
694 : /**
695 : * If NS_BLOCK_LOOK_FOR_DIRTY_FRAMES is set, call MarkLineDirty
696 : * on any line with a child frame that is dirty.
697 : */
698 : void LazyMarkLinesDirty();
699 :
700 : /**
701 : * Mark |aLine| dirty, and, if necessary because of possible
702 : * pull-up, mark the previous line dirty as well. Also invalidates textruns
703 : * on those lines because the text in the lines might have changed due to
704 : * addition/removal of frames.
705 : * @param aLine the line to mark dirty
706 : * @param aLineList the line list containing that line
707 : */
708 : void MarkLineDirty(LineIterator aLine, const nsLineList* aLineList);
709 :
710 : // XXX where to go
711 : bool IsLastLine(BlockReflowInput& aState,
712 : LineIterator aLine);
713 :
714 : void DeleteLine(BlockReflowInput& aState,
715 : nsLineList::iterator aLine,
716 : nsLineList::iterator aLineEnd);
717 :
718 : //----------------------------------------
719 : // Methods for individual frame reflow
720 :
721 : bool ShouldApplyBStartMargin(BlockReflowInput& aState,
722 : nsLineBox* aLine,
723 : nsIFrame* aChildFrame);
724 :
725 : void ReflowBlockFrame(BlockReflowInput& aState,
726 : LineIterator aLine,
727 : bool* aKeepGoing);
728 :
729 : void ReflowInlineFrames(BlockReflowInput& aState,
730 : LineIterator aLine,
731 : bool* aKeepLineGoing);
732 :
733 : void DoReflowInlineFrames(BlockReflowInput& aState,
734 : nsLineLayout& aLineLayout,
735 : LineIterator aLine,
736 : nsFlowAreaRect& aFloatAvailableSpace,
737 : nscoord& aAvailableSpaceBSize,
738 : nsFloatManager::SavedState* aFloatStateBeforeLine,
739 : bool* aKeepReflowGoing,
740 : LineReflowStatus* aLineReflowStatus,
741 : bool aAllowPullUp);
742 :
743 : void ReflowInlineFrame(BlockReflowInput& aState,
744 : nsLineLayout& aLineLayout,
745 : LineIterator aLine,
746 : nsIFrame* aFrame,
747 : LineReflowStatus* aLineReflowStatus);
748 :
749 : // Compute the available inline size for a float.
750 : mozilla::LogicalRect AdjustFloatAvailableSpace(
751 : BlockReflowInput& aState,
752 : const mozilla::LogicalRect& aFloatAvailableSpace,
753 : nsIFrame* aFloatFrame);
754 : // Computes the border-box inline size of the float
755 : nscoord ComputeFloatISize(BlockReflowInput& aState,
756 : const mozilla::LogicalRect& aFloatAvailableSpace,
757 : nsIFrame* aFloat);
758 : // An incomplete aReflowStatus indicates the float should be split
759 : // but only if the available height is constrained.
760 : // aAdjustedAvailableSpace is the result of calling
761 : // nsBlockFrame::AdjustFloatAvailableSpace.
762 : void ReflowFloat(BlockReflowInput& aState,
763 : const mozilla::LogicalRect& aAdjustedAvailableSpace,
764 : nsIFrame* aFloat,
765 : mozilla::LogicalMargin& aFloatMargin,
766 : mozilla::LogicalMargin& aFloatOffsets,
767 : // Whether the float's position
768 : // (aAdjustedAvailableSpace) has been pushed down
769 : // due to the presence of other floats.
770 : bool aFloatPushedDown,
771 : nsReflowStatus& aReflowStatus);
772 :
773 : //----------------------------------------
774 : // Methods for pushing/pulling lines/frames
775 :
776 : /**
777 : * Create a next-in-flow, if necessary, for aFrame. If a new frame is
778 : * created, place it in aLine if aLine is not null.
779 : * @param aState the block reflow state
780 : * @param aLine where to put a new frame
781 : * @param aFrame the frame
782 : * @return true if a new frame was created, false if not
783 : */
784 : bool CreateContinuationFor(BlockReflowInput& aState,
785 : nsLineBox* aLine,
786 : nsIFrame* aFrame);
787 :
788 : /**
789 : * Push aLine (and any after it), since it cannot be placed on this
790 : * page/column. Set aKeepReflowGoing to false and set
791 : * flag aState.mReflowStatus as incomplete.
792 : */
793 : void PushTruncatedLine(BlockReflowInput& aState,
794 : LineIterator aLine,
795 : bool* aKeepReflowGoing);
796 :
797 : void SplitLine(BlockReflowInput& aState,
798 : nsLineLayout& aLineLayout,
799 : LineIterator aLine,
800 : nsIFrame* aFrame,
801 : LineReflowStatus* aLineReflowStatus);
802 :
803 : /**
804 : * Pull a frame from the next available location (one of our lines or
805 : * one of our next-in-flows lines).
806 : * @return the pulled frame or nullptr
807 : */
808 : nsIFrame* PullFrame(BlockReflowInput& aState,
809 : LineIterator aLine);
810 :
811 : /**
812 : * Try to pull a frame out of a line pointed at by aFromLine.
813 : *
814 : * Note: pulling a frame from a line that is a place-holder frame
815 : * doesn't automatically remove the corresponding float from the
816 : * line's float array. This happens indirectly: either the line gets
817 : * emptied (and destroyed) or the line gets reflowed (because we mark
818 : * it dirty) and the code at the top of ReflowLine empties the
819 : * array. So eventually, it will be removed, just not right away.
820 : *
821 : * @return the pulled frame or nullptr
822 : */
823 : nsIFrame* PullFrameFrom(nsLineBox* aLine,
824 : nsBlockFrame* aFromContainer,
825 : nsLineList::iterator aFromLine);
826 :
827 : /**
828 : * Push the line after aLineBefore to the overflow line list.
829 : * @param aLineBefore a line in 'mLines' (or LinesBegin() when
830 : * pushing the first line)
831 : */
832 : void PushLines(BlockReflowInput& aState,
833 : nsLineList::iterator aLineBefore);
834 :
835 : void PropagateFloatDamage(BlockReflowInput& aState,
836 : nsLineBox* aLine,
837 : nscoord aDeltaBCoord);
838 :
839 : void CheckFloats(BlockReflowInput& aState);
840 :
841 : //----------------------------------------
842 : // List handling kludge
843 :
844 : void ReflowBullet(nsIFrame* aBulletFrame,
845 : BlockReflowInput& aState,
846 : ReflowOutput& aMetrics,
847 : nscoord aLineTop);
848 :
849 : //----------------------------------------
850 :
851 : virtual nsILineIterator* GetLineIterator() override;
852 :
853 : public:
854 955 : bool HasOverflowLines() const {
855 955 : return 0 != (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES);
856 : }
857 : FrameLines* GetOverflowLines() const;
858 : protected:
859 : FrameLines* RemoveOverflowLines();
860 : void SetOverflowLines(FrameLines* aOverflowLines);
861 : void DestroyOverflowLines();
862 :
863 : /**
864 : * This class is useful for efficiently modifying the out of flow
865 : * overflow list. It gives the client direct writable access to
866 : * the frame list temporarily but ensures that property is only
867 : * written back if absolutely necessary.
868 : */
869 : struct nsAutoOOFFrameList {
870 : nsFrameList mList;
871 :
872 0 : explicit nsAutoOOFFrameList(nsBlockFrame* aBlock)
873 0 : : mPropValue(aBlock->GetOverflowOutOfFlows())
874 0 : , mBlock(aBlock) {
875 0 : if (mPropValue) {
876 0 : mList = *mPropValue;
877 : }
878 0 : }
879 0 : ~nsAutoOOFFrameList() {
880 0 : mBlock->SetOverflowOutOfFlows(mList, mPropValue);
881 0 : }
882 : protected:
883 : nsFrameList* const mPropValue;
884 : nsBlockFrame* const mBlock;
885 : };
886 : friend struct nsAutoOOFFrameList;
887 :
888 : nsFrameList* GetOverflowOutOfFlows() const;
889 : void SetOverflowOutOfFlows(const nsFrameList& aList, nsFrameList* aPropValue);
890 :
891 : /**
892 : * @return the inside bullet frame or nullptr if we don't have one.
893 : */
894 : nsBulletFrame* GetInsideBullet() const;
895 :
896 : /**
897 : * @return the outside bullet frame or nullptr if we don't have one.
898 : */
899 : nsBulletFrame* GetOutsideBullet() const;
900 :
901 : /**
902 : * @return the outside bullet frame list frame property.
903 : */
904 : nsFrameList* GetOutsideBulletList() const;
905 :
906 : /**
907 : * @return true if this frame has pushed floats.
908 : */
909 1174 : bool HasPushedFloats() const {
910 1174 : return 0 != (GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS);
911 : }
912 :
913 : // Get the pushed floats list, which is used for *temporary* storage
914 : // of floats during reflow, between when we decide they don't fit in
915 : // this block until our next continuation takes them.
916 : nsFrameList* GetPushedFloats() const;
917 : // Get the pushed floats list, or if there is not currently one,
918 : // make a new empty one.
919 : nsFrameList* EnsurePushedFloats();
920 : // Remove and return the pushed floats list.
921 : nsFrameList* RemovePushedFloats();
922 :
923 : // Resolve a style context for our bullet frame. aType should be
924 : // mozListBullet or mozListNumber. Passing in the style set is an
925 : // optimization, because all callsites have it.
926 : already_AddRefed<nsStyleContext> ResolveBulletStyle(
927 : mozilla::CSSPseudoElementType aType,
928 : mozilla::StyleSetHandle aStyleSet);
929 :
930 : #ifdef DEBUG
931 : void VerifyLines(bool aFinalCheckOK);
932 : void VerifyOverflowSituation();
933 : int32_t GetDepth() const;
934 : #endif
935 :
936 : nscoord mMinWidth, mPrefWidth;
937 :
938 : nsLineList mLines;
939 :
940 : // List of all floats in this block
941 : // XXXmats blocks rarely have floats, make it a frame property
942 : nsFrameList mFloats;
943 :
944 : friend class mozilla::BlockReflowInput;
945 : friend class nsBlockInFlowLineIterator;
946 :
947 : #ifdef DEBUG
948 : public:
949 : static bool gLamePaintMetrics;
950 : static bool gLameReflowMetrics;
951 : static bool gNoisy;
952 : static bool gNoisyDamageRepair;
953 : static bool gNoisyIntrinsic;
954 : static bool gNoisyReflow;
955 : static bool gReallyNoisyReflow;
956 : static bool gNoisyFloatManager;
957 : static bool gVerifyLines;
958 : static bool gDisableResizeOpt;
959 :
960 : static int32_t gNoiseIndent;
961 :
962 : static const char* kReflowCommandType[];
963 :
964 : protected:
965 : static void InitDebugFlags();
966 : #endif
967 : };
968 :
969 : #ifdef DEBUG
970 : class AutoNoisyIndenter {
971 : public:
972 686 : explicit AutoNoisyIndenter(bool aDoIndent) : mIndented(aDoIndent) {
973 686 : if (mIndented) {
974 0 : nsBlockFrame::gNoiseIndent++;
975 : }
976 686 : }
977 1372 : ~AutoNoisyIndenter() {
978 686 : if (mIndented) {
979 0 : nsBlockFrame::gNoiseIndent--;
980 : }
981 686 : }
982 : private:
983 : bool mIndented;
984 : };
985 : #endif
986 :
987 : /**
988 : * Iterates over all lines in the prev-in-flows/next-in-flows of this block.
989 : */
990 42 : class nsBlockInFlowLineIterator {
991 : public:
992 : typedef nsBlockFrame::LineIterator LineIterator;
993 : /**
994 : * Set up the iterator to point to aLine which must be a normal line
995 : * in aFrame (not an overflow line).
996 : */
997 : nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine);
998 : /**
999 : * Set up the iterator to point to the first line found starting from
1000 : * aFrame. Sets aFoundValidLine to false if there is no such line.
1001 : * After aFoundValidLine has returned false, don't call any methods on this
1002 : * object again.
1003 : */
1004 : nsBlockInFlowLineIterator(nsBlockFrame* aFrame, bool* aFoundValidLine);
1005 : /**
1006 : * Set up the iterator to point to the line that contains aFindFrame (either
1007 : * directly or indirectly). If aFrame is out of flow, or contained in an
1008 : * out-of-flow, finds the line containing the out-of-flow's placeholder. If
1009 : * the frame is not found, sets aFoundValidLine to false. After
1010 : * aFoundValidLine has returned false, don't call any methods on this
1011 : * object again.
1012 : */
1013 : nsBlockInFlowLineIterator(nsBlockFrame* aFrame, nsIFrame* aFindFrame,
1014 : bool* aFoundValidLine);
1015 :
1016 69 : LineIterator GetLine() { return mLine; }
1017 : bool IsLastLineInList();
1018 0 : nsBlockFrame* GetContainer() { return mFrame; }
1019 42 : bool GetInOverflow() { return mLineList != &mFrame->mLines; }
1020 :
1021 : /**
1022 : * Returns the current line list we're iterating, null means
1023 : * we're iterating |mLines| of the container.
1024 : */
1025 : nsLineList* GetLineList() { return mLineList; }
1026 :
1027 : /**
1028 : * Returns the end-iterator of whatever line list we're in.
1029 : */
1030 : LineIterator End();
1031 :
1032 : /**
1033 : * Returns false if there are no more lines. After this has returned false,
1034 : * don't call any methods on this object again.
1035 : */
1036 : bool Next();
1037 : /**
1038 : * Returns false if there are no more lines. After this has returned false,
1039 : * don't call any methods on this object again.
1040 : */
1041 : bool Prev();
1042 :
1043 : private:
1044 : friend class nsBlockFrame;
1045 : friend class nsBidiPresUtils;
1046 : // XXX nsBlockFrame uses this internally in one place. Try to remove it.
1047 : // XXX uhm, and nsBidiPresUtils::Resolve too.
1048 : nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine, bool aInOverflow);
1049 :
1050 : nsBlockFrame* mFrame;
1051 : LineIterator mLine;
1052 : nsLineList* mLineList; // the line list mLine is in
1053 :
1054 : /**
1055 : * Moves iterator to next valid line reachable from the current block.
1056 : * Returns false if there are no valid lines.
1057 : */
1058 : bool FindValidLine();
1059 : };
1060 :
1061 : #endif /* nsBlockFrame_h___ */
|