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