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