nsBlockFrame.h (44744B)
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 "mozilla/IntrinsicISizesCache.h" 16 #include "nsCSSPseudoElements.h" 17 #include "nsContainerFrame.h" 18 #include "nsFloatManager.h" 19 #include "nsHTMLParts.h" 20 #include "nsLineBox.h" 21 22 enum class LineReflowStatus { 23 // The line was completely reflowed and fit in available width, and we should 24 // try to pull up content from the next line if possible. 25 OK, 26 // The line was completely reflowed and fit in available width, but we should 27 // not try to pull up content from the next line. 28 Stop, 29 // We need to reflow the line again at its current vertical position. The 30 // new reflow should not try to pull up any frames from the next line. 31 RedoNoPull, 32 // We need to reflow the line again using the floats from its height 33 // this reflow, since its height made it hit floats that were not 34 // adjacent to its top. 35 RedoMoreFloats, 36 // We need to reflow the line again at a lower vertical postion where there 37 // may be more horizontal space due to different float configuration. 38 RedoNextBand, 39 // The line did not fit in the available vertical space. Try pushing it to 40 // the next page or column if it's not the first line on the current 41 // page/column. 42 Truncated 43 }; 44 45 class nsBlockInFlowLineIterator; 46 namespace mozilla { 47 class BlockReflowState; 48 class PresShell; 49 class ServoRestyleState; 50 class ServoStyleSet; 51 } // namespace mozilla 52 53 /** 54 * Some invariants: 55 * -- The overflow out-of-flows list contains the out-of- 56 * flow frames whose placeholders are in the overflow list. 57 * -- A given piece of content has at most one placeholder 58 * frame in a block's normal child list. 59 * -- While a block is being reflowed, and from then until 60 * its next-in-flow is reflowed it may have a 61 * PushedFloatProperty frame property that points to 62 * an nsFrameList. This list contains continuations for 63 * floats whose prev-in-flow is in the block's regular float 64 * list and first-in-flows of floats that did not fit, but 65 * whose placeholders are in the block or one of its 66 * prev-in-flows. 67 * -- In all these frame lists, if there are two frames for 68 * the same content appearing in the list, then the frames 69 * appear with the prev-in-flow before the next-in-flow. 70 * -- While reflowing a block, its overflow line list 71 * will usually be empty but in some cases will have lines 72 * (while we reflow the block at its shrink-wrap width). 73 * In this case any new overflowing content must be 74 * prepended to the overflow lines. 75 */ 76 77 /* 78 * Base class for block and inline frames. 79 * The block frame has an additional child list, FrameChildListID::Absolute, 80 * which contains the absolutely positioned frames. 81 */ 82 class nsBlockFrame : public nsContainerFrame { 83 using BlockReflowState = mozilla::BlockReflowState; 84 85 public: 86 NS_DECL_FRAMEARENA_HELPERS(nsBlockFrame) 87 88 typedef nsLineList::iterator LineIterator; 89 typedef nsLineList::const_iterator ConstLineIterator; 90 typedef nsLineList::reverse_iterator ReverseLineIterator; 91 typedef nsLineList::const_reverse_iterator ConstReverseLineIterator; 92 93 LineIterator LinesBegin() { return mLines.begin(); } 94 LineIterator LinesEnd() { return mLines.end(); } 95 ConstLineIterator LinesBegin() const { return mLines.begin(); } 96 ConstLineIterator LinesEnd() const { return mLines.end(); } 97 ReverseLineIterator LinesRBegin() { return mLines.rbegin(); } 98 ReverseLineIterator LinesREnd() { return mLines.rend(); } 99 ConstReverseLineIterator LinesRBegin() const { return mLines.rbegin(); } 100 ConstReverseLineIterator LinesREnd() const { return mLines.rend(); } 101 LineIterator LinesBeginFrom(nsLineBox* aList) { return mLines.begin(aList); } 102 ReverseLineIterator LinesRBeginFrom(nsLineBox* aList) { 103 return mLines.rbegin(aList); 104 } 105 106 // Methods declared to be used in 'range-based-for-loop' 107 nsLineList& Lines() { return mLines; } 108 const nsLineList& Lines() const { return mLines; } 109 110 friend nsBlockFrame* NS_NewBlockFrame(mozilla::PresShell* aPresShell, 111 ComputedStyle* aStyle); 112 113 // nsQueryFrame 114 NS_DECL_QUERYFRAME 115 116 // nsIFrame 117 void Init(nsIContent* aContent, nsContainerFrame* aParent, 118 nsIFrame* aPrevInFlow) override; 119 void SetInitialChildList(ChildListID aListID, 120 nsFrameList&& aChildList) override; 121 void AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) override; 122 void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, 123 const nsLineList::iterator* aPrevFrameLine, 124 nsFrameList&& aFrameList) override; 125 void RemoveFrame(DestroyContext&, ChildListID, nsIFrame* aOldFrame) override; 126 nsContainerFrame* GetContentInsertionFrame() override; 127 void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override; 128 const nsFrameList& GetChildList(ChildListID aListID) const override; 129 void GetChildLists(nsTArray<ChildList>* aLists) const override; 130 nscoord SynthesizeFallbackBaseline( 131 mozilla::WritingMode aWM, 132 BaselineSharingGroup aBaselineGroup) const override; 133 BaselineSharingGroup GetDefaultBaselineSharingGroup() const override { 134 return BaselineSharingGroup::Last; 135 } 136 Maybe<nscoord> GetNaturalBaselineBOffset( 137 mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, 138 BaselineExportContext aExportContext) const override; 139 nscoord GetCaretBaseline() const override; 140 void Destroy(DestroyContext&) override; 141 142 bool IsFloatContainingBlock() const override; 143 void BuildDisplayList(nsDisplayListBuilder* aBuilder, 144 const nsDisplayListSet& aLists) override; 145 146 void InvalidateFrame(uint32_t aDisplayItemKey = 0, 147 bool aRebuildDisplayItems = true) override; 148 void InvalidateFrameWithRect(const nsRect& aRect, 149 uint32_t aDisplayItemKey = 0, 150 bool aRebuildDisplayItems = true) override; 151 152 #ifdef DEBUG_FRAME_DUMP 153 void List(FILE* out = stderr, const char* aPrefix = "", 154 ListFlags aFlags = ListFlags()) const override; 155 nsresult GetFrameName(nsAString& aResult) const override; 156 #endif 157 158 #ifdef ACCESSIBILITY 159 mozilla::a11y::AccType AccessibleType() override; 160 #endif 161 162 // Line cursor methods to speed up line searching in which one query result 163 // is expected to be close to the next in general. This is mainly for 164 // searching line(s) containing a point. It is also used as a cache for local 165 // computation. The basic idea for the former is that we set the cursor 166 // property if the lines' overflowArea.InkOverflow().ys and 167 // overflowArea.InkOverflow().yMosts are non-decreasing 168 // (considering only non-empty overflowArea.InkOverflow()s; empty 169 // overflowArea.InkOverflow()s never participate in event handling 170 // or painting), and the block has sufficient number of lines. The 171 // cursor property points to a "recently used" line. If we get a 172 // series of requests that work on lines 173 // "near" the cursor, then we can find those nearby lines quickly by 174 // starting our search at the cursor. 175 176 // We have two independent line cursors, one used for display-list building 177 // and the other for a11y or other frame queries. Either or both may be 178 // present at any given time. When we reflow or otherwise munge the lines, 179 // both cursors will be cleared. 180 // The display cursor is only created and used if the lines satisfy the non- 181 // decreasing y-coordinate condition (see SetupLineCursorForDisplay comment 182 // below), whereas the query cursor may be created for any block. The two 183 // are separated so creating a cursor for a11y queries (eg GetRenderedText) 184 // does not risk confusing the display-list building code. 185 186 // Clear out line cursors because we're disturbing the lines (i.e., Reflow) 187 void ClearLineCursors() { 188 if (MaybeHasLineCursor()) { 189 ClearLineCursorForDisplay(); 190 ClearLineCursorForQuery(); 191 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR); 192 } 193 ClearLineIterator(); 194 } 195 void ClearLineCursorForDisplay() { 196 RemoveProperty(LineCursorPropertyDisplay()); 197 } 198 void ClearLineCursorForQuery() { RemoveProperty(LineCursorPropertyQuery()); } 199 200 // Clear just the line-iterator property; this is used if we need to get a 201 // LineIterator temporarily during reflow, when using a persisted iterator 202 // would be invalid. So we clear the stored property immediately after use. 203 void ClearLineIterator() { RemoveProperty(LineIteratorProperty()); } 204 205 // Get the first line that might contain y-coord 'y', or nullptr if you must 206 // search all lines. If nonnull is returned then we guarantee that the lines' 207 // combinedArea.ys and combinedArea.yMosts are non-decreasing. 208 // The actual line returned might not contain 'y', but if not, it is 209 // guaranteed to be before any line which does contain 'y'. 210 nsLineBox* GetFirstLineContaining(nscoord y); 211 212 // Ensure the frame has a display-list line cursor, initializing it to the 213 // first line if it is not already present. (If there's an existing cursor, 214 // it is left untouched.) Only call this if you guarantee that the lines' 215 // combinedArea.ys and combinedArea.yMosts are non-decreasing. 216 void SetupLineCursorForDisplay(); 217 218 // Ensure the frame has a query line cursor, initializing it to the first 219 // line if it is not already present. (If there's an existing cursor, it is 220 // left untouched.) 221 void SetupLineCursorForQuery(); 222 223 void ChildIsDirty(nsIFrame* aChild) override; 224 225 bool IsEmpty() override; 226 bool CachedIsEmpty() override; 227 bool IsSelfEmpty() override; 228 bool LinesAreEmpty() const; 229 230 // Given that we have a ::marker frame, does it actually draw something, i.e., 231 // do we have either a 'list-style-type' or 'list-style-image' that is 232 // not 'none', and no 'content'? 233 // This is expected to be used only for outside markers, and when the caller 234 // already has a pointer to the marker frame. 235 bool MarkerIsEmpty(const nsIFrame* aMarker) const; 236 237 // Return true if this frame has a ::marker frame. 238 bool HasMarker() const { return HasAnyStateBits(NS_BLOCK_HAS_MARKER); } 239 240 // Return true if this frame has an outside ::marker frame. 241 bool HasOutsideMarker() const; 242 243 /** 244 * @return the first-letter frame or nullptr if we don't have one. 245 */ 246 nsIFrame* GetFirstLetter() const; 247 248 /** 249 * @return the ::first-line frame or nullptr if we don't have one. 250 */ 251 nsIFrame* GetFirstLineFrame() const; 252 253 void MarkIntrinsicISizesDirty() override; 254 255 private: 256 // Whether CSS text-indent should be applied to the given line. 257 bool TextIndentAppliesTo(const LineIterator& aLine) const; 258 259 void CheckIntrinsicCacheAgainstShrinkWrapState(); 260 261 nsRect ComputePaddingInflatedScrollableOverflow( 262 const nsRect& aInFlowChildBounds) const; 263 Maybe<nsRect> GetLineFrameInFlowBounds( 264 const nsLineBox& aLine, const nsIFrame& aLineChildFrame, 265 bool aConsiderPositiveMargins = true) const; 266 267 template <typename LineIteratorType> 268 Maybe<nscoord> GetBaselineBOffset(LineIteratorType aStart, 269 LineIteratorType aEnd, 270 mozilla::WritingMode aWM, 271 BaselineSharingGroup aBaselineGroup, 272 BaselineExportContext aExportContext) const; 273 274 protected: 275 // MinISize() and PrefISize() are helpers to implement IntrinsicISize(). 276 nscoord MinISize(const mozilla::IntrinsicSizeInput& aInput); 277 nscoord PrefISize(const mozilla::IntrinsicSizeInput& aInput); 278 279 public: 280 nscoord IntrinsicISize(const mozilla::IntrinsicSizeInput& aInput, 281 mozilla::IntrinsicISizeType aType) override; 282 283 nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override; 284 285 nsresult GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX, 286 nscoord* aXMost) override; 287 288 /** 289 * Compute the final block size of this frame. 290 * 291 * @param aState BlockReflowState passed from parent during reflow. 292 * Note: aState.mReflowStatus is mostly an "input" parameter. When this 293 * method is called, it should represent what our status would be as if 294 * we were shrinkwrapping our children's block-size. This method will 295 * then adjust it before returning if our status is different in light 296 * of our actual final block-size and current page/column's available 297 * block-size. 298 * @param aBEndEdgeOfChildren The distance between this frame's block-start 299 * border-edge and the block-end edge of our last child's border-box. 300 * This is effectively our block-start border-padding plus the 301 * block-size of our children, precomputed outside of this function. 302 * @return our final block-size with respect to aReflowInput's writing-mode. 303 */ 304 nscoord ComputeFinalBSize(BlockReflowState& aState, 305 nscoord aBEndEdgeOfChildren); 306 307 void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, 308 const ReflowInput& aReflowInput, 309 nsReflowStatus& aStatus) override; 310 311 /** 312 * Move any frames on our overflow list to the end of our principal list. 313 * @return true if there were any overflow frames 314 */ 315 bool DrainSelfOverflowList() override; 316 317 void StealFrame(nsIFrame* aChild) override; 318 319 void DeleteNextInFlowChild(DestroyContext&, nsIFrame* aNextInFlow, 320 bool aDeletingEmptyFrames) override; 321 322 /** 323 * This is a special method that allows a child class of nsBlockFrame to 324 * return a special, customized nsStyleText object to the nsLineLayout 325 * constructor. It is used when the nsBlockFrame child needs to specify its 326 * custom rendering style. 327 */ 328 virtual const nsStyleText* StyleTextForLineLayout(); 329 330 /** 331 * Determines whether the collapsed margin carried out of the last 332 * line includes the margin-top of a line with clearance (in which 333 * case we must avoid collapsing that margin with our bottom margin) 334 */ 335 bool CheckForCollapsedBEndMarginFromClearanceLine(); 336 337 static nsresult GetCurrentLine(BlockReflowState* aState, 338 nsLineBox** aOutCurrentLine); 339 340 /** 341 * Determine if this block is a margin root at the top/bottom edges. 342 */ 343 void IsMarginRoot(bool* aBStartMarginRoot, bool* aBEndMarginRoot); 344 345 static bool BlockNeedsFloatManager(nsIFrame* aBlock); 346 347 /** 348 * Returns whether aFrame is a block frame that will wrap its contents 349 * around floats intruding on it from the outside. (aFrame need not 350 * be a block frame, but if it's not, the result will be false.) 351 * 352 * Note: We often use the term "float-avoiding block" to refer to 353 * block-level frames for whom this function returns false. 354 */ 355 static bool BlockCanIntersectFloats(nsIFrame* aFrame); 356 357 /** 358 * Returns the inline size that needs to be cleared past floats for 359 * blocks that avoid (i.e. cannot intersect) floats. aState must already 360 * have GetFloatAvailableSpace called on it for the block-dir position that 361 * we care about (which need not be its current mBCoord) 362 */ 363 struct FloatAvoidingISizeToClear { 364 // Note that we care about the inline-start margin but can ignore 365 // the inline-end margin. 366 nscoord marginIStart, borderBoxISize; 367 }; 368 static FloatAvoidingISizeToClear ISizeToClearPastFloats( 369 const BlockReflowState& aState, 370 const mozilla::LogicalRect& aFloatAvailableSpace, 371 nsIFrame* aFloatAvoidingBlock); 372 373 /** 374 * Creates a contination for aFloat and adds it to the list of overflow 375 * floats. Also updates aState.mReflowStatus to include the float's 376 * incompleteness. Must only be called while this block frame is in reflow. 377 * aFloatStatus must be the float's true, unmodified reflow status. 378 */ 379 void SplitFloat(BlockReflowState& aState, nsIFrame* aFloat, 380 const nsReflowStatus& aFloatStatus); 381 382 /** 383 * Walks up the frame tree, starting with aCandidate, and returns the first 384 * block frame that it encounters. 385 */ 386 static nsBlockFrame* GetNearestAncestorBlock(nsIFrame* aCandidate); 387 388 struct FrameLines { 389 nsLineList mLines; 390 nsFrameList mFrames; 391 }; 392 393 /** 394 * Update the styles of our various pseudo-elements (marker, first-line, 395 * etc, but _not_ first-letter). 396 */ 397 void UpdatePseudoElementStyles(mozilla::ServoRestyleState& aRestyleState); 398 399 // Update our first-letter styles during stylo post-traversal. This needs to 400 // be done at a slightly different time than our other pseudo-elements. 401 void UpdateFirstLetterStyle(mozilla::ServoRestyleState& aRestyleState); 402 403 protected: 404 explicit nsBlockFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, 405 ClassID aID = kClassID) 406 : nsContainerFrame(aStyle, aPresContext, aID) { 407 #ifdef DEBUG 408 InitDebugFlags(); 409 #endif 410 } 411 412 virtual ~nsBlockFrame(); 413 414 void DidSetComputedStyle(ComputedStyle* aOldStyle) override; 415 416 #ifdef DEBUG 417 already_AddRefed<ComputedStyle> GetFirstLetterStyle( 418 nsPresContext* aPresContext); 419 #endif 420 421 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorPropertyDisplay, nsLineBox) 422 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorPropertyQuery, nsLineBox) 423 // Note that the NS_BLOCK_HAS_LINE_CURSOR flag does not necessarily mean the 424 // cursor is present, as it covers both the "display" and "query" cursors, 425 // but may remain set if they have been separately deleted. In such a case, 426 // the Get* accessors will be slightly more expensive, but will still safely 427 // return null if the cursor is absent. 428 bool MaybeHasLineCursor() { 429 return HasAnyStateBits(NS_BLOCK_HAS_LINE_CURSOR); 430 } 431 nsLineBox* GetLineCursorForDisplay() { 432 return MaybeHasLineCursor() ? GetProperty(LineCursorPropertyDisplay()) 433 : nullptr; 434 } 435 nsLineBox* GetLineCursorForQuery() { 436 return MaybeHasLineCursor() ? GetProperty(LineCursorPropertyQuery()) 437 : nullptr; 438 } 439 440 void SetLineCursorForDisplay(nsLineBox* aLine) { 441 MOZ_ASSERT(aLine, "must have a line"); 442 MOZ_ASSERT(!mLines.empty(), "aLine isn't my line"); 443 SetProperty(LineCursorPropertyDisplay(), aLine); 444 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR); 445 } 446 447 nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) { 448 return NS_NewLineBox(PresShell(), aFrame, aIsBlock); 449 } 450 nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, 451 int32_t aCount) { 452 return NS_NewLineBox(PresShell(), aFromLine, aFrame, aCount); 453 } 454 void FreeLineBox(nsLineBox* aLine) { 455 if (aLine == GetLineCursorForDisplay()) { 456 ClearLineCursorForDisplay(); 457 } 458 if (aLine == GetLineCursorForQuery()) { 459 ClearLineCursorForQuery(); 460 } 461 aLine->Destroy(PresShell()); 462 } 463 /** 464 * Helper method for StealFrame. 465 */ 466 void RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine, 467 nsFrameList& aFrameList, nsLineList& aLineList); 468 469 void TryAllLines(nsLineList::iterator* aIterator, 470 nsLineList::iterator* aStartIterator, 471 nsLineList::iterator* aEndIterator, bool* aInOverflowLines, 472 FrameLines** aOverflowLines); 473 474 /** move the frames contained by aLine by aDeltaBCoord 475 * if aLine is a block, its child floats are added to the state manager 476 */ 477 void SlideLine(BlockReflowState& aState, nsLineBox* aLine, 478 nscoord aDeltaBCoord); 479 480 void UpdateLineContainerSize(nsLineBox* aLine, 481 const nsSize& aNewContainerSize); 482 483 // helper for SlideLine and UpdateLineContainerSize 484 void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord); 485 486 // Returns block-end edge of children. 487 nscoord ComputeFinalSize(const ReflowInput& aReflowInput, 488 BlockReflowState& aState, ReflowOutput& aMetrics); 489 490 /** 491 * Calculates the necessary shift to honor 'align-content' and applies it. 492 */ 493 void AlignContent(BlockReflowState& aState, ReflowOutput& aMetrics, 494 nscoord aBEndEdgeOfChildren); 495 // Stash the effective align-content shift value between reflows 496 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(AlignContentShift, nscoord) 497 498 /** 499 * Helper method for Reflow(). Computes the overflow areas created by our 500 * children, and includes them into aOverflowAreas. 501 */ 502 void ComputeOverflowAreas(mozilla::OverflowAreas& aOverflowAreas, 503 const nsStyleDisplay* aDisplay) const; 504 505 /** 506 * Add the frames in aFrameList to this block after aPrevSibling. 507 * This block thinks in terms of lines, but the frame construction code 508 * knows nothing about lines at all so we need to find the line that 509 * contains aPrevSibling and add aFrameList after aPrevSibling on that line. 510 * New lines are created as necessary to handle block data in aFrameList. 511 * This function will clear aFrameList. 512 * 513 * aPrevSiblingLine, if present, must be the line containing aPrevSibling. 514 * Providing it will make this function faster. 515 */ 516 void AddFrames(nsFrameList&& aFrameList, nsIFrame* aPrevSibling, 517 const nsLineList::iterator* aPrevSiblingLine); 518 519 // Return the :-moz-block-ruby-content child frame, if any. 520 // (It's non-null only if this block frame is for 'display:block ruby'.) 521 nsContainerFrame* GetRubyContentPseudoFrame(); 522 523 /** 524 * Perform Bidi resolution on this frame 525 */ 526 nsresult ResolveBidi(); 527 528 public: 529 bool IsButtonControlFrame() const { 530 return IsInputButtonControlFrame() || IsColorControlFrame() || 531 IsComboboxControlFrame(); 532 } 533 534 bool IsButtonLike() const { 535 if (mContent->IsHTMLElement(nsGkAtoms::button)) { 536 // NOTE(emilio): We need the IsAnonBox check to deal with things like the 537 // :-moz-anonymous-item of a <button> with display: grid. We don't want 538 // that to e.g. center its contents. But we do want the scrolled-content 539 // box of a button to do it. 540 auto pseudoType = Style()->GetPseudoType(); 541 return !mozilla::PseudoStyle::IsAnonBox(pseudoType) || 542 pseudoType == mozilla::PseudoStyleType::scrolledContent; 543 } 544 return IsButtonControlFrame(); 545 } 546 547 /** Returns the effective align-content of this frame */ 548 mozilla::StyleAlignFlags EffectiveAlignContent() const { 549 if (IsButtonLike()) { 550 return mozilla::StyleAlignFlags::CENTER; 551 } 552 return StylePosition()->mAlignContent.primary; 553 } 554 555 bool IsContentAligned() const { 556 return EffectiveAlignContent() != mozilla::StyleAlignFlags::NORMAL; 557 } 558 559 protected: 560 nscoord GetAlignContentShift() const { 561 return IsContentAligned() ? GetProperty(AlignContentShift()) : 0; 562 } 563 564 /** 565 * For text-wrap:balance, we iteratively try reflowing with adjusted inline 566 * size to find the "best" result (the tightest size that can be applied 567 * without increasing the total line count of the block). 568 * This record is used to manage the state of these "trial reflows", and 569 * return results from the final trial. 570 */ 571 struct TrialReflowState { 572 // Values pre-computed at start of Reflow(), constant across trials. 573 const nscoord mConsumedBSize; 574 const nscoord mEffectiveContentBoxBSize; 575 bool mNeedFloatManager; 576 // [out] Whether reflowing resulted in use of an overflow-wrap break. 577 bool mUsedOverflowWrap = false; 578 // Settings for the current trial. 579 bool mBalancing = false; 580 nscoord mInset = 0; 581 // Results computed during the trial reflow. Values from the final trial 582 // will be used by the remainder of Reflow(). 583 mozilla::OverflowAreas mOcBounds; 584 mozilla::OverflowAreas mFcBounds; 585 nscoord mBlockEndEdgeOfChildren = 0; 586 nscoord mContainerWidth = 0; 587 588 // Initialize for the initial trial reflow, with zero inset. 589 TrialReflowState(nscoord aConsumedBSize, nscoord aEffectiveContentBoxBSize, 590 bool aNeedFloatManager) 591 : mConsumedBSize(aConsumedBSize), 592 mEffectiveContentBoxBSize(aEffectiveContentBoxBSize), 593 mNeedFloatManager(aNeedFloatManager) {} 594 595 // Adjust the inset amount, and reset state for a new trial. 596 void ResetForBalance(nscoord aInsetDelta) { 597 // Tells the reflow-lines loop we must consider all lines "dirty" (as we 598 // are modifying the effective inline-size to be used). 599 mBalancing = true; 600 // Adjust inset to apply. 601 mInset += aInsetDelta; 602 // Re-initialize state that the reflow loop will compute. 603 mOcBounds.Clear(); 604 mFcBounds.Clear(); 605 mBlockEndEdgeOfChildren = 0; 606 mContainerWidth = 0; 607 mUsedOverflowWrap = false; 608 } 609 }; 610 611 /** 612 * Internal helper for Reflow(); may be called repeatedly during a single 613 * Reflow() in order to implement text-wrap:balance. 614 * This method applies aTrialState.mInset during line-breaking to reduce 615 * the effective available inline-size (without affecting alignment). 616 */ 617 nsReflowStatus TrialReflow(nsPresContext* aPresContext, 618 ReflowOutput& aMetrics, 619 const ReflowInput& aReflowInput, 620 TrialReflowState& aTrialState); 621 622 public: 623 /** 624 * Helper function for the frame ctor to register a ::marker frame. 625 */ 626 void SetMarkerFrameForListItem(nsIFrame* aMarkerFrame); 627 628 /** 629 * Does all the real work for removing aDeletedFrame 630 * -- finds the line containing aDeletedFrame 631 * -- removes all aDeletedFrame next-in-flows (or all continuations, 632 * if REMOVE_FIXED_CONTINUATIONS is given) 633 * -- marks lines dirty as needed 634 * -- marks textruns dirty (unless FRAMES_ARE_EMPTY is given, in which 635 * case textruns do not need to be dirtied) 636 * -- destroys all removed frames 637 */ 638 enum { REMOVE_FIXED_CONTINUATIONS = 0x02, FRAMES_ARE_EMPTY = 0x04 }; 639 void DoRemoveFrame(DestroyContext&, nsIFrame* aDeletedFrame, uint32_t aFlags); 640 641 void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, 642 bool aReparentSiblings); 643 644 bool ComputeCustomOverflow(mozilla::OverflowAreas&) override; 645 646 void UnionChildOverflow(mozilla::OverflowAreas&, bool aAsIfScrolled) override; 647 648 /** 649 * Load all of aFrame's floats into the float manager iff aFrame is not a 650 * block formatting context. Handles all necessary float manager translations; 651 * assumes float manager is in aFrame's parent's coord system. 652 * 653 * Safe to call on non-blocks (does nothing). 654 */ 655 static void RecoverFloatsFor(nsIFrame* aFrame, nsFloatManager& aFloatManager, 656 mozilla::WritingMode aWM, 657 const nsSize& aContainerSize); 658 659 /** 660 * Determine if we have any pushed floats from a previous continuation. 661 * 662 * @returns true, if any of the floats at the beginning of our floats list 663 * have the NS_FRAME_IS_PUSHED_OUT_OF_FLOW bit set; false otherwise. 664 */ 665 bool HasPushedFloatsFromPrevContinuation() const; 666 667 // @see nsIFrame::AddSizeOfExcludingThisForTree 668 void AddSizeOfExcludingThisForTree(nsWindowSizes&) const override; 669 670 /** 671 * Clears any -webkit-line-clamp ellipsis on a line in this block or one 672 * of its descendants. 673 */ 674 void ClearLineClampEllipsis(); 675 676 /** 677 * Returns whether this block is in a -webkit-line-clamp context. That is, 678 * whether this block is in a block formatting-context whose root block has 679 * -webkit-line-clamp: <n>. 680 */ 681 bool IsInLineClampContext() const { return !!GetLineClampRoot(); } 682 683 /** 684 * @return false iff this block does not have a float on any child list. 685 * This function is O(1). 686 */ 687 bool MaybeHasFloats() const; 688 /** 689 * This indicates that exactly one line in this block has the 690 * LineClampEllipsis flag set, and that such a line must be found 691 * and have that flag cleared when reflowing this element's nearest legacy box 692 * container. 693 */ 694 bool HasLineClampEllipsis() const { 695 return HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS); 696 } 697 /** 698 * This indicates that we have a descendant in our block formatting context 699 * that has such a line. 700 */ 701 bool HasLineClampEllipsisDescendant() const { 702 return HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS_DESCENDANT); 703 } 704 void SetHasLineClampEllipsis(bool aValue) { 705 AddOrRemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS, aValue); 706 } 707 void SetHasLineClampEllipsisDescendant(bool aValue) { 708 AddOrRemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS_DESCENDANT, aValue); 709 } 710 711 protected: 712 nsBlockFrame* GetLineClampRoot() const; 713 nscoord ApplyLineClamp(nscoord aContentBlockEndEdge); 714 715 /** grab overflow lines from this block's prevInFlow, and make them 716 * part of this block's mLines list. 717 * @return true if any lines were drained. 718 */ 719 bool DrainOverflowLines(); 720 721 /** 722 * Moves frames from our PushedFloats list back into our mFloats list. 723 */ 724 void DrainSelfPushedFloats(); 725 726 /** 727 * First calls DrainSelfPushedFloats() then grabs pushed floats from this 728 * block's prev-in-flow, and splice them into this block's mFloats list too. 729 */ 730 void DrainPushedFloats(); 731 732 /** Load all our floats into the float manager (without reflowing them). 733 * Assumes float manager is in our own coordinate system. 734 */ 735 void RecoverFloats(nsFloatManager& aFloatManager, mozilla::WritingMode aWM, 736 const nsSize& aContainerSize); 737 738 /** Reflow pushed floats 739 */ 740 void ReflowPushedFloats(BlockReflowState& aState, 741 mozilla::OverflowAreas& aOverflowAreas); 742 743 /** 744 * Find any trailing BR clear from the last line of this block (or from its 745 * prev-in-flows). 746 */ 747 mozilla::UsedClear FindTrailingClear(); 748 749 /** 750 * Remove a float from our float list. 751 */ 752 void RemoveFloat(nsIFrame* aFloat); 753 /** 754 * Remove a float from the float cache for the line its placeholder is on. 755 */ 756 void RemoveFloatFromFloatCache(nsIFrame* aFloat); 757 758 void CollectFloats(nsIFrame* aFrame, nsFrameList& aList, 759 bool aCollectFromSiblings) { 760 if (MaybeHasFloats()) { 761 DoCollectFloats(aFrame, aList, aCollectFromSiblings); 762 } 763 } 764 void DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList, 765 bool aCollectFromSiblings); 766 767 // Remove a float and its continuations. 768 static void DoRemoveFloats(DestroyContext&, nsIFrame*); 769 770 /** set up the conditions necessary for an resize reflow 771 * the primary task is to mark the minimumly sufficient lines dirty. 772 */ 773 void PrepareResizeReflow(BlockReflowState& aState); 774 775 /** 776 * Reflow all lines that have been marked dirty. 777 * Returns whether an overflow-wrap break was used anywhere. 778 */ 779 bool ReflowDirtyLines(BlockReflowState& aState); 780 781 /** Mark a given line dirty due to reflow being interrupted on or before it */ 782 void MarkLineDirtyForInterrupt(nsLineBox* aLine); 783 784 //---------------------------------------- 785 // Methods for line reflow 786 /** 787 * Reflow a line. 788 * 789 * @param aState 790 * the current reflow input 791 * @param aLine 792 * the line to reflow. can contain a single block frame or contain 1 or 793 * more inline frames. 794 * @param aKeepReflowGoing [OUT] 795 * indicates whether the caller should continue to reflow more lines 796 * @returns 797 * whether an overflow-wrap breakpoint was used 798 */ 799 bool ReflowLine(BlockReflowState& aState, LineIterator aLine, 800 bool* aKeepReflowGoing); 801 802 // Return false if it needs another reflow because of reduced space 803 // between floats that are next to it (but not next to its top), and 804 // return true otherwise. 805 bool PlaceLine(BlockReflowState& aState, nsLineLayout& aLineLayout, 806 LineIterator aLine, 807 nsFloatManager::SavedState* aFloatStateBeforeLine, 808 nsFlowAreaRect& aFlowArea, // in-out 809 nscoord& aAvailableSpaceBSize, // in-out 810 bool* aKeepReflowGoing); 811 812 /** 813 * If NS_BLOCK_LOOK_FOR_DIRTY_FRAMES is set, call MarkLineDirty 814 * on any line with a child frame that is dirty. 815 */ 816 void LazyMarkLinesDirty(); 817 818 /** 819 * Mark |aLine| dirty, and, if necessary because of possible 820 * pull-up, mark the previous line dirty as well. Also invalidates textruns 821 * on those lines because the text in the lines might have changed due to 822 * addition/removal of frames. 823 * @param aLine the line to mark dirty 824 * @param aLineList the line list containing that line 825 */ 826 void MarkLineDirty(LineIterator aLine, const nsLineList* aLineList); 827 828 // XXX where to go 829 bool IsLastLine(BlockReflowState& aState, LineIterator aLine); 830 831 void DeleteLine(BlockReflowState& aState, nsLineList::iterator aLine, 832 nsLineList::iterator aLineEnd); 833 834 //---------------------------------------- 835 // Methods for individual frame reflow 836 837 bool ShouldApplyBStartMargin(BlockReflowState& aState, nsLineBox* aLine); 838 839 void ReflowBlockFrame(BlockReflowState& aState, LineIterator aLine, 840 bool* aKeepGoing); 841 842 // Returns whether an overflow-wrap break was used. 843 bool ReflowInlineFrames(BlockReflowState& aState, LineIterator aLine, 844 bool* aKeepLineGoing); 845 846 void DoReflowInlineFrames( 847 BlockReflowState& aState, nsLineLayout& aLineLayout, LineIterator aLine, 848 nsFlowAreaRect& aFloatAvailableSpace, nscoord& aAvailableSpaceBSize, 849 nsFloatManager::SavedState* aFloatStateBeforeLine, bool* aKeepReflowGoing, 850 LineReflowStatus* aLineReflowStatus, bool aAllowPullUp); 851 852 void ReflowInlineFrame(BlockReflowState& aState, nsLineLayout& aLineLayout, 853 LineIterator aLine, nsIFrame* aFrame, 854 LineReflowStatus* aLineReflowStatus); 855 856 // @param aReflowStatus an incomplete status indicates the float should be 857 // split but only if the available block-size is constrained. 858 void ReflowFloat(BlockReflowState& aState, ReflowInput& aFloatRI, 859 nsIFrame* aFloat, nsReflowStatus& aReflowStatus); 860 861 //---------------------------------------- 862 // Methods for pushing/pulling lines/frames 863 864 /** 865 * Create a next-in-flow, if necessary, for aFrame. If a new frame is 866 * created, place it in aLine if aLine is not null. 867 * @param aState the block reflow state 868 * @param aLine where to put a new frame 869 * @param aFrame the frame 870 * @return true if a new frame was created, false if not 871 */ 872 bool CreateContinuationFor(BlockReflowState& aState, nsLineBox* aLine, 873 nsIFrame* aFrame); 874 875 /** 876 * Set line-break-before status in aState.mReflowStatus because aLine cannot 877 * be placed on this page/column and we don't want to break within ourselves. 878 * Also, mark the aLine dirty, and set aKeepReflowGoing to false; 879 */ 880 void SetBreakBeforeStatusBeforeLine(BlockReflowState& aState, 881 LineIterator aLine, 882 bool* aKeepReflowGoing); 883 884 /** 885 * Indicates if we need to compute a page name for the next page when pushing 886 * a truncated line. 887 * 888 * Using a value of No saves work when a new page name has already been set 889 * with nsCSSFrameConstructor::SetNextPageContentFramePageName. 890 */ 891 enum class ComputeNewPageNameIfNeeded : uint8_t { Yes, No }; 892 893 /** 894 * Push aLine (and any after it), since it cannot be placed on this 895 * page/column. Set aKeepReflowGoing to false and set 896 * flag aState.mReflowStatus as incomplete. 897 */ 898 void PushTruncatedLine(BlockReflowState& aState, LineIterator aLine, 899 bool* aKeepReflowGoing, 900 ComputeNewPageNameIfNeeded aComputeNewPageName = 901 ComputeNewPageNameIfNeeded::Yes); 902 903 void SplitLine(BlockReflowState& aState, nsLineLayout& aLineLayout, 904 LineIterator aLine, nsIFrame* aFrame, 905 LineReflowStatus* aLineReflowStatus); 906 907 /** 908 * Pull a frame from the next available location (one of our lines or 909 * one of our next-in-flows lines). 910 * @return the pulled frame or nullptr 911 */ 912 nsIFrame* PullFrame(BlockReflowState& aState, LineIterator aLine); 913 914 /** 915 * Try to pull a frame out of a line pointed at by aFromLine. 916 * 917 * Note: pulling a frame from a line that is a place-holder frame 918 * doesn't automatically remove the corresponding float from the 919 * line's float array. This happens indirectly: either the line gets 920 * emptied (and destroyed) or the line gets reflowed (because we mark 921 * it dirty) and the code at the top of ReflowLine empties the 922 * array. So eventually, it will be removed, just not right away. 923 * 924 * @return the pulled frame or nullptr 925 */ 926 nsIFrame* PullFrameFrom(nsLineBox* aLine, nsBlockFrame* aFromContainer, 927 nsLineList::iterator aFromLine); 928 929 /** 930 * Push the line after aLineBefore to the overflow line list. 931 * @param aLineBefore a line in 'mLines' (or LinesBegin() when 932 * pushing the first line) 933 */ 934 void PushLines(BlockReflowState& aState, nsLineList::iterator aLineBefore); 935 936 void PropagateFloatDamage(BlockReflowState& aState, nsLineBox* aLine, 937 nscoord aDeltaBCoord); 938 939 void CheckFloats(BlockReflowState& aState); 940 941 //---------------------------------------- 942 // List handling kludge 943 944 void ReflowOutsideMarker(nsIFrame* aMarkerFrame, BlockReflowState& aState, 945 ReflowOutput& aMetrics, nscoord aLineTop); 946 947 //---------------------------------------- 948 949 NS_DECLARE_FRAME_PROPERTY_DELETABLE(LineIteratorProperty, nsLineIterator); 950 951 bool CanProvideLineIterator() const final { return true; } 952 nsILineIterator* GetLineIterator() final; 953 954 public: 955 bool HasOverflowLines() const { 956 return HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); 957 } 958 FrameLines* GetOverflowLines() const; 959 960 protected: 961 FrameLines* RemoveOverflowLines(); 962 void SetOverflowLines(FrameLines* aOverflowLines); 963 void DestroyOverflowLines(); 964 965 /** 966 * This class is useful for efficiently modifying the out of flow 967 * overflow list. It gives the client direct writable access to 968 * the frame list temporarily but ensures that property is only 969 * written back if absolutely necessary. 970 */ 971 struct nsAutoOOFFrameList { 972 nsFrameList mList; 973 974 explicit nsAutoOOFFrameList(nsBlockFrame* aBlock) 975 : mPropValue(aBlock->GetOverflowOutOfFlows()), mBlock(aBlock) { 976 if (mPropValue) { 977 mList = std::move(*mPropValue); 978 } 979 } 980 ~nsAutoOOFFrameList() { 981 mBlock->SetOverflowOutOfFlows(std::move(mList), mPropValue); 982 } 983 984 protected: 985 nsFrameList* const mPropValue; 986 nsBlockFrame* const mBlock; 987 }; 988 friend struct nsAutoOOFFrameList; 989 990 nsFrameList* GetOverflowOutOfFlows() const; 991 992 // This takes ownership of the frames in aList. 993 void SetOverflowOutOfFlows(nsFrameList&& aList, nsFrameList* aPropValue); 994 995 // Return the ::marker frame or nullptr if we don't have one. 996 nsIFrame* GetMarker() const { 997 nsIFrame* outside = GetOutsideMarker(); 998 return outside ? outside : GetInsideMarker(); 999 } 1000 1001 // Return the inside ::marker frame or nullptr if we don't have one. 1002 nsIFrame* GetInsideMarker() const; 1003 1004 // Return the outside ::marker frame or nullptr if we don't have one. 1005 nsIFrame* GetOutsideMarker() const; 1006 1007 // Return the outside ::marker frame list frame property. 1008 nsFrameList* GetOutsideMarkerList() const; 1009 1010 // Return true if this frame has floats. 1011 bool HasFloats() const; 1012 1013 // Get the floats list, or nullptr if there isn't one. 1014 nsFrameList* GetFloats() const; 1015 1016 // Get the floats list, or if there is not currently one, make a new empty 1017 // one. 1018 nsFrameList* EnsureFloats() MOZ_NONNULL_RETURN; 1019 1020 // Get the float list and remove the property from this frame. 1021 // 1022 // The caller is responsible for deleting the returned list and managing the 1023 // ownership of all frames in the list. 1024 [[nodiscard]] nsFrameList* StealFloats(); 1025 1026 // Return true if this frame has pushed floats. 1027 bool HasPushedFloats() const; 1028 1029 // Get the pushed floats list, or nullptr if there isn't one. 1030 // 1031 // The pushed floats list is used for *temporary* storage of floats during 1032 // reflow, between when we decide they don't fit in this block until our next 1033 // continuation takes them. 1034 nsFrameList* GetPushedFloats() const; 1035 1036 // Get the pushed floats list, or if there is not currently one, 1037 // make a new empty one. 1038 nsFrameList* EnsurePushedFloats() MOZ_NONNULL_RETURN; 1039 1040 // Get the pushed float list and remove the property from this frame. 1041 // 1042 // The caller is responsible for deleting the returned list and managing the 1043 // ownership of all frames in the list. 1044 [[nodiscard]] nsFrameList* StealPushedFloats(); 1045 1046 #ifdef DEBUG 1047 void VerifyLines(bool aFinalCheckOK); 1048 void VerifyOverflowSituation(); 1049 int32_t GetDepth() const; 1050 #endif 1051 1052 mozilla::IntrinsicISizesCache mCachedIntrinsics; 1053 1054 nsLineList mLines; 1055 1056 friend class mozilla::BlockReflowState; 1057 friend class nsBlockInFlowLineIterator; 1058 1059 #ifdef DEBUG 1060 public: 1061 static bool gLamePaintMetrics; 1062 static bool gLameReflowMetrics; 1063 static bool gNoisy; 1064 static bool gNoisyDamageRepair; 1065 static bool gNoisyIntrinsic; 1066 static bool gNoisyReflow; 1067 static bool gReallyNoisyReflow; 1068 static bool gNoisyFloatManager; 1069 static bool gVerifyLines; 1070 static bool gDisableResizeOpt; 1071 1072 static int32_t gNoiseIndent; 1073 1074 protected: 1075 static void InitDebugFlags(); 1076 #endif 1077 }; 1078 1079 #ifdef DEBUG 1080 class AutoNoisyIndenter { 1081 public: 1082 explicit AutoNoisyIndenter(bool aDoIndent) : mIndented(aDoIndent) { 1083 if (mIndented) { 1084 nsBlockFrame::gNoiseIndent++; 1085 } 1086 } 1087 ~AutoNoisyIndenter() { 1088 if (mIndented) { 1089 nsBlockFrame::gNoiseIndent--; 1090 } 1091 } 1092 1093 private: 1094 bool mIndented; 1095 }; 1096 #endif 1097 1098 /** 1099 * Iterates over all lines in the prev-in-flows/next-in-flows of this block. 1100 */ 1101 class nsBlockInFlowLineIterator { 1102 public: 1103 typedef nsBlockFrame::LineIterator LineIterator; 1104 /** 1105 * Set up the iterator to point to aLine which must be a normal line 1106 * in aFrame (not an overflow line). 1107 */ 1108 nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine); 1109 /** 1110 * Set up the iterator to point to the first line found starting from 1111 * aFrame. Sets aFoundValidLine to false if there is no such line. 1112 * After aFoundValidLine has returned false, don't call any methods on this 1113 * object again. 1114 */ 1115 nsBlockInFlowLineIterator(nsBlockFrame* aFrame, bool* aFoundValidLine); 1116 /** 1117 * Set up the iterator to point to the line that contains aFindFrame (either 1118 * directly or indirectly). If aFrame is out of flow, or contained in an 1119 * out-of-flow, finds the line containing the out-of-flow's placeholder. If 1120 * the frame is not found, sets aFoundValidLine to false. After 1121 * aFoundValidLine has returned false, don't call any methods on this 1122 * object again. 1123 */ 1124 nsBlockInFlowLineIterator(nsBlockFrame* aFrame, nsIFrame* aFindFrame, 1125 bool* aFoundValidLine); 1126 1127 // Allow to be uninitialized (and then assigned from another object). 1128 nsBlockInFlowLineIterator() : mFrame(nullptr) {} 1129 1130 LineIterator GetLine() { return mLine; } 1131 bool IsLastLineInList(); 1132 nsBlockFrame* GetContainer() { return mFrame; } 1133 bool GetInOverflow() { return mLineList != &mFrame->mLines; } 1134 1135 /** 1136 * Returns the current line list we're iterating, null means 1137 * we're iterating |mLines| of the container. 1138 */ 1139 nsLineList* GetLineList() { return mLineList; } 1140 1141 /** 1142 * Returns the end-iterator of whatever line list we're in. 1143 */ 1144 LineIterator End(); 1145 1146 /** 1147 * Returns false if there are no more lines. After this has returned false, 1148 * don't call any methods on this object again. 1149 */ 1150 bool Next(); 1151 /** 1152 * Returns false if there are no more lines. After this has returned false, 1153 * don't call any methods on this object again. 1154 */ 1155 bool Prev(); 1156 1157 // XXX nsBlockFrame uses this internally in one place. Try to remove it. 1158 // XXX uhm, and nsBidiPresUtils::Resolve too. 1159 nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine, 1160 bool aInOverflow); 1161 1162 private: 1163 nsBlockFrame* mFrame; 1164 LineIterator mLine; 1165 nsLineList* mLineList; // the line list mLine is in 1166 1167 /** 1168 * Moves iterator to next valid line reachable from the current block. 1169 * Returns false if there are no valid lines. 1170 */ 1171 bool FindValidLine(); 1172 }; 1173 1174 #endif /* nsBlockFrame_h___ */