nsContainerFrame.cpp (114733B)
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 /* base class #1 for rendering objects that have child lists */ 8 9 #include "nsContainerFrame.h" 10 11 #include <algorithm> 12 13 #include "AnchorPositioningUtils.h" 14 #include "CSSAlignUtils.h" 15 #include "mozilla/AbsoluteContainingBlock.h" 16 #include "mozilla/AutoRestore.h" 17 #include "mozilla/ComputedStyle.h" 18 #include "mozilla/PresShell.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/dom/HTMLSummaryElement.h" 21 #include "mozilla/gfx/2D.h" 22 #include "mozilla/gfx/Types.h" 23 #include "mozilla/webrender/WebRenderAPI.h" 24 #include "mozilla/widget/InitData.h" 25 #include "nsAttrValue.h" 26 #include "nsAttrValueInlines.h" 27 #include "nsBlockFrame.h" 28 #include "nsCOMPtr.h" 29 #include "nsCSSFrameConstructor.h" 30 #include "nsCSSRendering.h" 31 #include "nsCanvasFrame.h" 32 #include "nsContainerFrameInlines.h" 33 #include "nsDisplayList.h" 34 #include "nsError.h" 35 #include "nsFlexContainerFrame.h" 36 #include "nsFrameSelection.h" 37 #include "nsGkAtoms.h" 38 #include "nsIBaseWindow.h" 39 #include "nsIFrameInlines.h" 40 #include "nsIWidget.h" 41 #include "nsPlaceholderFrame.h" 42 #include "nsPoint.h" 43 #include "nsPresContext.h" 44 #include "nsPrintfCString.h" 45 #include "nsRect.h" 46 #include "nsStyleConsts.h" 47 48 using namespace mozilla; 49 using namespace mozilla::dom; 50 using namespace mozilla::layout; 51 52 using mozilla::gfx::ColorPattern; 53 using mozilla::gfx::DeviceColor; 54 using mozilla::gfx::DrawTarget; 55 using mozilla::gfx::Rect; 56 using mozilla::gfx::sRGBColor; 57 using mozilla::gfx::ToDeviceColor; 58 59 nsContainerFrame::~nsContainerFrame() = default; 60 61 NS_QUERYFRAME_HEAD(nsContainerFrame) 62 NS_QUERYFRAME_ENTRY(nsContainerFrame) 63 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame) 64 65 void nsContainerFrame::SetInitialChildList(ChildListID aListID, 66 nsFrameList&& aChildList) { 67 #ifdef DEBUG 68 nsIFrame::VerifyDirtyBitSet(aChildList); 69 for (nsIFrame* f : aChildList) { 70 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent"); 71 } 72 #endif 73 if (aListID == FrameChildListID::Principal) { 74 MOZ_ASSERT(mFrames.IsEmpty(), 75 "unexpected second call to SetInitialChildList"); 76 mFrames = std::move(aChildList); 77 } else { 78 MOZ_ASSERT_UNREACHABLE("Unexpected child list"); 79 } 80 } 81 82 void nsContainerFrame::AppendFrames(ChildListID aListID, 83 nsFrameList&& aFrameList) { 84 MOZ_ASSERT(aListID == FrameChildListID::Principal || 85 aListID == FrameChildListID::NoReflowPrincipal, 86 "unexpected child list"); 87 88 if (MOZ_UNLIKELY(aFrameList.IsEmpty())) { 89 return; 90 } 91 92 DrainSelfOverflowList(); // ensure the last frame is in mFrames 93 mFrames.AppendFrames(this, std::move(aFrameList)); 94 95 if (aListID != FrameChildListID::NoReflowPrincipal) { 96 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors, 97 NS_FRAME_HAS_DIRTY_CHILDREN); 98 } 99 } 100 101 void nsContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, 102 const nsLineList::iterator* aPrevFrameLine, 103 nsFrameList&& aFrameList) { 104 MOZ_ASSERT(aListID == FrameChildListID::Principal || 105 aListID == FrameChildListID::NoReflowPrincipal, 106 "unexpected child list"); 107 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, 108 "inserting after sibling frame with different parent"); 109 110 if (MOZ_UNLIKELY(aFrameList.IsEmpty())) { 111 return; 112 } 113 114 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames 115 mFrames.InsertFrames(this, aPrevFrame, std::move(aFrameList)); 116 117 if (aListID != FrameChildListID::NoReflowPrincipal) { 118 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors, 119 NS_FRAME_HAS_DIRTY_CHILDREN); 120 } 121 } 122 123 void nsContainerFrame::RemoveFrame(DestroyContext& aContext, 124 ChildListID aListID, nsIFrame* aOldFrame) { 125 MOZ_ASSERT(aListID == FrameChildListID::Principal || 126 aListID == FrameChildListID::NoReflowPrincipal, 127 "unexpected child list"); 128 129 AutoTArray<nsIFrame*, 10> continuations; 130 { 131 nsIFrame* continuation = aOldFrame; 132 while (continuation) { 133 continuations.AppendElement(continuation); 134 continuation = continuation->GetNextContinuation(); 135 } 136 } 137 138 mozilla::PresShell* presShell = PresShell(); 139 nsContainerFrame* lastParent = nullptr; 140 141 // Loop and destroy aOldFrame and all of its continuations. 142 // 143 // Request a reflow on the parent frames involved unless we were explicitly 144 // told not to (FrameChildListID::NoReflowPrincipal). 145 const bool generateReflowCommand = 146 aListID != FrameChildListID::NoReflowPrincipal; 147 for (nsIFrame* continuation : Reversed(continuations)) { 148 nsContainerFrame* parent = continuation->GetParent(); 149 150 // Please note that 'parent' may not actually be where 'continuation' lives. 151 // We really MUST use StealFrame() and nothing else here. 152 // @see nsInlineFrame::StealFrame for details. 153 parent->StealFrame(continuation); 154 continuation->Destroy(aContext); 155 if (generateReflowCommand && parent != lastParent) { 156 presShell->FrameNeedsReflow(parent, IntrinsicDirty::FrameAndAncestors, 157 NS_FRAME_HAS_DIRTY_CHILDREN); 158 lastParent = parent; 159 } 160 } 161 } 162 163 void nsContainerFrame::DestroyAbsoluteFrames(DestroyContext& aContext) { 164 if (auto* absCB = GetAbsoluteContainingBlock()) { 165 absCB->DestroyFrames(aContext); 166 MarkAsNotAbsoluteContainingBlock(); 167 } 168 } 169 170 void nsContainerFrame::SafelyDestroyFrameListProp( 171 DestroyContext& aContext, mozilla::PresShell* aPresShell, 172 FrameListPropertyDescriptor aProp) { 173 // Note that the last frame can be removed through another route and thus 174 // delete the property -- that's why we fetch the property again before 175 // removing each frame rather than fetching it once and iterating the list. 176 while (nsFrameList* frameList = GetProperty(aProp)) { 177 // Note: Similar to nsFrameList::DestroyFrames(), we remove the frames in 178 // reverse order to avoid unnecessary updates to the first-continuation and 179 // first-in-flow cache. If we delete them from front to back, updating the 180 // cache has a O(n^2) time complexity. 181 nsIFrame* frame = frameList->RemoveLastChild(); 182 if (MOZ_LIKELY(frame)) { 183 frame->Destroy(aContext); 184 } else { 185 (void)TakeProperty(aProp); 186 frameList->Delete(aPresShell); 187 return; 188 } 189 } 190 } 191 192 void nsContainerFrame::Destroy(DestroyContext& aContext) { 193 DestroyAbsoluteFrames(aContext); 194 195 // Destroy frames on the principal child list. 196 mFrames.DestroyFrames(aContext); 197 198 // If we have any IB split siblings, clear their references to us. 199 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) { 200 // Delete previous sibling's reference to me. 201 if (nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling())) { 202 NS_WARNING_ASSERTION( 203 this == prevSib->GetProperty(nsIFrame::IBSplitSibling()), 204 "IB sibling chain is inconsistent"); 205 prevSib->RemoveProperty(nsIFrame::IBSplitSibling()); 206 } 207 208 // Delete next sibling's reference to me. 209 if (nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling())) { 210 NS_WARNING_ASSERTION( 211 this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()), 212 "IB sibling chain is inconsistent"); 213 nextSib->RemoveProperty(nsIFrame::IBSplitPrevSibling()); 214 } 215 216 #ifdef DEBUG 217 // This is just so we can assert it's not set in nsIFrame::DestroyFrom. 218 RemoveStateBits(NS_FRAME_PART_OF_IBSPLIT); 219 #endif 220 } 221 222 if (MOZ_UNLIKELY(!mProperties.IsEmpty())) { 223 using T = mozilla::FrameProperties::UntypedDescriptor; 224 bool hasO = false, hasOC = false, hasEOC = false; 225 mProperties.ForEach([&](const T& aProp, uint64_t) { 226 if (aProp == OverflowProperty()) { 227 hasO = true; 228 } else if (aProp == OverflowContainersProperty()) { 229 hasOC = true; 230 } else if (aProp == ExcessOverflowContainersProperty()) { 231 hasEOC = true; 232 } 233 return true; 234 }); 235 236 // Destroy frames on the auxiliary frame lists and delete the lists. 237 mozilla::PresShell* presShell = PresShell(); 238 if (hasO) { 239 SafelyDestroyFrameListProp(aContext, presShell, OverflowProperty()); 240 } 241 242 MOZ_ASSERT(CanContainOverflowContainers() || !(hasOC || hasEOC), 243 "this type of frame shouldn't have overflow containers"); 244 if (hasOC) { 245 SafelyDestroyFrameListProp(aContext, presShell, 246 OverflowContainersProperty()); 247 } 248 if (hasEOC) { 249 SafelyDestroyFrameListProp(aContext, presShell, 250 ExcessOverflowContainersProperty()); 251 } 252 } 253 254 nsSplittableFrame::Destroy(aContext); 255 } 256 257 ///////////////////////////////////////////////////////////////////////////// 258 // Child frame enumeration 259 260 const nsFrameList& nsContainerFrame::GetChildList(ChildListID aListID) const { 261 // We only know about the principal child list, the overflow lists, 262 // and the backdrop list. 263 switch (aListID) { 264 case FrameChildListID::Principal: 265 return mFrames; 266 case FrameChildListID::Overflow: { 267 nsFrameList* list = GetOverflowFrames(); 268 return list ? *list : nsFrameList::EmptyList(); 269 } 270 case FrameChildListID::OverflowContainers: { 271 nsFrameList* list = GetOverflowContainers(); 272 return list ? *list : nsFrameList::EmptyList(); 273 } 274 case FrameChildListID::ExcessOverflowContainers: { 275 nsFrameList* list = GetExcessOverflowContainers(); 276 return list ? *list : nsFrameList::EmptyList(); 277 } 278 default: 279 return nsSplittableFrame::GetChildList(aListID); 280 } 281 } 282 283 void nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const { 284 mFrames.AppendIfNonempty(aLists, FrameChildListID::Principal); 285 286 using T = mozilla::FrameProperties::UntypedDescriptor; 287 mProperties.ForEach([this, aLists](const T& aProp, uint64_t aValue) { 288 typedef const nsFrameList* L; 289 if (aProp == OverflowProperty()) { 290 reinterpret_cast<L>(aValue)->AppendIfNonempty(aLists, 291 FrameChildListID::Overflow); 292 } else if (aProp == OverflowContainersProperty()) { 293 MOZ_ASSERT(CanContainOverflowContainers(), 294 "found unexpected OverflowContainersProperty"); 295 (void)this; // silence clang -Wunused-lambda-capture in opt builds 296 reinterpret_cast<L>(aValue)->AppendIfNonempty( 297 aLists, FrameChildListID::OverflowContainers); 298 } else if (aProp == ExcessOverflowContainersProperty()) { 299 MOZ_ASSERT(CanContainOverflowContainers(), 300 "found unexpected ExcessOverflowContainersProperty"); 301 (void)this; // silence clang -Wunused-lambda-capture in opt builds 302 reinterpret_cast<L>(aValue)->AppendIfNonempty( 303 aLists, FrameChildListID::ExcessOverflowContainers); 304 } 305 return true; 306 }); 307 308 nsSplittableFrame::GetChildLists(aLists); 309 } 310 311 ///////////////////////////////////////////////////////////////////////////// 312 // Painting/Events 313 314 void nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 315 const nsDisplayListSet& aLists) { 316 DisplayBorderBackgroundOutline(aBuilder, aLists); 317 BuildDisplayListForNonBlockChildren(aBuilder, aLists); 318 } 319 320 void nsContainerFrame::BuildDisplayListForNonBlockChildren( 321 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists, 322 DisplayChildFlags aFlags) { 323 nsIFrame* kid = mFrames.FirstChild(); 324 if (!kid || HidesContent()) { 325 return; 326 } 327 // Put each child's background directly onto the content list 328 nsDisplayListSet set(aLists, aLists.Content()); 329 // The children should be in content order 330 while (kid) { 331 BuildDisplayListForChild(aBuilder, kid, set, aFlags); 332 kid = kid->GetNextSibling(); 333 } 334 } 335 336 class nsDisplaySelectionOverlay final : public nsPaintedDisplayItem { 337 public: 338 /** 339 * @param aSelectionValue nsISelectionController::getDisplaySelection. 340 */ 341 nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 342 int16_t aSelectionValue) 343 : nsPaintedDisplayItem(aBuilder, aFrame), 344 mSelectionValue(aSelectionValue) { 345 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay); 346 } 347 348 MOZ_COUNTED_DTOR_FINAL(nsDisplaySelectionOverlay) 349 350 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; 351 bool CreateWebRenderCommands( 352 mozilla::wr::DisplayListBuilder& aBuilder, 353 mozilla::wr::IpcResourceUpdateQueue& aResources, 354 const StackingContextHelper& aSc, 355 mozilla::layers::RenderRootStateManager* aManager, 356 nsDisplayListBuilder* aDisplayListBuilder) override; 357 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY) 358 private: 359 DeviceColor ComputeColor() const; 360 361 static DeviceColor ComputeColorFromSelectionStyle(ComputedStyle&); 362 static DeviceColor ApplyTransparencyIfNecessary(nscolor); 363 364 // nsISelectionController::getDisplaySelection. 365 int16_t mSelectionValue; 366 }; 367 368 DeviceColor nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary( 369 nscolor aColor) { 370 // If it has already alpha, leave it like that. 371 if (NS_GET_A(aColor) != 255) { 372 return ToDeviceColor(aColor); 373 } 374 375 // NOTE(emilio): Blink and WebKit do something slightly different here, and 376 // blend the color with white instead, both for overlays and text backgrounds. 377 auto color = sRGBColor::FromABGR(aColor); 378 color.a = 0.5; 379 return ToDeviceColor(color); 380 } 381 382 DeviceColor nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle( 383 ComputedStyle& aStyle) { 384 return ApplyTransparencyIfNecessary( 385 aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor)); 386 } 387 388 DeviceColor nsDisplaySelectionOverlay::ComputeColor() const { 389 LookAndFeel::ColorID colorID; 390 if (RefPtr<ComputedStyle> style = 391 mFrame->ComputeSelectionStyle(mSelectionValue)) { 392 return ComputeColorFromSelectionStyle(*style); 393 } 394 if (mSelectionValue == nsISelectionController::SELECTION_ON) { 395 colorID = LookAndFeel::ColorID::Highlight; 396 } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) { 397 colorID = LookAndFeel::ColorID::TextSelectAttentionBackground; 398 } else { 399 colorID = LookAndFeel::ColorID::TextSelectDisabledBackground; 400 } 401 402 return ApplyTransparencyIfNecessary( 403 LookAndFeel::Color(colorID, mFrame, NS_RGB(255, 255, 255))); 404 } 405 406 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder, 407 gfxContext* aCtx) { 408 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget(); 409 ColorPattern color(ComputeColor()); 410 411 nsIntRect pxRect = 412 GetPaintRect(aBuilder, aCtx) 413 .ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel()); 414 Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height); 415 MaybeSnapToDevicePixels(rect, aDrawTarget, true); 416 417 aDrawTarget.FillRect(rect, color); 418 } 419 420 bool nsDisplaySelectionOverlay::CreateWebRenderCommands( 421 mozilla::wr::DisplayListBuilder& aBuilder, 422 mozilla::wr::IpcResourceUpdateQueue& aResources, 423 const StackingContextHelper& aSc, 424 mozilla::layers::RenderRootStateManager* aManager, 425 nsDisplayListBuilder* aDisplayListBuilder) { 426 wr::LayoutRect bounds = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits( 427 nsRect(ToReferenceFrame(), Frame()->GetSize()), 428 mFrame->PresContext()->AppUnitsPerDevPixel())); 429 aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(), false, false, 430 wr::ToColorF(ComputeColor())); 431 return true; 432 } 433 434 void nsContainerFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, 435 nsDisplayList* aList, 436 uint16_t aContentType) { 437 if (!IsSelected() || !IsVisibleForPainting()) { 438 return; 439 } 440 441 int16_t displaySelection = PresShell()->GetSelectionFlags(); 442 if (!(displaySelection & aContentType)) { 443 return; 444 } 445 446 const nsFrameSelection* frameSelection = GetConstFrameSelection(); 447 int16_t selectionValue = frameSelection->GetDisplaySelection(); 448 449 if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) { 450 return; // selection is hidden or off 451 } 452 453 nsIContent* newContent = mContent->GetParent(); 454 455 // check to see if we are anonymous content 456 // XXXbz there has GOT to be a better way of determining this! 457 int32_t offset = 458 newContent ? newContent->ComputeIndexOf_Deprecated(mContent) : 0; 459 460 // look up to see what selection(s) are on this frame 461 UniquePtr<SelectionDetails> details = frameSelection->LookUpSelection( 462 newContent, offset, 1, 463 ShouldPaintNormalSelection() 464 ? nsFrameSelection::IgnoreNormalSelection::No 465 : nsFrameSelection::IgnoreNormalSelection::Yes); 466 if (!details) { 467 return; 468 } 469 470 bool normal = false; 471 for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) { 472 if (sd->mSelectionType == SelectionType::eNormal) { 473 normal = true; 474 } 475 } 476 477 if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) { 478 // Don't overlay an image if it's not in the primary selection. 479 return; 480 } 481 482 aList->AppendNewToTop<nsDisplaySelectionOverlay>(aBuilder, this, 483 selectionValue); 484 } 485 486 /* virtual */ 487 void nsContainerFrame::ChildIsDirty(nsIFrame* aChild) { 488 NS_ASSERTION(aChild->IsSubtreeDirty(), "child isn't actually dirty"); 489 490 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 491 } 492 493 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetNoAmount( 494 bool aForward, int32_t* aOffset) { 495 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range"); 496 // Don't allow the caret to stay in an empty (leaf) container frame. 497 return CONTINUE_EMPTY; 498 } 499 500 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetCharacter( 501 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) { 502 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range"); 503 // Don't allow the caret to stay in an empty (leaf) container frame. 504 return CONTINUE_EMPTY; 505 } 506 507 ///////////////////////////////////////////////////////////////////////////// 508 // Helper member functions 509 510 void nsContainerFrame::ReparentFrame(nsIFrame* aFrame, 511 nsContainerFrame* aOldParent, 512 nsContainerFrame* aNewParent) { 513 NS_ASSERTION(aOldParent == aFrame->GetParent(), 514 "Parent not consistent with expectations"); 515 516 aFrame->SetParent(aNewParent); 517 } 518 519 void nsContainerFrame::ReparentFrames(nsFrameList& aFrameList, 520 nsContainerFrame* aOldParent, 521 nsContainerFrame* aNewParent) { 522 for (auto* f : aFrameList) { 523 ReparentFrame(f, aOldParent, aNewParent); 524 } 525 } 526 527 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, 528 nsIWidget* aWidget, 529 const nsSize& aMinSize, 530 const nsSize& aMaxSize) { 531 LayoutDeviceIntSize devMinSize( 532 aPresContext->AppUnitsToDevPixels(aMinSize.width), 533 aPresContext->AppUnitsToDevPixels(aMinSize.height)); 534 LayoutDeviceIntSize devMaxSize( 535 aMaxSize.width == NS_UNCONSTRAINEDSIZE 536 ? NS_MAXSIZE 537 : aPresContext->AppUnitsToDevPixels(aMaxSize.width), 538 aMaxSize.height == NS_UNCONSTRAINEDSIZE 539 ? NS_MAXSIZE 540 : aPresContext->AppUnitsToDevPixels(aMaxSize.height)); 541 542 // MinSize has a priority over MaxSize 543 if (devMinSize.width > devMaxSize.width) { 544 devMaxSize.width = devMinSize.width; 545 } 546 if (devMinSize.height > devMaxSize.height) { 547 devMaxSize.height = devMinSize.height; 548 } 549 550 DesktopToLayoutDeviceScale constraintsScale(MOZ_WIDGET_INVALID_SCALE); 551 if (nsIWidget* rootWidget = aPresContext->GetNearestWidget()) { 552 constraintsScale = rootWidget->GetDesktopToDeviceScale(); 553 } 554 555 widget::SizeConstraints constraints(devMinSize, devMaxSize, constraintsScale); 556 557 // The sizes are in inner window sizes, so convert them into outer window 558 // sizes. Use a size of (200, 200) as only the difference between the inner 559 // and outer size is needed. 560 const LayoutDeviceIntSize sizeDiff = 561 aWidget->NormalSizeModeClientToWindowSizeDifference(); 562 if (constraints.mMinSize.width) { 563 constraints.mMinSize.width += sizeDiff.width; 564 } 565 if (constraints.mMinSize.height) { 566 constraints.mMinSize.height += sizeDiff.height; 567 } 568 if (constraints.mMaxSize.width != NS_MAXSIZE) { 569 constraints.mMaxSize.width += sizeDiff.width; 570 } 571 if (constraints.mMaxSize.height != NS_MAXSIZE) { 572 constraints.mMaxSize.height += sizeDiff.height; 573 } 574 575 aWidget->SetSizeConstraints(constraints); 576 } 577 578 void nsContainerFrame::DoInlineMinISize(const IntrinsicSizeInput& aInput, 579 InlineMinISizeData* aData) { 580 auto handleChildren = [&](auto frame, auto data) { 581 for (nsIFrame* kid : frame->mFrames) { 582 const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(), 583 GetWritingMode()); 584 kid->AddInlineMinISize(kidInput, data); 585 } 586 }; 587 DoInlineIntrinsicISize(aData, handleChildren); 588 } 589 590 void nsContainerFrame::DoInlinePrefISize(const IntrinsicSizeInput& aInput, 591 InlinePrefISizeData* aData) { 592 auto handleChildren = [&](auto frame, auto data) { 593 for (nsIFrame* kid : frame->mFrames) { 594 const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(), 595 GetWritingMode()); 596 kid->AddInlinePrefISize(kidInput, data); 597 } 598 }; 599 DoInlineIntrinsicISize(aData, handleChildren); 600 aData->mLineIsEmpty = false; 601 } 602 603 /* virtual */ 604 LogicalSize nsContainerFrame::ComputeAutoSize( 605 const SizeComputationInput& aSizingInput, WritingMode aWM, 606 const LogicalSize& aCBSize, nscoord aAvailableISize, 607 const LogicalSize& aMargin, const mozilla::LogicalSize& aBorderPadding, 608 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) { 609 const bool isTableCaption = IsTableCaption(); 610 // Skip table caption, which requires special sizing - see bug 1109571. 611 if (IsAbsolutelyPositionedWithDefiniteContainingBlock() && !isTableCaption) { 612 return ComputeAbsolutePosAutoSize(aSizingInput, aWM, aCBSize, 613 aAvailableISize, aMargin, aBorderPadding, 614 aSizeOverrides, aFlags); 615 } 616 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE); 617 if (aFlags.contains(ComputeSizeFlag::ShrinkWrap)) { 618 // Delegate to nsIFrame::ComputeAutoSize() for computing the shrink-wrapping 619 // size. 620 result = nsIFrame::ComputeAutoSize(aSizingInput, aWM, aCBSize, 621 aAvailableISize, aMargin, aBorderPadding, 622 aSizeOverrides, aFlags); 623 } else { 624 result.ISize(aWM) = 625 aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM); 626 } 627 628 if (isTableCaption) { 629 // If we're a container for font size inflation, then shrink 630 // wrapping inside of us should not apply font size inflation. 631 AutoMaybeDisableFontInflation an(this); 632 633 WritingMode tableWM = GetParent()->GetWritingMode(); 634 const IntrinsicSizeInput input( 635 aSizingInput.mRenderingContext, 636 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)), Nothing()); 637 if (aWM.IsOrthogonalTo(tableWM)) { 638 // For an orthogonal caption on a block-dir side of the table, shrink-wrap 639 // to min-isize. 640 result.ISize(aWM) = GetMinISize(input); 641 } else { 642 // The outer frame constrains our available isize to the isize of 643 // the table. Grow if our min-isize is bigger than that, but not 644 // larger than the containing block isize. (It would really be nice 645 // to transmit that information another way, so we could grow up to 646 // the table's available isize, but that's harder.) 647 nscoord min = GetMinISize(input); 648 if (min > aCBSize.ISize(aWM)) { 649 min = aCBSize.ISize(aWM); 650 } 651 if (min > result.ISize(aWM)) { 652 result.ISize(aWM) = min; 653 } 654 } 655 } 656 return result; 657 } 658 659 void nsContainerFrame::ReflowChild( 660 nsIFrame* aKidFrame, nsPresContext* aPresContext, 661 ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, 662 const WritingMode& aWM, const LogicalPoint& aPos, 663 const nsSize& aContainerSize, ReflowChildFlags aFlags, 664 nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker) { 665 MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input"); 666 if (aWM.IsPhysicalRTL()) { 667 NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, 668 "ReflowChild with unconstrained container width!"); 669 } 670 MOZ_ASSERT(aDesiredSize.InkOverflow() == nsRect(0, 0, 0, 0) && 671 aDesiredSize.ScrollableOverflow() == nsRect(0, 0, 0, 0), 672 "please reset the overflow areas before calling ReflowChild"); 673 674 // Position the child frame and its view if requested. 675 if (ReflowChildFlags::NoMoveFrame != 676 (aFlags & ReflowChildFlags::NoMoveFrame)) { 677 aKidFrame->SetPosition(aWM, aPos, aContainerSize); 678 } 679 680 // Reflow the child frame 681 aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); 682 683 // If the child frame is complete, delete any next-in-flows, 684 // but only if the NoDeleteNextInFlowChild flag isn't set. 685 if (!aStatus.IsInlineBreakBefore() && aStatus.IsFullyComplete() && 686 !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) { 687 if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) { 688 // Remove all of the childs next-in-flows. Make sure that we ask 689 // the right parent to do the removal (it's possible that the 690 // parent is not this because we are executing pullup code) 691 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); 692 DestroyContext context(PresShell()); 693 kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow, 694 true); 695 } 696 } 697 } 698 699 // XXX temporary: hold on to a copy of the old physical version of 700 // ReflowChild so that we can convert callers incrementally. 701 void nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, 702 nsPresContext* aPresContext, 703 ReflowOutput& aDesiredSize, 704 const ReflowInput& aReflowInput, nscoord aX, 705 nscoord aY, ReflowChildFlags aFlags, 706 nsReflowStatus& aStatus, 707 nsOverflowContinuationTracker* aTracker) { 708 MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input"); 709 710 // Position the child frame and its view if requested. 711 if (ReflowChildFlags::NoMoveFrame != 712 (aFlags & ReflowChildFlags::NoMoveFrame)) { 713 aKidFrame->SetPosition(nsPoint(aX, aY)); 714 } 715 716 // Reflow the child frame 717 aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); 718 719 // If the child frame is complete, delete any next-in-flows, 720 // but only if the NoDeleteNextInFlowChild flag isn't set. 721 if (aStatus.IsFullyComplete() && 722 !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) { 723 if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) { 724 // Remove all of the childs next-in-flows. Make sure that we ask 725 // the right parent to do the removal (it's possible that the 726 // parent is not this because we are executing pullup code) 727 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); 728 DestroyContext context(PresShell()); 729 kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow, 730 true); 731 } 732 } 733 } 734 735 void nsContainerFrame::FinishReflowChild( 736 nsIFrame* aKidFrame, nsPresContext* aPresContext, 737 const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput, 738 const WritingMode& aWM, const LogicalPoint& aPos, 739 const nsSize& aContainerSize, nsIFrame::ReflowChildFlags aFlags) { 740 MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == aKidFrame); 741 MOZ_ASSERT(aReflowInput || aKidFrame->IsMathMLFrame() || 742 aKidFrame->IsTableCellFrame(), 743 "aReflowInput should be passed in almost all cases"); 744 745 if (aWM.IsPhysicalRTL()) { 746 NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, 747 "FinishReflowChild with unconstrained container width!"); 748 } 749 750 const LogicalSize convertedSize = aDesiredSize.Size(aWM); 751 LogicalPoint pos(aPos); 752 753 if (aFlags & ReflowChildFlags::ApplyRelativePositioning) { 754 MOZ_ASSERT(aReflowInput, "caller must have passed reflow input"); 755 // ApplyRelativePositioning in right-to-left writing modes needs to know 756 // the updated frame width to set the normal position correctly. 757 aKidFrame->SetSize(aWM, convertedSize); 758 759 const LogicalMargin offsets = aReflowInput->ComputedLogicalOffsets(aWM); 760 ReflowInput::ApplyRelativePositioning(aKidFrame, aWM, offsets, &pos, 761 aContainerSize); 762 } 763 764 if (ReflowChildFlags::NoMoveFrame != 765 (aFlags & ReflowChildFlags::NoMoveFrame)) { 766 aKidFrame->SetRect(aWM, LogicalRect(aWM, pos, convertedSize), 767 aContainerSize); 768 } else { 769 aKidFrame->SetSize(aWM, convertedSize); 770 } 771 772 aKidFrame->DidReflow(aPresContext, aReflowInput); 773 } 774 #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64) 775 # pragma optimize("", on) 776 #endif 777 778 // XXX temporary: hold on to a copy of the old physical version of 779 // FinishReflowChild so that we can convert callers incrementally. 780 void nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, 781 nsPresContext* aPresContext, 782 const ReflowOutput& aDesiredSize, 783 const ReflowInput* aReflowInput, 784 nscoord aX, nscoord aY, 785 ReflowChildFlags aFlags) { 786 MOZ_ASSERT(!(aFlags & ReflowChildFlags::ApplyRelativePositioning), 787 "only the logical version supports ApplyRelativePositioning " 788 "since ApplyRelativePositioning requires the container size"); 789 790 nsPoint pos(aX, aY); 791 nsSize size(aDesiredSize.PhysicalSize()); 792 793 if (ReflowChildFlags::NoMoveFrame != 794 (aFlags & ReflowChildFlags::NoMoveFrame)) { 795 aKidFrame->SetRect(nsRect(pos, size)); 796 } else { 797 aKidFrame->SetSize(size); 798 } 799 800 aKidFrame->DidReflow(aPresContext, aReflowInput); 801 } 802 803 void nsContainerFrame::FinishReflowWithAbsoluteFrames( 804 nsPresContext* aPresContext, ReflowOutput& aDesiredSize, 805 const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { 806 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus); 807 FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay); 808 } 809 810 void nsContainerFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, 811 ReflowOutput& aDesiredSize, 812 const ReflowInput& aReflowInput, 813 nsReflowStatus& aStatus) { 814 auto* absoluteContainer = GetAbsoluteContainingBlock(); 815 if (absoluteContainer && absoluteContainer->PrepareAbsoluteFrames(this)) { 816 // The containing block for the abs pos kids is formed by our padding edge. 817 const auto wm = GetWritingMode(); 818 LogicalRect cbRect(wm, LogicalPoint(wm), aDesiredSize.Size(wm)); 819 cbRect.Deflate(wm, GetLogicalUsedBorder(wm).ApplySkipSides( 820 PreReflowBlockLevelLogicalSkipSides())); 821 // XXX: To optimize the performance, set the flags only when the CB width or 822 // height actually changes. 823 AbsPosReflowFlags flags{AbsPosReflowFlag::AllowFragmentation, 824 AbsPosReflowFlag::CBWidthChanged, 825 AbsPosReflowFlag::CBHeightChanged}; 826 absoluteContainer->Reflow( 827 this, aPresContext, aReflowInput, aStatus, 828 cbRect.GetPhysicalRect(wm, aDesiredSize.PhysicalSize()), flags, 829 &aDesiredSize.mOverflowAreas); 830 } 831 } 832 833 void nsContainerFrame::ReflowOverflowContainerChildren( 834 nsPresContext* aPresContext, const ReflowInput& aReflowInput, 835 OverflowAreas& aOverflowRects, ReflowChildFlags aFlags, 836 nsReflowStatus& aStatus, ChildFrameMerger aMergeFunc, 837 Maybe<nsSize> aContainerSize) { 838 MOZ_ASSERT(aPresContext, "null pointer"); 839 840 nsFrameList* overflowContainers = 841 DrainExcessOverflowContainersList(aMergeFunc); 842 if (!overflowContainers) { 843 return; // nothing to reflow 844 } 845 846 nsOverflowContinuationTracker tracker(this, false, false); 847 bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids(); 848 849 for (nsIFrame* frame : *overflowContainers) { 850 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) { 851 // frame's prevInFlow has moved, skip reflowing this frame; 852 // it will get reflowed once it's been placed 853 if (GetNextInFlow()) { 854 // We report OverflowIncomplete status in this case to avoid our parent 855 // deleting our next-in-flows which might destroy non-empty frames. 856 nsReflowStatus status; 857 status.SetOverflowIncomplete(); 858 aStatus.MergeCompletionStatusFrom(status); 859 } 860 continue; 861 } 862 863 auto ScrollableOverflowExceedsAvailableBSize = 864 [this, &aReflowInput](nsIFrame* aFrame) { 865 if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { 866 return false; 867 } 868 const auto parentWM = GetWritingMode(); 869 const nscoord scrollableOverflowRectBEnd = 870 LogicalRect(parentWM, 871 aFrame->ScrollableOverflowRectRelativeToParent(), 872 GetSize()) 873 .BEnd(parentWM); 874 return scrollableOverflowRectBEnd > aReflowInput.AvailableBSize(); 875 }; 876 877 // If the available block-size has changed, or the existing scrollable 878 // overflow's block-end exceeds it, we need to reflow even if the frame 879 // isn't dirty. 880 if (shouldReflowAllKids || frame->IsSubtreeDirty() || 881 ScrollableOverflowExceedsAvailableBSize(frame)) { 882 nsIFrame* prevInFlow = frame->GetPrevInFlow(); 883 NS_ASSERTION(prevInFlow, 884 "overflow container frame must have a prev-in-flow"); 885 NS_ASSERTION( 886 frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER), 887 "overflow container frame must have overflow container bit set"); 888 WritingMode wm = frame->GetWritingMode(); 889 // Note: aReflowInput's available inline-size is technically wrong for us 890 // to hand off to children here, because it doesn't account for the space 891 // that's been used for the container's margin/border/padding (and some 892 // other space that a concrete container type, e.g. fieldset and grid [1], 893 // might reserve before setting up the available space for their 894 // children). Since we don't have a way to query the specific available 895 // inline-size each container type used, nor do we know how the container 896 // computes its non-overflow-container children's inline-size, we just 897 // unconditionally override the frame's inline-size, so that the available 898 // inline-size for the children doesn't really matter anyway. 899 // 900 // [1] For example, fieldset uses its computed inline-size with padding as 901 // the available inline-size to reflow its inner child frame. 902 // https://searchfox.org/mozilla-central/rev/04f7743d94691fa24212fb43099f9d84c3bfc890/layout/forms/nsFieldSetFrame.cpp#535-536 903 const LogicalSize availSpace = aReflowInput.AvailableSize(wm); 904 905 StyleSizeOverrides sizeOverride; 906 // We override current continuation's inline-size by using the 907 // prev-in-flow's inline-size since both should be the same. 908 sizeOverride.mStyleISize.emplace( 909 StyleSize::LengthPercentage(LengthPercentage::FromAppUnits( 910 frame->StylePosition()->mBoxSizing == StyleBoxSizing::Border 911 ? prevInFlow->ISize(wm) 912 : prevInFlow->ContentISize(wm)))); 913 914 if (frame->IsFlexItem()) { 915 // An overflow container's block-size must be 0. 916 sizeOverride.mStyleBSize.emplace( 917 StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(0))); 918 } 919 ReflowOutput desiredSize(wm); 920 ReflowInput reflowInput(aPresContext, aReflowInput, frame, availSpace, 921 Nothing(), {}, sizeOverride); 922 const nsSize containerSize = 923 aContainerSize ? *aContainerSize 924 : aReflowInput.AvailableSize(wm).GetPhysicalSize(wm); 925 const LogicalPoint pos(wm, prevInFlow->IStart(wm, containerSize), 0); 926 nsReflowStatus frameStatus; 927 928 ReflowChild(frame, aPresContext, desiredSize, reflowInput, wm, pos, 929 containerSize, aFlags, frameStatus, &tracker); 930 FinishReflowChild(frame, aPresContext, desiredSize, &reflowInput, wm, pos, 931 containerSize, aFlags); 932 933 // Handle continuations 934 if (!frameStatus.IsFullyComplete()) { 935 if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 936 // Abspos frames can't cause their parent to be incomplete, 937 // only overflow incomplete. 938 frameStatus.SetOverflowIncomplete(); 939 } else { 940 NS_ASSERTION(frameStatus.IsComplete(), 941 "overflow container frames can't be incomplete, only " 942 "overflow-incomplete"); 943 } 944 945 // Acquire a next-in-flow, creating it if necessary 946 nsIFrame* nif = frame->GetNextInFlow(); 947 if (!nif) { 948 NS_ASSERTION(frameStatus.NextInFlowNeedsReflow(), 949 "Someone forgot a NextInFlowNeedsReflow flag"); 950 nif = PresShell()->FrameConstructor()->CreateContinuingFrame(frame, 951 this); 952 } else if (!nif->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) { 953 // used to be a normal next-in-flow; steal it from the child list 954 nif->GetParent()->StealFrame(nif); 955 } 956 957 tracker.Insert(nif, frameStatus); 958 } 959 aStatus.MergeCompletionStatusFrom(frameStatus); 960 // At this point it would be nice to assert 961 // !frame->GetOverflowRect().IsEmpty(), but we have some unsplittable 962 // frames that, when taller than availableHeight will push zero-height 963 // content into a next-in-flow. 964 } else { 965 tracker.Skip(frame, aStatus); 966 if (aReflowInput.mFloatManager) { 967 nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager, 968 aReflowInput.GetWritingMode(), 969 aReflowInput.ComputedPhysicalSize()); 970 } 971 } 972 ConsiderChildOverflow(aOverflowRects, frame, /* aAsIfScrolled = */ false); 973 } 974 } 975 976 void nsContainerFrame::DisplayOverflowContainers( 977 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { 978 if (nsFrameList* overflowconts = GetOverflowContainers()) { 979 for (nsIFrame* frame : *overflowconts) { 980 BuildDisplayListForChild(aBuilder, frame, aLists); 981 } 982 } 983 } 984 985 void nsContainerFrame::DisplayPushedAbsoluteFrames( 986 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { 987 for (nsIFrame* frame : GetChildList(FrameChildListID::Absolute)) { 988 if (frame->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) { 989 BuildDisplayListForChild(aBuilder, frame, aLists); 990 } 991 } 992 } 993 994 bool nsContainerFrame::TryRemoveFrame(FrameListPropertyDescriptor aProp, 995 nsIFrame* aChildToRemove) { 996 nsFrameList* list = GetProperty(aProp); 997 if (list && list->StartRemoveFrame(aChildToRemove)) { 998 // aChildToRemove *may* have been removed from this list. 999 if (list->IsEmpty()) { 1000 (void)TakeProperty(aProp); 1001 list->Delete(PresShell()); 1002 } 1003 return true; 1004 } 1005 return false; 1006 } 1007 1008 bool nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild) { 1009 bool removed = false; 1010 if (MOZ_UNLIKELY(aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER))) { 1011 // Try removing from the overflow container list. 1012 removed = TryRemoveFrame(OverflowContainersProperty(), aChild); 1013 if (!removed) { 1014 // It might be in the excess overflow container list. 1015 removed = TryRemoveFrame(ExcessOverflowContainersProperty(), aChild); 1016 } 1017 } 1018 return removed; 1019 } 1020 1021 void nsContainerFrame::StealFrame(nsIFrame* aChild) { 1022 #ifdef DEBUG 1023 if (!mFrames.ContainsFrame(aChild)) { 1024 nsFrameList* list = GetOverflowFrames(); 1025 if (!list || !list->ContainsFrame(aChild)) { 1026 list = GetOverflowContainers(); 1027 if (!list || !list->ContainsFrame(aChild)) { 1028 list = GetExcessOverflowContainers(); 1029 MOZ_ASSERT(list && list->ContainsFrame(aChild), 1030 "aChild isn't our child" 1031 " or on a frame list not supported by StealFrame"); 1032 } 1033 } 1034 } 1035 #endif 1036 1037 if (MaybeStealOverflowContainerFrame(aChild)) { 1038 return; 1039 } 1040 1041 // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers 1042 // on the normal lists so we might get here also if the frame bit 1043 // NS_FRAME_IS_OVERFLOW_CONTAINER is set. 1044 if (mFrames.StartRemoveFrame(aChild)) { 1045 return; 1046 } 1047 1048 // We didn't find the child in our principal child list. 1049 // Maybe it's on the overflow list? 1050 nsFrameList* frameList = GetOverflowFrames(); 1051 if (frameList && frameList->ContinueRemoveFrame(aChild)) { 1052 if (frameList->IsEmpty()) { 1053 DestroyOverflowList(); 1054 } 1055 return; 1056 } 1057 1058 MOZ_ASSERT_UNREACHABLE("StealFrame: can't find aChild"); 1059 } 1060 1061 nsFrameList nsContainerFrame::StealFramesAfter(nsIFrame* aChild) { 1062 NS_ASSERTION( 1063 !aChild || !aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER), 1064 "StealFramesAfter doesn't handle overflow containers"); 1065 NS_ASSERTION(!IsBlockFrame(), "unexpected call"); 1066 1067 if (!aChild) { 1068 return std::move(mFrames); 1069 } 1070 1071 for (nsIFrame* f : mFrames) { 1072 if (f == aChild) { 1073 return mFrames.TakeFramesAfter(f); 1074 } 1075 } 1076 1077 // We didn't find the child in the principal child list. 1078 // Maybe it's on the overflow list? 1079 if (nsFrameList* overflowFrames = GetOverflowFrames()) { 1080 for (nsIFrame* f : *overflowFrames) { 1081 if (f == aChild) { 1082 return mFrames.TakeFramesAfter(f); 1083 } 1084 } 1085 } 1086 1087 NS_ERROR("StealFramesAfter: can't find aChild"); 1088 return nsFrameList(); 1089 } 1090 1091 /* 1092 * Create a next-in-flow for aFrame. Will return the newly created 1093 * frame <b>if and only if</b> a new frame is created; otherwise 1094 * nullptr is returned. 1095 */ 1096 nsIFrame* nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame) { 1097 MOZ_ASSERT( 1098 !IsBlockFrame(), 1099 "you should have called nsBlockFrame::CreateContinuationFor instead"); 1100 MOZ_ASSERT(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame"); 1101 1102 nsIFrame* nextInFlow = aFrame->GetNextInFlow(); 1103 if (nullptr == nextInFlow) { 1104 // Create a continuation frame for the child frame and insert it 1105 // into our child list. 1106 nextInFlow = 1107 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this); 1108 mFrames.InsertFrame(nullptr, aFrame, nextInFlow); 1109 1110 NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES, 1111 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p", 1112 aFrame, nextInFlow)); 1113 1114 return nextInFlow; 1115 } 1116 return nullptr; 1117 } 1118 1119 /** 1120 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and 1121 * flow pointers 1122 */ 1123 void nsContainerFrame::DeleteNextInFlowChild(DestroyContext& aContext, 1124 nsIFrame* aNextInFlow, 1125 bool aDeletingEmptyFrames) { 1126 #ifdef DEBUG 1127 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); 1128 #endif 1129 MOZ_ASSERT(prevInFlow, "bad prev-in-flow"); 1130 1131 // If the next-in-flow has a next-in-flow then delete it, too (and 1132 // delete it first). 1133 // Do this in a loop so we don't overflow the stack for frames 1134 // with very many next-in-flows 1135 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow(); 1136 if (nextNextInFlow) { 1137 AutoTArray<nsIFrame*, 8> frames; 1138 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) { 1139 frames.AppendElement(f); 1140 } 1141 for (nsIFrame* delFrame : Reversed(frames)) { 1142 nsContainerFrame* parent = delFrame->GetParent(); 1143 parent->DeleteNextInFlowChild(aContext, delFrame, aDeletingEmptyFrames); 1144 } 1145 } 1146 1147 // Take the next-in-flow out of the parent's child list 1148 StealFrame(aNextInFlow); 1149 1150 #ifdef DEBUG 1151 if (aDeletingEmptyFrames) { 1152 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); 1153 } 1154 #endif 1155 1156 // Delete the next-in-flow frame and its descendants. This will also 1157 // remove it from its next-in-flow/prev-in-flow chain. 1158 aNextInFlow->Destroy(aContext); 1159 1160 MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); 1161 } 1162 1163 void nsContainerFrame::PushChildrenToOverflow(nsIFrame* aFromChild, 1164 nsIFrame* aPrevSibling) { 1165 MOZ_ASSERT(aFromChild, "null pointer"); 1166 MOZ_ASSERT(aPrevSibling, "pushing first child"); 1167 MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling"); 1168 1169 // Add the frames to our overflow list (let our next in flow drain 1170 // our overflow list when it is ready) 1171 SetOverflowFrames(mFrames.TakeFramesAfter(aPrevSibling)); 1172 } 1173 1174 bool nsContainerFrame::PushIncompleteChildren( 1175 const FrameHashtable& aPushedItems, const FrameHashtable& aIncompleteItems, 1176 const FrameHashtable& aOverflowIncompleteItems) { 1177 MOZ_ASSERT(IsFlexOrGridContainer(), 1178 "Only Grid / Flex containers can call this!"); 1179 1180 if (aPushedItems.IsEmpty() && aIncompleteItems.IsEmpty() && 1181 aOverflowIncompleteItems.IsEmpty()) { 1182 return false; 1183 } 1184 1185 // Iterate the children in normal document order and append them (or a NIF) 1186 // to one of the following frame lists according to their status. 1187 nsFrameList pushedList; 1188 nsFrameList incompleteList; 1189 nsFrameList overflowIncompleteList; 1190 auto* fc = PresShell()->FrameConstructor(); 1191 for (nsIFrame* child = PrincipalChildList().FirstChild(); child;) { 1192 MOZ_ASSERT((aPushedItems.Contains(child) ? 1 : 0) + 1193 (aIncompleteItems.Contains(child) ? 1 : 0) + 1194 (aOverflowIncompleteItems.Contains(child) ? 1 : 0) <= 1195 1, 1196 "child should only be in one of these sets"); 1197 // Save the next-sibling so we can continue the loop if |child| is moved. 1198 nsIFrame* next = child->GetNextSibling(); 1199 if (aPushedItems.Contains(child)) { 1200 MOZ_ASSERT(child->GetParent() == this); 1201 StealFrame(child); 1202 pushedList.AppendFrame(nullptr, child); 1203 } else if (aIncompleteItems.Contains(child)) { 1204 nsIFrame* childNIF = child->GetNextInFlow(); 1205 if (!childNIF) { 1206 childNIF = fc->CreateContinuingFrame(child, this); 1207 incompleteList.AppendFrame(nullptr, childNIF); 1208 } else { 1209 auto* parent = childNIF->GetParent(); 1210 MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF), 1211 "child's NIF shouldn't be in the same principal list"); 1212 // If child's existing NIF is an overflow container, convert it to an 1213 // actual NIF, since now |child| has non-overflow stuff to give it. 1214 // Or, if it's further away then our next-in-flow, then pull it up. 1215 if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) || 1216 (parent != this && parent != GetNextInFlow())) { 1217 parent->StealFrame(childNIF); 1218 childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1219 if (parent == this) { 1220 incompleteList.AppendFrame(nullptr, childNIF); 1221 } else { 1222 // If childNIF already lives on the next fragment, then we 1223 // don't need to reparent it, since we know it's destined to end 1224 // up there anyway. Just move it to its parent's overflow list. 1225 if (parent == GetNextInFlow()) { 1226 nsFrameList toMove(childNIF, childNIF); 1227 parent->MergeSortedOverflow(toMove); 1228 } else { 1229 ReparentFrame(childNIF, parent, this); 1230 incompleteList.AppendFrame(nullptr, childNIF); 1231 } 1232 } 1233 } 1234 } 1235 } else if (aOverflowIncompleteItems.Contains(child)) { 1236 nsIFrame* childNIF = child->GetNextInFlow(); 1237 if (!childNIF) { 1238 childNIF = fc->CreateContinuingFrame(child, this); 1239 childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1240 overflowIncompleteList.AppendFrame(nullptr, childNIF); 1241 } else { 1242 DebugOnly<nsContainerFrame*> lastParent = this; 1243 auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow()); 1244 // If child has any non-overflow-container NIFs, convert them to 1245 // overflow containers, since that's all |child| needs now. 1246 while (childNIF && 1247 !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1248 auto* parent = childNIF->GetParent(); 1249 parent->StealFrame(childNIF); 1250 childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1251 if (parent == this) { 1252 overflowIncompleteList.AppendFrame(nullptr, childNIF); 1253 } else { 1254 if (!nif || parent == nif) { 1255 nsFrameList toMove(childNIF, childNIF); 1256 parent->MergeSortedExcessOverflowContainers(toMove); 1257 } else { 1258 ReparentFrame(childNIF, parent, nif); 1259 nsFrameList toMove(childNIF, childNIF); 1260 nif->MergeSortedExcessOverflowContainers(toMove); 1261 } 1262 // We only need to reparent the first childNIF (or not at all if 1263 // its parent is our NIF). 1264 nif = nullptr; 1265 } 1266 lastParent = parent; 1267 childNIF = childNIF->GetNextInFlow(); 1268 } 1269 } 1270 } 1271 child = next; 1272 } 1273 1274 // Merge the results into our respective overflow child lists. 1275 if (!pushedList.IsEmpty()) { 1276 MergeSortedOverflow(pushedList); 1277 } 1278 if (!incompleteList.IsEmpty()) { 1279 MergeSortedOverflow(incompleteList); 1280 } 1281 if (!overflowIncompleteList.IsEmpty()) { 1282 // If our next-in-flow already has overflow containers list, merge the 1283 // overflowIncompleteList into that list. Otherwise, merge it into our 1284 // excess overflow containers list, to be drained by our next-in-flow. 1285 auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow()); 1286 nsFrameList* oc = nif ? nif->GetOverflowContainers() : nullptr; 1287 if (oc) { 1288 ReparentFrames(overflowIncompleteList, this, nif); 1289 MergeSortedFrameLists(*oc, overflowIncompleteList, GetContent()); 1290 } else { 1291 MergeSortedExcessOverflowContainers(overflowIncompleteList); 1292 } 1293 } 1294 return true; 1295 } 1296 1297 void nsContainerFrame::NormalizeChildLists() { 1298 MOZ_ASSERT(IsFlexOrGridContainer(), 1299 "Only Flex / Grid containers can call this!"); 1300 1301 // Note: the following description uses grid container as an example. Flex 1302 // container is similar. 1303 // 1304 // First we gather child frames we should include in our reflow/placement, 1305 // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow 1306 // children (that might now fit). It's important to note that these children 1307 // can be in arbitrary order vis-a-vis the current children in our lists. 1308 // E.g. grid items in the document order: A, B, C may be placed in the rows 1309 // 3, 2, 1. Assume each row goes in a separate grid container fragment, 1310 // and we reflow the second fragment. Now if C (in fragment 1) overflows, 1311 // we can't just prepend it to our mFrames like we usually do because that 1312 // would violate the document order invariant that other code depends on. 1313 // Similarly if we pull up child A (from fragment 3) we can't just append 1314 // that for the same reason. Instead, we must sort these children into 1315 // our child lists. (The sorting is trivial given that both lists are 1316 // already fully sorted individually - it's just a merge.) 1317 // 1318 // The invariants that we maintain are that each grid container child list 1319 // is sorted in the normal document order at all times, but that children 1320 // in different grid container continuations may be in arbitrary order. 1321 1322 const auto didPushItemsBit = IsFlexContainerFrame() 1323 ? NS_STATE_FLEX_DID_PUSH_ITEMS 1324 : NS_STATE_GRID_DID_PUSH_ITEMS; 1325 const auto hasChildNifBit = IsFlexContainerFrame() 1326 ? NS_STATE_FLEX_HAS_CHILD_NIFS 1327 : NS_STATE_GRID_HAS_CHILD_NIFS; 1328 1329 auto* prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow()); 1330 // Merge overflow frames from our prev-in-flow into our principal child list. 1331 if (prevInFlow) { 1332 AutoFrameListPtr overflow(PresContext(), prevInFlow->StealOverflowFrames()); 1333 if (overflow) { 1334 ReparentFrames(*overflow, prevInFlow, this); 1335 MergeSortedFrameLists(mFrames, *overflow, GetContent()); 1336 1337 // Move trailing next-in-flows into our overflow list. 1338 nsFrameList continuations; 1339 for (nsIFrame* f = mFrames.FirstChild(); f;) { 1340 nsIFrame* next = f->GetNextSibling(); 1341 nsIFrame* pif = f->GetPrevInFlow(); 1342 if (pif && pif->GetParent() == this) { 1343 mFrames.RemoveFrame(f); 1344 continuations.AppendFrame(nullptr, f); 1345 } 1346 f = next; 1347 } 1348 MergeSortedOverflow(continuations); 1349 1350 // Move prev-in-flow's excess overflow containers list into our own 1351 // overflow containers list. If we already have an excess overflow 1352 // containers list, any child in that list which doesn't have a 1353 // prev-in-flow in this frame is also merged into our overflow container 1354 // list. 1355 nsFrameList* overflowContainers = 1356 DrainExcessOverflowContainersList(MergeSortedFrameListsFor); 1357 1358 // Move trailing OC next-in-flows into our excess overflow containers 1359 // list. 1360 if (overflowContainers) { 1361 nsFrameList moveToEOC; 1362 for (nsIFrame* f = overflowContainers->FirstChild(); f;) { 1363 nsIFrame* next = f->GetNextSibling(); 1364 nsIFrame* pif = f->GetPrevInFlow(); 1365 if (pif && pif->GetParent() == this) { 1366 overflowContainers->RemoveFrame(f); 1367 moveToEOC.AppendFrame(nullptr, f); 1368 } 1369 f = next; 1370 } 1371 if (overflowContainers->IsEmpty()) { 1372 DestroyOverflowContainers(); 1373 } 1374 MergeSortedExcessOverflowContainers(moveToEOC); 1375 } 1376 } 1377 } 1378 1379 // For each item in aItems, pull up its next-in-flow (if any), and reparent it 1380 // to our next-in-flow, unless its parent is already ourselves or our 1381 // next-in-flow (to avoid leaving a hole there). 1382 auto PullItemsNextInFlow = [this](const nsFrameList& aItems) { 1383 auto* firstNIF = static_cast<nsContainerFrame*>(GetNextInFlow()); 1384 if (!firstNIF) { 1385 return; 1386 } 1387 nsFrameList childNIFs; 1388 nsFrameList childOCNIFs; 1389 for (auto* child : aItems) { 1390 if (auto* childNIF = child->GetNextInFlow()) { 1391 if (auto* parent = childNIF->GetParent(); 1392 parent != this && parent != firstNIF) { 1393 parent->StealFrame(childNIF); 1394 ReparentFrame(childNIF, parent, firstNIF); 1395 if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1396 childOCNIFs.AppendFrame(nullptr, childNIF); 1397 } else { 1398 childNIFs.AppendFrame(nullptr, childNIF); 1399 } 1400 } 1401 } 1402 } 1403 // Merge aItems' NIFs into our NIF's respective overflow child lists. 1404 firstNIF->MergeSortedOverflow(childNIFs); 1405 firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs); 1406 }; 1407 1408 // Merge our own overflow frames into our principal child list, 1409 // except those that are a next-in-flow for one of our items. 1410 DebugOnly<bool> foundOwnPushedChild = false; 1411 { 1412 nsFrameList* ourOverflow = GetOverflowFrames(); 1413 if (ourOverflow) { 1414 nsFrameList items; 1415 for (nsIFrame* f = ourOverflow->FirstChild(); f;) { 1416 nsIFrame* next = f->GetNextSibling(); 1417 nsIFrame* pif = f->GetPrevInFlow(); 1418 if (!pif || pif->GetParent() != this) { 1419 MOZ_ASSERT(f->GetParent() == this); 1420 ourOverflow->RemoveFrame(f); 1421 items.AppendFrame(nullptr, f); 1422 if (!pif) { 1423 foundOwnPushedChild = true; 1424 } 1425 } 1426 f = next; 1427 } 1428 1429 if (ourOverflow->IsEmpty()) { 1430 DestroyOverflowList(); 1431 ourOverflow = nullptr; 1432 } 1433 if (items.NotEmpty()) { 1434 PullItemsNextInFlow(items); 1435 } 1436 MergeSortedFrameLists(mFrames, items, GetContent()); 1437 } 1438 } 1439 1440 // Push any child next-in-flows in our principal list to OverflowList. 1441 if (HasAnyStateBits(hasChildNifBit)) { 1442 nsFrameList framesToPush; 1443 nsIFrame* firstChild = mFrames.FirstChild(); 1444 // Note that we potentially modify our mFrames list as we go. 1445 for (auto* child = firstChild; child; child = child->GetNextSibling()) { 1446 if (auto* childNIF = child->GetNextInFlow()) { 1447 if (childNIF->GetParent() == this) { 1448 for (auto* c = child->GetNextSibling(); c; c = c->GetNextSibling()) { 1449 if (c == childNIF) { 1450 // child's next-in-flow is in our principal child list, push it. 1451 mFrames.RemoveFrame(childNIF); 1452 framesToPush.AppendFrame(nullptr, childNIF); 1453 break; 1454 } 1455 } 1456 } 1457 } 1458 } 1459 if (!framesToPush.IsEmpty()) { 1460 MergeSortedOverflow(framesToPush); 1461 } 1462 RemoveStateBits(hasChildNifBit); 1463 } 1464 1465 // Pull up any first-in-flow children we might have pushed. 1466 if (HasAnyStateBits(didPushItemsBit)) { 1467 RemoveStateBits(didPushItemsBit); 1468 nsFrameList items; 1469 auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow()); 1470 DebugOnly<bool> nifNeedPushedItem = false; 1471 while (nif) { 1472 nsFrameList nifItems; 1473 for (nsIFrame* nifChild = nif->PrincipalChildList().FirstChild(); 1474 nifChild;) { 1475 nsIFrame* next = nifChild->GetNextSibling(); 1476 if (!nifChild->GetPrevInFlow()) { 1477 nif->StealFrame(nifChild); 1478 ReparentFrame(nifChild, nif, this); 1479 nifItems.AppendFrame(nullptr, nifChild); 1480 nifNeedPushedItem = false; 1481 } 1482 nifChild = next; 1483 } 1484 MergeSortedFrameLists(items, nifItems, GetContent()); 1485 1486 if (!nif->HasAnyStateBits(didPushItemsBit)) { 1487 MOZ_ASSERT(!nifNeedPushedItem || mDidPushItemsBitMayLie, 1488 "The state bit stored in didPushItemsBit lied!"); 1489 break; 1490 } 1491 nifNeedPushedItem = true; 1492 1493 for (nsIFrame* nifChild = 1494 nif->GetChildList(FrameChildListID::Overflow).FirstChild(); 1495 nifChild;) { 1496 nsIFrame* next = nifChild->GetNextSibling(); 1497 if (!nifChild->GetPrevInFlow()) { 1498 nif->StealFrame(nifChild); 1499 ReparentFrame(nifChild, nif, this); 1500 nifItems.AppendFrame(nullptr, nifChild); 1501 nifNeedPushedItem = false; 1502 } 1503 nifChild = next; 1504 } 1505 MergeSortedFrameLists(items, nifItems, GetContent()); 1506 1507 nif->RemoveStateBits(didPushItemsBit); 1508 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow()); 1509 MOZ_ASSERT(nif || !nifNeedPushedItem || mDidPushItemsBitMayLie, 1510 "The state bit stored in didPushItemsBit lied!"); 1511 } 1512 1513 if (!items.IsEmpty()) { 1514 PullItemsNextInFlow(items); 1515 } 1516 1517 MOZ_ASSERT( 1518 foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie, 1519 "The state bit stored in didPushItemsBit lied!"); 1520 MergeSortedFrameLists(mFrames, items, GetContent()); 1521 } 1522 } 1523 1524 void nsContainerFrame::NoteNewChildren(ChildListID aListID, 1525 const nsFrameList& aFrameList) { 1526 MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list"); 1527 MOZ_ASSERT(IsFlexOrGridContainer(), 1528 "Only Flex / Grid containers can call this!"); 1529 1530 mozilla::PresShell* presShell = PresShell(); 1531 const auto didPushItemsBit = IsFlexContainerFrame() 1532 ? NS_STATE_FLEX_DID_PUSH_ITEMS 1533 : NS_STATE_GRID_DID_PUSH_ITEMS; 1534 for (auto* pif = GetPrevInFlow(); pif; pif = pif->GetPrevInFlow()) { 1535 pif->AddStateBits(didPushItemsBit); 1536 presShell->FrameNeedsReflow(pif, IntrinsicDirty::FrameAndAncestors, 1537 NS_FRAME_IS_DIRTY); 1538 } 1539 } 1540 1541 bool nsContainerFrame::MoveOverflowToChildList() { 1542 bool result = false; 1543 1544 // Check for an overflow list with our prev-in-flow 1545 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow(); 1546 if (nullptr != prevInFlow) { 1547 AutoFrameListPtr prevOverflowFrames(PresContext(), 1548 prevInFlow->StealOverflowFrames()); 1549 if (prevOverflowFrames) { 1550 // Tables are special; they can have repeated header/footer 1551 // frames on mFrames at this point. 1552 NS_ASSERTION(mFrames.IsEmpty() || IsTableFrame(), "bad overflow list"); 1553 mFrames.AppendFrames(this, std::move(*prevOverflowFrames)); 1554 result = true; 1555 } 1556 } 1557 1558 // It's also possible that we have an overflow list for ourselves. 1559 return DrainSelfOverflowList() || result; 1560 } 1561 1562 void nsContainerFrame::MergeSortedOverflow(nsFrameList& aList) { 1563 if (aList.IsEmpty()) { 1564 return; 1565 } 1566 MOZ_ASSERT( 1567 !aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER), 1568 "this is the wrong list to put this child frame"); 1569 MOZ_ASSERT(aList.FirstChild()->GetParent() == this); 1570 nsFrameList* overflow = GetOverflowFrames(); 1571 if (overflow) { 1572 MergeSortedFrameLists(*overflow, aList, GetContent()); 1573 } else { 1574 SetOverflowFrames(std::move(aList)); 1575 } 1576 } 1577 1578 void nsContainerFrame::MergeSortedExcessOverflowContainers(nsFrameList& aList) { 1579 if (aList.IsEmpty()) { 1580 return; 1581 } 1582 MOZ_ASSERT( 1583 aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER), 1584 "this is the wrong list to put this child frame"); 1585 MOZ_ASSERT(aList.FirstChild()->GetParent() == this); 1586 if (nsFrameList* eoc = GetExcessOverflowContainers()) { 1587 MergeSortedFrameLists(*eoc, aList, GetContent()); 1588 } else { 1589 SetExcessOverflowContainers(std::move(aList)); 1590 } 1591 } 1592 1593 nsIFrame* nsContainerFrame::GetFirstNonAnonBoxInSubtree(nsIFrame* aFrame) { 1594 while (aFrame) { 1595 // If aFrame isn't an anonymous container, or it's text or such, then it'll 1596 // do. 1597 if (!aFrame->Style()->IsAnonBox() || 1598 nsCSSAnonBoxes::IsNonElement(aFrame->Style()->GetPseudoType())) { 1599 break; 1600 } 1601 1602 // Otherwise, descend to its first child and repeat. 1603 1604 // SPECIAL CASE: if we're dealing with an anonymous table, then it might 1605 // be wrapping something non-anonymous in its col-group list 1606 // (instead of its principal child list), so we have to look there. 1607 // (Note: For anonymous tables that have a non-anon cell *and* a non-anon 1608 // column, we'll always return the column. This is fine; we're really just 1609 // looking for a handle to *anything* with a meaningful content node inside 1610 // the table, for use in DOM comparisons to things outside of the table.) 1611 if (MOZ_UNLIKELY(aFrame->IsTableFrame())) { 1612 nsIFrame* colgroupDescendant = GetFirstNonAnonBoxInSubtree( 1613 aFrame->GetChildList(FrameChildListID::ColGroup).FirstChild()); 1614 if (colgroupDescendant) { 1615 return colgroupDescendant; 1616 } 1617 } 1618 1619 // USUAL CASE: Descend to the first child in principal list. 1620 aFrame = aFrame->PrincipalChildList().FirstChild(); 1621 } 1622 return aFrame; 1623 } 1624 1625 /** 1626 * Is aFrame1 a prev-continuation of aFrame2? 1627 */ 1628 static bool IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2) { 1629 nsIFrame* prev = aFrame2; 1630 while ((prev = prev->GetPrevContinuation())) { 1631 if (prev == aFrame1) { 1632 return true; 1633 } 1634 } 1635 return false; 1636 } 1637 1638 void nsContainerFrame::MergeSortedFrameLists(nsFrameList& aDest, 1639 nsFrameList& aSrc, 1640 nsIContent* aCommonAncestor) { 1641 // Returns a frame whose DOM node can be used for the purpose of ordering 1642 // aFrame among its sibling frames by DOM position. If aFrame is 1643 // non-anonymous, this just returns aFrame itself. Otherwise, this returns the 1644 // first non-anonymous descendant in aFrame's continuation chain. 1645 auto FrameForDOMPositionComparison = [](nsIFrame* aFrame) { 1646 if (!aFrame->Style()->IsAnonBox()) { 1647 // The usual case. 1648 return aFrame; 1649 } 1650 1651 // Walk the continuation chain from the start, and return the first 1652 // non-anonymous descendant that we find. 1653 for (nsIFrame* f = aFrame->FirstContinuation(); f; 1654 f = f->GetNextContinuation()) { 1655 if (nsIFrame* nonAnonBox = GetFirstNonAnonBoxInSubtree(f)) { 1656 return nonAnonBox; 1657 } 1658 } 1659 1660 MOZ_ASSERT_UNREACHABLE( 1661 "Why is there no non-anonymous descendants in the continuation chain?"); 1662 return aFrame; 1663 }; 1664 1665 nsIFrame* dest = aDest.FirstChild(); 1666 for (nsIFrame* src = aSrc.FirstChild(); src;) { 1667 if (!dest) { 1668 aDest.AppendFrames(nullptr, std::move(aSrc)); 1669 break; 1670 } 1671 nsIContent* srcContent = FrameForDOMPositionComparison(src)->GetContent(); 1672 nsIContent* destContent = FrameForDOMPositionComparison(dest)->GetContent(); 1673 int32_t result = nsContentUtils::CompareTreePosition<TreeKind::Flat>( 1674 srcContent, destContent, aCommonAncestor); 1675 if (MOZ_UNLIKELY(result == 0)) { 1676 // NOTE: we get here when comparing ::before/::after for the same element. 1677 if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForBefore())) { 1678 if (MOZ_LIKELY(!destContent->IsGeneratedContentContainerForBefore()) || 1679 ::IsPrevContinuationOf(src, dest)) { 1680 result = -1; 1681 } 1682 } else if (MOZ_UNLIKELY( 1683 srcContent->IsGeneratedContentContainerForAfter())) { 1684 if (MOZ_UNLIKELY(destContent->IsGeneratedContentContainerForAfter()) && 1685 ::IsPrevContinuationOf(src, dest)) { 1686 result = -1; 1687 } 1688 } else if (::IsPrevContinuationOf(src, dest)) { 1689 result = -1; 1690 } 1691 } 1692 if (result < 0) { 1693 // src should come before dest 1694 nsIFrame* next = src->GetNextSibling(); 1695 aSrc.RemoveFrame(src); 1696 aDest.InsertFrame(nullptr, dest->GetPrevSibling(), src); 1697 src = next; 1698 } else { 1699 dest = dest->GetNextSibling(); 1700 } 1701 } 1702 MOZ_ASSERT(aSrc.IsEmpty()); 1703 } 1704 1705 bool nsContainerFrame::MoveInlineOverflowToChildList(nsIFrame* aLineContainer) { 1706 MOZ_ASSERT(aLineContainer, 1707 "Must have line container for moving inline overflows"); 1708 1709 bool result = false; 1710 1711 // Check for an overflow list with our prev-in-flow 1712 if (auto prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow())) { 1713 AutoFrameListPtr prevOverflowFrames(PresContext(), 1714 prevInFlow->StealOverflowFrames()); 1715 if (prevOverflowFrames) { 1716 // We may need to reparent floats from prev-in-flow to our line 1717 // container if the container has prev continuation. 1718 if (aLineContainer->GetPrevContinuation()) { 1719 ReparentFloatsForInlineChild(aLineContainer, 1720 prevOverflowFrames->FirstChild(), true); 1721 } 1722 // Prepend overflow frames to the list. 1723 mFrames.InsertFrames(this, nullptr, std::move(*prevOverflowFrames)); 1724 result = true; 1725 } 1726 } 1727 1728 // It's also possible that we have overflow list for ourselves. 1729 return DrainSelfOverflowList() || result; 1730 } 1731 1732 bool nsContainerFrame::DrainSelfOverflowList() { 1733 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); 1734 if (overflowFrames) { 1735 mFrames.AppendFrames(nullptr, std::move(*overflowFrames)); 1736 return true; 1737 } 1738 return false; 1739 } 1740 1741 bool nsContainerFrame::DrainAndMergeSelfOverflowList() { 1742 MOZ_ASSERT(IsFlexOrGridContainer(), 1743 "Only Flex / Grid containers can call this!"); 1744 1745 // Unlike nsContainerFrame::DrainSelfOverflowList, flex or grid containers 1746 // need to merge these lists so that the resulting mFrames is in document 1747 // content order. 1748 // NOTE: nsContainerFrame::AppendFrames/InsertFrames calls this method and 1749 // there are also direct calls from the fctor (FindAppendPrevSibling). 1750 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); 1751 if (overflowFrames) { 1752 MergeSortedFrameLists(mFrames, *overflowFrames, GetContent()); 1753 // We set a frame bit to push them again in Reflow() to avoid creating 1754 // multiple flex / grid items per flex / grid container fragment for the 1755 // same content. 1756 AddStateBits(IsFlexContainerFrame() ? NS_STATE_FLEX_HAS_CHILD_NIFS 1757 : NS_STATE_GRID_HAS_CHILD_NIFS); 1758 return true; 1759 } 1760 return false; 1761 } 1762 1763 nsFrameList* nsContainerFrame::DrainExcessOverflowContainersList( 1764 ChildFrameMerger aMergeFunc) { 1765 nsFrameList* overflowContainers = GetOverflowContainers(); 1766 1767 // Drain excess overflow containers from our prev-in-flow. 1768 if (auto* prev = static_cast<nsContainerFrame*>(GetPrevInFlow())) { 1769 AutoFrameListPtr excessFrames(PresContext(), 1770 prev->StealExcessOverflowContainers()); 1771 if (excessFrames) { 1772 excessFrames->ApplySetParent(this); 1773 if (overflowContainers) { 1774 // The default merge function is AppendFrames, so we use excessFrames as 1775 // the destination and then assign the result to overflowContainers. 1776 aMergeFunc(*excessFrames, *overflowContainers, this); 1777 *overflowContainers = std::move(*excessFrames); 1778 } else { 1779 overflowContainers = SetOverflowContainers(std::move(*excessFrames)); 1780 } 1781 } 1782 } 1783 1784 // Our own excess overflow containers from a previous reflow can still be 1785 // present if our next-in-flow hasn't been reflown yet. Move any children 1786 // from it that don't have a continuation in this frame to the 1787 // OverflowContainers list. 1788 AutoFrameListPtr selfExcessOCFrames(PresContext(), 1789 StealExcessOverflowContainers()); 1790 if (selfExcessOCFrames) { 1791 nsFrameList toMove; 1792 auto child = selfExcessOCFrames->FirstChild(); 1793 while (child) { 1794 auto next = child->GetNextSibling(); 1795 MOZ_ASSERT(child->GetPrevInFlow(), 1796 "ExcessOverflowContainers frames must be continuations"); 1797 if (child->GetPrevInFlow()->GetParent() != this) { 1798 selfExcessOCFrames->RemoveFrame(child); 1799 toMove.AppendFrame(nullptr, child); 1800 } 1801 child = next; 1802 } 1803 1804 // If there's any remaining excess overflow containers, put them back. 1805 if (selfExcessOCFrames->NotEmpty()) { 1806 SetExcessOverflowContainers(std::move(*selfExcessOCFrames)); 1807 } 1808 1809 if (toMove.NotEmpty()) { 1810 if (overflowContainers) { 1811 aMergeFunc(*overflowContainers, toMove, this); 1812 } else { 1813 overflowContainers = SetOverflowContainers(std::move(toMove)); 1814 } 1815 } 1816 } 1817 1818 return overflowContainers; 1819 } 1820 1821 nsIFrame* nsContainerFrame::GetNextInFlowChild( 1822 ContinuationTraversingState& aState, bool* aIsInOverflow) { 1823 nsContainerFrame*& nextInFlow = aState.mNextInFlow; 1824 while (nextInFlow) { 1825 // See if there is any frame in the container 1826 nsIFrame* frame = nextInFlow->mFrames.FirstChild(); 1827 if (frame) { 1828 if (aIsInOverflow) { 1829 *aIsInOverflow = false; 1830 } 1831 return frame; 1832 } 1833 // No frames in the principal list, try its overflow list 1834 nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames(); 1835 if (overflowFrames) { 1836 if (aIsInOverflow) { 1837 *aIsInOverflow = true; 1838 } 1839 return overflowFrames->FirstChild(); 1840 } 1841 nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow()); 1842 } 1843 return nullptr; 1844 } 1845 1846 nsIFrame* nsContainerFrame::PullNextInFlowChild( 1847 ContinuationTraversingState& aState) { 1848 bool isInOverflow; 1849 nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow); 1850 if (frame) { 1851 nsContainerFrame* nextInFlow = aState.mNextInFlow; 1852 if (isInOverflow) { 1853 nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames(); 1854 overflowFrames->RemoveFirstChild(); 1855 if (overflowFrames->IsEmpty()) { 1856 nextInFlow->DestroyOverflowList(); 1857 } 1858 } else { 1859 nextInFlow->mFrames.RemoveFirstChild(); 1860 } 1861 1862 // Move the frame to the principal frame list of this container 1863 mFrames.AppendFrame(this, frame); 1864 } 1865 return frame; 1866 } 1867 1868 /* static */ 1869 void nsContainerFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer, 1870 nsIFrame* aFrame, 1871 bool aReparentSiblings) { 1872 // XXXbz this would be better if it took a nsFrameList or a frame 1873 // list slice.... 1874 NS_ASSERTION(aOurLineContainer->GetNextContinuation() || 1875 aOurLineContainer->GetPrevContinuation(), 1876 "Don't call this when we have no continuation, it's a waste"); 1877 if (!aFrame) { 1878 NS_ASSERTION(aReparentSiblings, "Why did we get called?"); 1879 return; 1880 } 1881 1882 nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame); 1883 if (!frameBlock || frameBlock == aOurLineContainer) { 1884 return; 1885 } 1886 1887 nsBlockFrame* ourBlock = do_QueryFrame(aOurLineContainer); 1888 NS_ASSERTION(ourBlock, "Not a block, but broke vertically?"); 1889 1890 while (true) { 1891 ourBlock->ReparentFloats(aFrame, frameBlock, false); 1892 1893 if (!aReparentSiblings) { 1894 return; 1895 } 1896 nsIFrame* next = aFrame->GetNextSibling(); 1897 if (!next) { 1898 return; 1899 } 1900 if (next->GetParent() == aFrame->GetParent()) { 1901 aFrame = next; 1902 continue; 1903 } 1904 // This is paranoid and will hardly ever get hit ... but we can't actually 1905 // trust that the frames in the sibling chain all have the same parent, 1906 // because lazy reparenting may be going on. If we find a different 1907 // parent we need to redo our analysis. 1908 ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings); 1909 return; 1910 } 1911 } 1912 1913 bool nsContainerFrame::ResolvedOrientationIsVertical() const { 1914 StyleOrient orient = StyleDisplay()->mOrient; 1915 switch (orient) { 1916 case StyleOrient::Horizontal: 1917 return false; 1918 case StyleOrient::Vertical: 1919 return true; 1920 case StyleOrient::Inline: 1921 return GetWritingMode().IsVertical(); 1922 case StyleOrient::Block: 1923 return !GetWritingMode().IsVertical(); 1924 } 1925 MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value"); 1926 return false; 1927 } 1928 1929 LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( 1930 gfxContext* aRenderingContext, WritingMode aWM, 1931 const IntrinsicSize& aIntrinsicSize, const AspectRatio& aAspectRatio, 1932 const LogicalSize& aCBSize, const LogicalSize& aMargin, 1933 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, 1934 ComputeSizeFlags aFlags) { 1935 const nsStylePosition* stylePos = StylePosition(); 1936 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 1937 const auto styleISize = 1938 aSizeOverrides.mStyleISize 1939 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize) 1940 : stylePos->ISize(aWM, anchorResolutionParams); 1941 1942 const auto styleBSize = [&] { 1943 auto styleBSizeConsideringOverrides = 1944 aSizeOverrides.mStyleBSize 1945 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize) 1946 : stylePos->BSize(aWM, anchorResolutionParams); 1947 if (styleBSizeConsideringOverrides->BehavesLikeStretchOnBlockAxis() && 1948 aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) { 1949 // We've got a 'stretch' BSize; resolve it to a length: 1950 nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize( 1951 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM), 1952 stylePos->mBoxSizing); 1953 // Note(dshin): This allocates. 1954 return AnchorResolvedSizeHelper::LengthPercentage( 1955 LengthPercentage::FromAppUnits(stretchBSize)); 1956 } 1957 return styleBSizeConsideringOverrides; 1958 }(); 1959 1960 const auto& aspectRatio = 1961 aSizeOverrides.mAspectRatio ? *aSizeOverrides.mAspectRatio : aAspectRatio; 1962 1963 auto* parentFrame = GetParent(); 1964 const bool isGridItem = IsGridItem(); 1965 1966 // flexItemMainAxis is set if this frame is a flex item in a modern flexbox 1967 // layout. It indicates which logical axis (in this frame's own WM) 1968 // corresponds to its flex container's main axis. 1969 Maybe<LogicalAxis> flexItemMainAxis; 1970 if (IsFlexItem() && !parentFrame->HasAnyStateBits( 1971 NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX)) { 1972 flexItemMainAxis = Some(nsFlexContainerFrame::IsItemInlineAxisMainAxis(this) 1973 ? LogicalAxis::Inline 1974 : LogicalAxis::Block); 1975 } 1976 1977 // Handle intrinsic sizes and their interaction with 1978 // {min-,max-,}{width,height} according to the rules in 1979 // https://www.w3.org/TR/CSS22/visudet.html#min-max-widths and 1980 // https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes 1981 1982 // Note: throughout the following section of the function, I avoid 1983 // a * (b / c) because of its reduced accuracy relative to a * b / c 1984 // or (a * b) / c (which are equivalent). 1985 1986 const bool isAutoOrMaxContentISize = 1987 styleISize->IsAuto() || styleISize->IsMaxContent(); 1988 const bool isAutoBSize = 1989 nsLayoutUtils::IsAutoBSize(*styleBSize, aCBSize.BSize(aWM)); 1990 1991 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border 1992 ? aBorderPadding 1993 : LogicalSize(aWM); 1994 const nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + 1995 aBorderPadding.ISize(aWM) - 1996 boxSizingAdjust.ISize(aWM); 1997 1998 // We don't expect these intial values of iSize/bSize to be used, but this 1999 // silences a GCC warning about them being uninitialized. 2000 nscoord minISize, maxISize, minBSize, maxBSize, iSize = 0, bSize = 0; 2001 enum class FillCB { 2002 // No stretching or clamping in the relevant axis. 2003 No, 2004 // Stretch to fill the containing block size in the relevant axis while 2005 // preserving the aspect-ratio. If both axes are stretched, the aspect-ratio 2006 // will be disregarded. 2007 Stretch, 2008 // Clamp to fill the containing block size in the relevant axis while 2009 // preserving the aspect-ratio. If both axes are clamped, the aspect-ratio 2010 // is respected, and the dimensions do not overflow the containing block 2011 // size. 2012 Clamp, 2013 }; 2014 2015 FillCB inlineFillCB = FillCB::No; // fill CB behavior in the inline axis 2016 FillCB blockFillCB = FillCB::No; // fill CB behavior in the block axis 2017 2018 const LogicalSize fallbackIntrinsicSize(aWM, kFallbackIntrinsicSize); 2019 const Maybe<nscoord>& maybeIntrinsicISize = aIntrinsicSize.ISize(aWM); 2020 const bool hasIntrinsicISize = maybeIntrinsicISize.isSome(); 2021 nscoord intrinsicISize = std::max(0, maybeIntrinsicISize.valueOr(0)); 2022 2023 const auto& maybeIntrinsicBSize = aIntrinsicSize.BSize(aWM); 2024 const bool hasIntrinsicBSize = maybeIntrinsicBSize.isSome(); 2025 nscoord intrinsicBSize = std::max(0, maybeIntrinsicBSize.valueOr(0)); 2026 2027 Maybe<nscoord> iSizeToFillCB; 2028 Maybe<nscoord> bSizeToFillCB; 2029 if (!isAutoOrMaxContentISize) { 2030 iSize = ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust, 2031 boxSizingToMarginEdgeISize, *styleISize, 2032 *styleBSize, aspectRatio, aFlags) 2033 .mISize; 2034 } else if (MOZ_UNLIKELY(isGridItem) && 2035 !parentFrame->IsMasonry(aWM, LogicalAxis::Inline)) { 2036 MOZ_ASSERT(!IsTrueOverflowContainer()); 2037 // 'auto' inline-size for grid-level box - apply 'stretch' as needed: 2038 auto cbSize = aCBSize.ISize(aWM); 2039 if (cbSize != NS_UNCONSTRAINEDSIZE) { 2040 if (!StyleMargin()->HasInlineAxisAuto( 2041 aWM, AnchorPosResolutionParams::From(this))) { 2042 auto inlineAxisAlignment = stylePos->UsedSelfAlignment( 2043 aWM, LogicalAxis::Inline, parentFrame->GetWritingMode(), 2044 parentFrame->Style()); 2045 if (inlineAxisAlignment == StyleAlignFlags::STRETCH) { 2046 inlineFillCB = FillCB::Stretch; 2047 } 2048 } 2049 if (inlineFillCB != FillCB::No || 2050 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) { 2051 iSizeToFillCB.emplace(std::max( 2052 0, cbSize - aBorderPadding.ISize(aWM) - aMargin.ISize(aWM))); 2053 } 2054 } else { 2055 // Reset this flag to avoid applying the clamping below. 2056 aFlags -= ComputeSizeFlag::IClampMarginBoxMinSize; 2057 } 2058 } 2059 2060 // Flex items ignore their min & max sizing properties in their flex 2061 // container's main-axis. (Those properties get applied later in the flexbox 2062 // algorithm.) 2063 const bool isFlexItemInlineAxisMainAxis = 2064 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Inline; 2065 const auto maxISizeCoord = stylePos->MaxISize(aWM, anchorResolutionParams); 2066 if (!maxISizeCoord->IsNone() && !isFlexItemInlineAxisMainAxis) { 2067 maxISize = 2068 ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust, 2069 boxSizingToMarginEdgeISize, *maxISizeCoord, 2070 *styleBSize, aspectRatio, aFlags) 2071 .mISize; 2072 } else { 2073 maxISize = nscoord_MAX; 2074 } 2075 2076 const auto minISizeCoord = stylePos->MinISize(aWM, anchorResolutionParams); 2077 if (!minISizeCoord->IsAuto() && !isFlexItemInlineAxisMainAxis) { 2078 minISize = 2079 ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust, 2080 boxSizingToMarginEdgeISize, *minISizeCoord, 2081 *styleBSize, aspectRatio, aFlags) 2082 .mISize; 2083 } else { 2084 // Treat "min-width: auto" as 0. 2085 // NOTE: Technically, "auto" is supposed to behave like "min-content" on 2086 // flex items. However, we don't need to worry about that here, because 2087 // flex items' min-sizes are intentionally ignored until the flex 2088 // container explicitly considers them during space distribution. 2089 minISize = 0; 2090 } 2091 2092 if (!isAutoBSize) { 2093 bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch( 2094 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM), 2095 boxSizingAdjust.BSize(aWM), *styleBSize); 2096 } else if (MOZ_UNLIKELY(isGridItem) && 2097 !parentFrame->IsMasonry(aWM, LogicalAxis::Block)) { 2098 MOZ_ASSERT(!IsTrueOverflowContainer()); 2099 // 'auto' block-size for grid-level box - apply 'stretch' as needed: 2100 auto cbSize = aCBSize.BSize(aWM); 2101 if (cbSize != NS_UNCONSTRAINEDSIZE) { 2102 if (!StyleMargin()->HasBlockAxisAuto( 2103 aWM, AnchorPosResolutionParams::From(this))) { 2104 auto blockAxisAlignment = stylePos->UsedSelfAlignment( 2105 aWM, LogicalAxis::Block, parentFrame->GetWritingMode(), 2106 parentFrame->Style()); 2107 if (blockAxisAlignment == StyleAlignFlags::STRETCH) { 2108 blockFillCB = FillCB::Stretch; 2109 } 2110 } 2111 if (blockFillCB != FillCB::No || 2112 aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) { 2113 bSizeToFillCB.emplace(std::max( 2114 0, cbSize - aBorderPadding.BSize(aWM) - aMargin.BSize(aWM))); 2115 } 2116 } else { 2117 // Reset this flag to avoid applying the clamping below. 2118 aFlags -= ComputeSizeFlag::BClampMarginBoxMinSize; 2119 } 2120 } 2121 2122 // Flex items ignore their min & max sizing properties in their flex 2123 // container's main-axis. (Those properties get applied later in the flexbox 2124 // algorithm.) 2125 const bool isFlexItemBlockAxisMainAxis = 2126 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Block; 2127 const auto maxBSizeCoord = stylePos->MaxBSize(aWM, anchorResolutionParams); 2128 if (!nsLayoutUtils::IsAutoBSize(*maxBSizeCoord, aCBSize.BSize(aWM)) && 2129 !isFlexItemBlockAxisMainAxis) { 2130 maxBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch( 2131 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM), 2132 boxSizingAdjust.BSize(aWM), *maxBSizeCoord); 2133 } else { 2134 maxBSize = nscoord_MAX; 2135 } 2136 2137 const auto minBSizeCoord = stylePos->MinBSize(aWM, anchorResolutionParams); 2138 if (!nsLayoutUtils::IsAutoBSize(*minBSizeCoord, aCBSize.BSize(aWM)) && 2139 !isFlexItemBlockAxisMainAxis) { 2140 minBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch( 2141 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM), 2142 boxSizingAdjust.BSize(aWM), *minBSizeCoord); 2143 } else { 2144 minBSize = 0; 2145 } 2146 2147 NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE, 2148 "Our containing block must not have unconstrained inline-size!"); 2149 MOZ_ASSERT(!(inlineFillCB != FillCB::No || 2150 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) || 2151 iSizeToFillCB, 2152 "iSizeToFillCB must be valid when stretching or clamping in the " 2153 "inline axis!"); 2154 MOZ_ASSERT(!(blockFillCB != FillCB::No || 2155 aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) || 2156 bSizeToFillCB, 2157 "bSizeToFillCB must be valid when stretching or clamping in the " 2158 "block axis!"); 2159 2160 // Now calculate the used values for iSize and bSize: 2161 if (isAutoOrMaxContentISize) { 2162 if (isAutoBSize) { 2163 // 'auto' iSize, 'auto' bSize 2164 2165 // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2: 2166 2167 nscoord tentISize, tentBSize; 2168 2169 if (hasIntrinsicISize) { 2170 tentISize = intrinsicISize; 2171 } else if (hasIntrinsicBSize && aspectRatio) { 2172 tentISize = aspectRatio.ComputeRatioDependentSize( 2173 LogicalAxis::Inline, aWM, intrinsicBSize, boxSizingAdjust); 2174 } else if (aspectRatio) { 2175 tentISize = 2176 aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) - aMargin.ISize(aWM); 2177 if (tentISize < 0) { 2178 tentISize = 0; 2179 } 2180 } else { 2181 tentISize = fallbackIntrinsicSize.ISize(aWM); 2182 } 2183 2184 // If we need to clamp the inline size to fit the CB, we use the 'stretch' 2185 // or 'normal' codepath. We use the ratio-preserving 'normal' codepath 2186 // unless we have 'stretch' in the other axis. 2187 if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize) && 2188 inlineFillCB != FillCB::Stretch && tentISize > *iSizeToFillCB) { 2189 inlineFillCB = 2190 (blockFillCB == FillCB::Stretch ? FillCB::Stretch : FillCB::Clamp); 2191 } 2192 2193 if (hasIntrinsicBSize) { 2194 tentBSize = intrinsicBSize; 2195 } else if (aspectRatio) { 2196 tentBSize = aspectRatio.ComputeRatioDependentSize( 2197 LogicalAxis::Block, aWM, tentISize, boxSizingAdjust); 2198 } else { 2199 tentBSize = fallbackIntrinsicSize.BSize(aWM); 2200 } 2201 2202 // (ditto the comment about clamping the inline size above) 2203 if (aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize) && 2204 blockFillCB != FillCB::Stretch && tentBSize > *bSizeToFillCB) { 2205 blockFillCB = 2206 (inlineFillCB == FillCB::Stretch ? FillCB::Stretch : FillCB::Clamp); 2207 } 2208 2209 // The slash notation is the used value of "align-self / justify-self". 2210 if (inlineFillCB == FillCB::Stretch) { 2211 tentISize = *iSizeToFillCB; // * / 'stretch' 2212 if (blockFillCB == FillCB::Stretch) { 2213 tentBSize = *bSizeToFillCB; // 'stretch' / 'stretch' 2214 } else if (aspectRatio) { 2215 // * (except 'stretch') / 'stretch' 2216 tentBSize = aspectRatio.ComputeRatioDependentSize( 2217 LogicalAxis::Block, aWM, *iSizeToFillCB, boxSizingAdjust); 2218 } 2219 } else if (blockFillCB == FillCB::Stretch) { 2220 tentBSize = *bSizeToFillCB; // 'stretch' / * (except 'stretch') 2221 if (aspectRatio) { 2222 tentISize = aspectRatio.ComputeRatioDependentSize( 2223 LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust); 2224 } 2225 } else if (inlineFillCB == FillCB::Clamp && aspectRatio) { 2226 tentISize = *iSizeToFillCB; // * (except 'stretch') / 'normal' 2227 tentBSize = aspectRatio.ComputeRatioDependentSize( 2228 LogicalAxis::Block, aWM, *iSizeToFillCB, boxSizingAdjust); 2229 if (blockFillCB == FillCB::Clamp && tentBSize > *bSizeToFillCB) { 2230 // Clamp within the CB size with preserved aspect ratio. 2231 tentBSize = *bSizeToFillCB; // 'normal' / 'normal' 2232 tentISize = aspectRatio.ComputeRatioDependentSize( 2233 LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust); 2234 } 2235 } else if (blockFillCB == FillCB::Clamp && aspectRatio) { 2236 // 'normal' / * (except 'normal' and 'stretch') 2237 tentBSize = *bSizeToFillCB; 2238 tentISize = aspectRatio.ComputeRatioDependentSize( 2239 LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust); 2240 } 2241 2242 // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when 2243 // applying the min/max-size. We don't want that when we have 'stretch' 2244 // in either axis because tentISize/tentBSize is likely not according to 2245 // ratio now. 2246 if (aspectRatio && inlineFillCB != FillCB::Stretch && 2247 blockFillCB != FillCB::Stretch) { 2248 nsSize autoSize = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions( 2249 minISize, minBSize, maxISize, maxBSize, tentISize, tentBSize); 2250 // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will 2251 // actually contain logical values if the parameters passed to it were 2252 // logical coordinates, so we do NOT perform a physical-to-logical 2253 // conversion here, but just assign the fields directly to our result. 2254 iSize = autoSize.width; 2255 bSize = autoSize.height; 2256 } else { 2257 // Not honoring an intrinsic ratio: clamp the dimensions independently. 2258 iSize = CSSMinMax(tentISize, minISize, maxISize); 2259 bSize = CSSMinMax(tentBSize, minBSize, maxBSize); 2260 } 2261 } else { 2262 // 'auto' iSize, non-'auto' bSize 2263 bSize = CSSMinMax(bSize, minBSize, maxBSize); 2264 if (inlineFillCB == FillCB::Stretch) { 2265 iSize = *iSizeToFillCB; 2266 } else if (aspectRatio) { 2267 iSize = aspectRatio.ComputeRatioDependentSize(LogicalAxis::Inline, aWM, 2268 bSize, boxSizingAdjust); 2269 } else if (hasIntrinsicISize) { 2270 iSize = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize) && 2271 intrinsicISize > *iSizeToFillCB 2272 ? *iSizeToFillCB 2273 : intrinsicISize; 2274 } else { 2275 iSize = fallbackIntrinsicSize.ISize(aWM); 2276 } 2277 iSize = CSSMinMax(iSize, minISize, maxISize); 2278 } 2279 } else { 2280 if (isAutoBSize) { 2281 // non-'auto' iSize, 'auto' bSize 2282 iSize = CSSMinMax(iSize, minISize, maxISize); 2283 if (blockFillCB == FillCB::Stretch) { 2284 bSize = *bSizeToFillCB; 2285 } else if (aspectRatio) { 2286 bSize = aspectRatio.ComputeRatioDependentSize(LogicalAxis::Block, aWM, 2287 iSize, boxSizingAdjust); 2288 } else if (hasIntrinsicBSize) { 2289 bSize = aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize) && 2290 intrinsicBSize > *bSizeToFillCB 2291 ? *bSizeToFillCB 2292 : intrinsicBSize; 2293 } else { 2294 bSize = fallbackIntrinsicSize.BSize(aWM); 2295 } 2296 bSize = CSSMinMax(bSize, minBSize, maxBSize); 2297 } else { 2298 // non-'auto' iSize, non-'auto' bSize 2299 iSize = CSSMinMax(iSize, minISize, maxISize); 2300 bSize = CSSMinMax(bSize, minBSize, maxBSize); 2301 } 2302 } 2303 2304 return LogicalSize(aWM, iSize, bSize); 2305 } 2306 2307 nsRect nsContainerFrame::ComputeSimpleTightBounds( 2308 DrawTarget* aDrawTarget) const { 2309 if (StyleOutline()->ShouldPaintOutline() || StyleBorder()->HasBorder() || 2310 !StyleBackground()->IsTransparent(this) || 2311 StyleDisplay()->HasAppearance()) { 2312 // Not necessarily tight, due to clipping, negative 2313 // outline-offset, and lots of other issues, but that's OK 2314 return InkOverflowRect(); 2315 } 2316 2317 nsRect r(0, 0, 0, 0); 2318 for (const auto& childLists : ChildLists()) { 2319 for (nsIFrame* child : childLists.mList) { 2320 r.UnionRect( 2321 r, child->ComputeTightBounds(aDrawTarget) + child->GetPosition()); 2322 } 2323 } 2324 return r; 2325 } 2326 2327 void nsContainerFrame::PushDirtyBitToAbsoluteFrames() { 2328 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY)) { 2329 return; // No dirty bit to push. 2330 } 2331 if (!HasAbsolutelyPositionedChildren()) { 2332 return; // No absolute children to push to. 2333 } 2334 GetAbsoluteContainingBlock()->MarkAllFramesDirty(); 2335 } 2336 2337 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus 2338 // 4 for the frames above the document's frames: 2339 // the Viewport, GFXScroll, ScrollPort, and Canvas 2340 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH + 4) 2341 2342 bool nsContainerFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput, 2343 ReflowOutput& aMetrics, 2344 nsReflowStatus& aStatus) { 2345 if (aReflowInput.mReflowDepth > MAX_FRAME_DEPTH) { 2346 NS_WARNING("frame tree too deep; setting zero size and returning"); 2347 AddStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE); 2348 ClearOverflowRects(); 2349 aMetrics.ClearSize(); 2350 aMetrics.SetBlockStartAscent(0); 2351 aMetrics.mCarriedOutBEndMargin.Zero(); 2352 aMetrics.mOverflowAreas.Clear(); 2353 2354 aStatus.Reset(); 2355 if (GetNextInFlow()) { 2356 // Reflow depth might vary between reflows, so we might have 2357 // successfully reflowed and split this frame before. If so, we 2358 // shouldn't delete its continuations. 2359 aStatus.SetIncomplete(); 2360 } 2361 2362 return true; 2363 } 2364 RemoveStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE); 2365 return false; 2366 } 2367 2368 bool nsContainerFrame::ShouldAvoidBreakInside( 2369 const ReflowInput& aReflowInput) const { 2370 MOZ_ASSERT(this == aReflowInput.mFrame, 2371 "Caller should pass a ReflowInput for this frame!"); 2372 2373 const auto* disp = StyleDisplay(); 2374 const bool mayAvoidBreak = [&] { 2375 switch (disp->mBreakInside) { 2376 case StyleBreakWithin::Auto: 2377 return false; 2378 case StyleBreakWithin::Avoid: 2379 return true; 2380 case StyleBreakWithin::AvoidPage: 2381 return aReflowInput.mBreakType == ReflowInput::BreakType::Page; 2382 case StyleBreakWithin::AvoidColumn: 2383 return aReflowInput.mBreakType == ReflowInput::BreakType::Column; 2384 } 2385 MOZ_ASSERT_UNREACHABLE("Unknown break-inside value"); 2386 return false; 2387 }(); 2388 2389 if (!mayAvoidBreak) { 2390 return false; 2391 } 2392 if (aReflowInput.mFlags.mIsTopOfPage) { 2393 return false; 2394 } 2395 if (IsAbsolutelyPositioned(disp)) { 2396 return false; 2397 } 2398 if (GetPrevInFlow()) { 2399 return false; 2400 } 2401 return true; 2402 } 2403 2404 void nsContainerFrame::ConsiderChildOverflow(OverflowAreas& aOverflowAreas, 2405 nsIFrame* aChildFrame, 2406 bool aAsIfScrolled) { 2407 if (StyleDisplay()->IsContainLayout() && SupportsContainLayoutAndPaint() && 2408 !aAsIfScrolled) { 2409 // If we have layout containment and are not a non-atomic, inline-level 2410 // principal box, we should only consider our child's ink overflow, 2411 // leaving the scrollable regions of the parent unaffected. 2412 // Note: scrollable overflow is a subset of ink overflow, 2413 // so this has the same affect as unioning the child's ink and 2414 // scrollable overflow with the parent's ink overflow. 2415 const OverflowAreas childOverflows(aChildFrame->InkOverflowRect(), 2416 nsRect()); 2417 aOverflowAreas.UnionWith(childOverflows + aChildFrame->GetPosition()); 2418 } else { 2419 aOverflowAreas.UnionWith( 2420 aChildFrame->GetActualAndNormalOverflowAreasRelativeToParent()); 2421 } 2422 } 2423 2424 StyleAlignFlags nsContainerFrame::CSSAlignmentForAbsPosChild( 2425 const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const { 2426 MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(), 2427 "This method should only be called for abspos children"); 2428 // For computing the static position of an absolutely positioned box, 2429 // `auto` takes from parent's `align-items`. 2430 StyleAlignFlags alignment = 2431 aChildRI.mStylePosition->UsedSelfAlignment(aLogicalAxis, Style()); 2432 return CSSAlignUtils::UsedAlignmentForAbsPos(aChildRI.mFrame, alignment, 2433 aLogicalAxis, GetWritingMode()); 2434 } 2435 2436 StyleAlignFlags 2437 nsContainerFrame::CSSAlignmentForAbsPosChildWithinContainingBlock( 2438 const SizeComputationInput& aSizingInput, LogicalAxis aLogicalAxis, 2439 const StylePositionArea& aResolvedPositionArea, 2440 const LogicalSize& aCBSize) const { 2441 MOZ_ASSERT(aSizingInput.mFrame->IsAbsolutelyPositioned(), 2442 "This method should only be called for abspos children"); 2443 // When determining the position of absolutely-positioned boxes, 2444 // `auto` behaves as `normal`. 2445 StyleAlignFlags alignment = 2446 aSizingInput.mFrame->StylePosition()->UsedSelfAlignment(aLogicalAxis, 2447 nullptr); 2448 2449 // Check if position-area is set - if so, it determines the default alignment 2450 // https://drafts.csswg.org/css-anchor-position/#position-area-alignment 2451 if (!aResolvedPositionArea.IsNone() && alignment == StyleAlignFlags::NORMAL) { 2452 const WritingMode cbWM = GetWritingMode(); 2453 const auto anchorResolutionParams = AnchorPosResolutionParams::From( 2454 &aSizingInput, /* aIgnorePositionArea = */ true); 2455 const auto anchorOffsetResolutionParams = 2456 AnchorPosOffsetResolutionParams::ExplicitCBFrameSize( 2457 anchorResolutionParams, &aCBSize); 2458 2459 // Check if we have exactly one auto inset in this axis (IMCB situation) 2460 const auto singleAutoInset = 2461 aSizingInput.mFrame->StylePosition()->GetSingleAutoInsetInAxis( 2462 aLogicalAxis, cbWM, anchorOffsetResolutionParams); 2463 2464 // Check if exactly one inset in the axis is auto 2465 // https://drafts.csswg.org/css-anchor-position/#position-area-alignment 2466 // "However, if only one inset property in the relevant axis is auto, the 2467 // default alignment is instead towards the edge with the non-auto inset; 2468 // and this is an unsafe alignment." 2469 if (singleAutoInset.isSome()) { 2470 const LogicalSide startSide = aLogicalAxis == LogicalAxis::Inline 2471 ? LogicalSide::IStart 2472 : LogicalSide::BStart; 2473 const mozilla::Side autoSide = *singleAutoInset; 2474 const mozilla::Side startPhysicalSide = cbWM.PhysicalSide(startSide); 2475 // Exactly one inset is auto - align toward the non-auto edge, unsafely 2476 alignment = (autoSide == startPhysicalSide) ? StyleAlignFlags::END 2477 : StyleAlignFlags::START; 2478 alignment |= StyleAlignFlags::UNSAFE; 2479 } else { 2480 // Use default position-area self-alignment: 2481 // https://drafts.csswg.org/css-anchor-position-1/#position-area-alignment 2482 const auto axis = ToStyleLogicalAxis(aLogicalAxis); 2483 const auto cbSWM = cbWM.ToStyleWritingMode(); 2484 const auto selfWM = 2485 aSizingInput.mFrame->GetWritingMode().ToStyleWritingMode(); 2486 Servo_ResolvePositionAreaSelfAlignment(&aResolvedPositionArea, axis, 2487 &cbSWM, &selfWM, &alignment); 2488 } 2489 } 2490 2491 return CSSAlignUtils::UsedAlignmentForAbsPos(aSizingInput.mFrame, alignment, 2492 aLogicalAxis, GetWritingMode()); 2493 } 2494 2495 nsOverflowContinuationTracker::nsOverflowContinuationTracker( 2496 nsContainerFrame* aFrame, bool aWalkOOFFrames, 2497 bool aSkipOverflowContainerChildren) 2498 : mOverflowContList(nullptr), 2499 mPrevOverflowCont(nullptr), 2500 mSentry(nullptr), 2501 mParent(aFrame), 2502 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren), 2503 mWalkOOFFrames(aWalkOOFFrames) { 2504 MOZ_ASSERT(aFrame, "null frame pointer"); 2505 SetupOverflowContList(); 2506 } 2507 2508 void nsOverflowContinuationTracker::SetupOverflowContList() { 2509 MOZ_ASSERT(mParent, "null frame pointer"); 2510 MOZ_ASSERT(!mOverflowContList, "already have list"); 2511 nsContainerFrame* nif = 2512 static_cast<nsContainerFrame*>(mParent->GetNextInFlow()); 2513 if (nif) { 2514 mOverflowContList = nif->GetOverflowContainers(); 2515 if (mOverflowContList) { 2516 mParent = nif; 2517 SetUpListWalker(); 2518 } 2519 } 2520 if (!mOverflowContList) { 2521 mOverflowContList = mParent->GetExcessOverflowContainers(); 2522 if (mOverflowContList) { 2523 SetUpListWalker(); 2524 } 2525 } 2526 } 2527 2528 /** 2529 * Helper function to walk past overflow continuations whose prev-in-flow 2530 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly. 2531 */ 2532 void nsOverflowContinuationTracker::SetUpListWalker() { 2533 NS_ASSERTION(!mSentry && !mPrevOverflowCont, 2534 "forgot to reset mSentry or mPrevOverflowCont"); 2535 if (mOverflowContList) { 2536 nsIFrame* cur = mOverflowContList->FirstChild(); 2537 if (mSkipOverflowContainerChildren) { 2538 while (cur && cur->GetPrevInFlow()->HasAnyStateBits( 2539 NS_FRAME_IS_OVERFLOW_CONTAINER)) { 2540 mPrevOverflowCont = cur; 2541 cur = cur->GetNextSibling(); 2542 } 2543 while (cur && 2544 (cur->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) != mWalkOOFFrames)) { 2545 mPrevOverflowCont = cur; 2546 cur = cur->GetNextSibling(); 2547 } 2548 } 2549 if (cur) { 2550 mSentry = cur->GetPrevInFlow(); 2551 } 2552 } 2553 } 2554 2555 /** 2556 * Helper function to step forward through the overflow continuations list. 2557 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames 2558 * as appropriate. May only be called when we have already set up an 2559 * mOverflowContList; mOverflowContList cannot be null. 2560 */ 2561 void nsOverflowContinuationTracker::StepForward() { 2562 MOZ_ASSERT(mOverflowContList, "null list"); 2563 2564 // Step forward 2565 if (mPrevOverflowCont) { 2566 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling(); 2567 } else { 2568 mPrevOverflowCont = mOverflowContList->FirstChild(); 2569 } 2570 2571 // Skip over oof or non-oof frames as appropriate 2572 if (mSkipOverflowContainerChildren) { 2573 nsIFrame* cur = mPrevOverflowCont->GetNextSibling(); 2574 while (cur && 2575 (cur->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) != mWalkOOFFrames)) { 2576 mPrevOverflowCont = cur; 2577 cur = cur->GetNextSibling(); 2578 } 2579 } 2580 2581 // Set up the sentry 2582 mSentry = (mPrevOverflowCont->GetNextSibling()) 2583 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow() 2584 : nullptr; 2585 } 2586 2587 nsresult nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont, 2588 nsReflowStatus& aReflowStatus) { 2589 MOZ_ASSERT(aOverflowCont, "null frame pointer"); 2590 MOZ_ASSERT(!mSkipOverflowContainerChildren || 2591 mWalkOOFFrames == 2592 aOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), 2593 "shouldn't insert frame that doesn't match walker type"); 2594 MOZ_ASSERT(aOverflowCont->GetPrevInFlow(), 2595 "overflow containers must have a prev-in-flow"); 2596 2597 nsresult rv = NS_OK; 2598 bool reparented = false; 2599 nsPresContext* presContext = aOverflowCont->PresContext(); 2600 bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow(); 2601 2602 // If we have a list and aOverflowCont is already in it then don't try to 2603 // add it again. 2604 if (addToList && aOverflowCont->GetParent() == mParent && 2605 aOverflowCont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) && 2606 mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) { 2607 addToList = false; 2608 mPrevOverflowCont = aOverflowCont->GetPrevSibling(); 2609 } 2610 2611 if (addToList) { 2612 if (aOverflowCont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) { 2613 // aOverflowCont is in some other overflow container list, 2614 // steal it first 2615 NS_ASSERTION(!(mOverflowContList && 2616 mOverflowContList->ContainsFrame(aOverflowCont)), 2617 "overflow containers out of order"); 2618 aOverflowCont->GetParent()->StealFrame(aOverflowCont); 2619 } else { 2620 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 2621 } 2622 if (!mOverflowContList) { 2623 // Note: We don't use SetExcessOverflowContainers() since it requires 2624 // setting a non-empty list. It's OK to manually set an empty list to 2625 // ExcessOverflowContainersProperty() because we are going to insert 2626 // aOverflowCont to mOverflowContList below, which guarantees an nonempty 2627 // list in ExcessOverflowContainersProperty(). 2628 mOverflowContList = new (presContext->PresShell()) nsFrameList(); 2629 mParent->SetProperty(nsContainerFrame::ExcessOverflowContainersProperty(), 2630 mOverflowContList); 2631 SetUpListWalker(); 2632 } 2633 if (aOverflowCont->GetParent() != mParent) { 2634 reparented = true; 2635 } 2636 2637 // If aOverflowCont has a prev/next-in-flow that might be in 2638 // mOverflowContList we need to find it and insert after/before it to 2639 // maintain the order amongst next-in-flows in this list. 2640 nsIFrame* pif = aOverflowCont->GetPrevInFlow(); 2641 nsIFrame* nif = aOverflowCont->GetNextInFlow(); 2642 if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) || 2643 (nif && nif->GetParent() == mParent && mPrevOverflowCont)) { 2644 for (nsIFrame* f : *mOverflowContList) { 2645 if (f == pif) { 2646 mPrevOverflowCont = pif; 2647 break; 2648 } 2649 if (f == nif) { 2650 mPrevOverflowCont = f->GetPrevSibling(); 2651 break; 2652 } 2653 } 2654 } 2655 2656 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont); 2657 aReflowStatus.SetNextInFlowNeedsReflow(); 2658 } 2659 2660 // If we need to reflow it, mark it dirty 2661 if (aReflowStatus.NextInFlowNeedsReflow()) { 2662 aOverflowCont->MarkSubtreeDirty(); 2663 } 2664 2665 // It's in our list, just step forward 2666 StepForward(); 2667 NS_ASSERTION(mPrevOverflowCont == aOverflowCont || 2668 (mSkipOverflowContainerChildren && 2669 mPrevOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) != 2670 aOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)), 2671 "OverflowContTracker in unexpected state"); 2672 2673 if (addToList) { 2674 // Convert all non-overflow-container next-in-flows of aOverflowCont 2675 // into overflow containers and move them to our overflow 2676 // tracker. This preserves the invariant that the next-in-flows 2677 // of an overflow container are also overflow containers. 2678 nsIFrame* f = aOverflowCont->GetNextInFlow(); 2679 if (f && (!f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) || 2680 (!reparented && f->GetParent() == mParent) || 2681 (reparented && f->GetParent() != mParent))) { 2682 if (!f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) { 2683 f->GetParent()->StealFrame(f); 2684 } 2685 Insert(f, aReflowStatus); 2686 } 2687 } 2688 return rv; 2689 } 2690 2691 void nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) { 2692 MOZ_ASSERT(aChild, "null ptr"); 2693 MOZ_ASSERT(aChild->GetNextInFlow(), 2694 "supposed to call Finish *before* deleting next-in-flow!"); 2695 2696 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) { 2697 // We'll update these in EndFinish after the next-in-flows are gone. 2698 if (f == mPrevOverflowCont) { 2699 mSentry = nullptr; 2700 mPrevOverflowCont = nullptr; 2701 break; 2702 } 2703 if (f == mSentry) { 2704 mSentry = nullptr; 2705 break; 2706 } 2707 } 2708 } 2709 2710 void nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) { 2711 if (!mOverflowContList) { 2712 return; 2713 } 2714 // Forget mOverflowContList if it was deleted. 2715 nsFrameList* eoc = mParent->GetExcessOverflowContainers(); 2716 if (eoc != mOverflowContList) { 2717 nsFrameList* oc = mParent->GetOverflowContainers(); 2718 if (oc != mOverflowContList) { 2719 // mOverflowContList was deleted 2720 mPrevOverflowCont = nullptr; 2721 mSentry = nullptr; 2722 mParent = aChild->GetParent(); 2723 mOverflowContList = nullptr; 2724 SetupOverflowContList(); 2725 return; 2726 } 2727 } 2728 // The list survived, update mSentry if needed. 2729 if (!mSentry) { 2730 if (!mPrevOverflowCont) { 2731 SetUpListWalker(); 2732 } else { 2733 mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont); 2734 // step backward to make StepForward() use our current mPrevOverflowCont 2735 mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling(); 2736 StepForward(); 2737 } 2738 } 2739 } 2740 2741 RubyMetrics nsContainerFrame::RubyMetricsIncludingChildren( 2742 float aRubyMetricsFactor) const { 2743 mozilla::RubyMetrics result; 2744 WritingMode containerWM = GetWritingMode(); 2745 bool foundAnyFrames = false; 2746 for (const auto* f : mFrames) { 2747 WritingMode wm = f->GetWritingMode(); 2748 if (wm.IsOrthogonalTo(containerWM) || f->IsPlaceholderFrame()) { 2749 continue; 2750 } 2751 mozilla::RubyMetrics m = f->RubyMetrics(aRubyMetricsFactor); 2752 result.CombineWith(m); 2753 foundAnyFrames = true; 2754 } 2755 if (!foundAnyFrames) { 2756 result = nsIFrame::RubyMetrics(aRubyMetricsFactor); 2757 } 2758 return result; 2759 } 2760 2761 ///////////////////////////////////////////////////////////////////////////// 2762 // Debugging 2763 2764 #ifdef DEBUG 2765 void nsContainerFrame::SanityCheckChildListsBeforeReflow() const { 2766 MOZ_ASSERT(IsFlexOrGridContainer(), 2767 "Only Flex / Grid containers can call this!"); 2768 2769 const auto didPushItemsBit = IsFlexContainerFrame() 2770 ? NS_STATE_FLEX_DID_PUSH_ITEMS 2771 : NS_STATE_GRID_DID_PUSH_ITEMS; 2772 ChildListIDs absLists = { 2773 FrameChildListID::Absolute, FrameChildListID::PushedAbsolute, 2774 FrameChildListID::Fixed, FrameChildListID::OverflowContainers, 2775 FrameChildListID::ExcessOverflowContainers}; 2776 ChildListIDs itemLists = {FrameChildListID::Principal, 2777 FrameChildListID::Overflow}; 2778 for (const nsIFrame* f = this; f; f = f->GetNextInFlow()) { 2779 MOZ_ASSERT(!f->HasAnyStateBits(didPushItemsBit), 2780 "At start of reflow, we should've pulled items back from all " 2781 "NIFs and cleared the state bit stored in didPushItemsBit in " 2782 "the process."); 2783 for (const auto& [list, listID] : f->ChildLists()) { 2784 if (!itemLists.contains(listID)) { 2785 MOZ_ASSERT(absLists.contains(listID), 2786 "unexpected non-empty child list"); 2787 continue; 2788 } 2789 for (const auto* child : list) { 2790 MOZ_ASSERT(f == this || child->GetPrevInFlow(), 2791 "all pushed items must be pulled up before reflow"); 2792 } 2793 } 2794 } 2795 // If we have a prev-in-flow, each of its children's next-in-flow 2796 // should be one of our children or be null. 2797 const auto* pif = static_cast<nsContainerFrame*>(GetPrevInFlow()); 2798 if (pif) { 2799 const nsFrameList* oc = GetOverflowContainers(); 2800 const nsFrameList* eoc = GetExcessOverflowContainers(); 2801 const nsFrameList* pifEOC = pif->GetExcessOverflowContainers(); 2802 for (const nsIFrame* child : pif->PrincipalChildList()) { 2803 const nsIFrame* childNIF = child->GetNextInFlow(); 2804 MOZ_ASSERT(!childNIF || mFrames.ContainsFrame(childNIF) || 2805 (pifEOC && pifEOC->ContainsFrame(childNIF)) || 2806 (oc && oc->ContainsFrame(childNIF)) || 2807 (eoc && eoc->ContainsFrame(childNIF))); 2808 } 2809 } 2810 } 2811 2812 void nsContainerFrame::SetDidPushItemsBitIfNeeded(ChildListID aListID, 2813 nsIFrame* aOldFrame) { 2814 MOZ_ASSERT(IsFlexOrGridContainer(), 2815 "Only Flex / Grid containers can call this!"); 2816 2817 // Note that FrameChildListID::Principal doesn't mean aOldFrame must be on 2818 // that list. It can also be on FrameChildListID::Overflow, in which case it 2819 // might be a pushed item, and if it's the only pushed item our DID_PUSH_ITEMS 2820 // bit will lie. 2821 if (aListID == FrameChildListID::Principal && !aOldFrame->GetPrevInFlow()) { 2822 // Since the bit may lie, set the mDidPushItemsBitMayLie value to true for 2823 // ourself and for all our prev-in-flows. 2824 nsContainerFrame* frameThatMayLie = this; 2825 do { 2826 frameThatMayLie->mDidPushItemsBitMayLie = true; 2827 frameThatMayLie = 2828 static_cast<nsContainerFrame*>(frameThatMayLie->GetPrevInFlow()); 2829 } while (frameThatMayLie); 2830 } 2831 } 2832 #endif 2833 2834 #ifdef DEBUG_FRAME_DUMP 2835 void nsContainerFrame::List(FILE* out, const char* aPrefix, 2836 ListFlags aFlags) const { 2837 nsCString str; 2838 ListGeneric(str, aPrefix, aFlags); 2839 ExtraContainerFrameInfo(str, 2840 aFlags.contains(ListFlag::OnlyListDeterministicInfo)); 2841 2842 // Output the frame name and various fields. 2843 fprintf_stderr(out, "%s <\n", str.get()); 2844 2845 const nsCString pfx = nsCString(aPrefix) + " "_ns; 2846 2847 // Output principal child list separately since we want to omit its 2848 // name and address. 2849 for (nsIFrame* kid : PrincipalChildList()) { 2850 kid->List(out, pfx.get(), aFlags); 2851 } 2852 2853 // Output rest of the child lists. 2854 const ChildListIDs skippedListIDs = {FrameChildListID::Principal}; 2855 ListChildLists(out, pfx.get(), aFlags, skippedListIDs); 2856 2857 fprintf_stderr(out, "%s>\n", aPrefix); 2858 } 2859 2860 void nsContainerFrame::ListWithMatchedRules(FILE* out, 2861 const char* aPrefix) const { 2862 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get()); 2863 2864 nsCString rulePrefix; 2865 rulePrefix += aPrefix; 2866 rulePrefix += " "; 2867 ListMatchedRules(out, rulePrefix.get()); 2868 2869 nsCString childPrefix; 2870 childPrefix += aPrefix; 2871 childPrefix += " "; 2872 2873 for (const auto& childList : ChildLists()) { 2874 for (const nsIFrame* kid : childList.mList) { 2875 kid->ListWithMatchedRules(out, childPrefix.get()); 2876 } 2877 } 2878 } 2879 2880 void nsContainerFrame::ListChildLists(FILE* aOut, const char* aPrefix, 2881 ListFlags aFlags, 2882 ChildListIDs aSkippedListIDs) const { 2883 const nsCString nestedPfx = nsCString(aPrefix) + " "_ns; 2884 2885 for (const auto& [list, listID] : ChildLists()) { 2886 if (aSkippedListIDs.contains(listID)) { 2887 continue; 2888 } 2889 2890 // Use nsPrintfCString so that %p don't output prefix "0x". This is 2891 // consistent with nsIFrame::ListTag(). 2892 nsCString str{nsPrintfCString("%s%s", aPrefix, ChildListName(listID))}; 2893 ListPtr(str, aFlags, &GetChildList(listID), "@"); 2894 str += " <\n"; 2895 fprintf_stderr(aOut, "%s", str.get()); 2896 2897 for (nsIFrame* kid : list) { 2898 // Verify the child frame's parent frame pointer is correct. 2899 NS_ASSERTION(kid->GetParent() == this, "Bad parent frame pointer!"); 2900 kid->List(aOut, nestedPfx.get(), aFlags); 2901 } 2902 fprintf_stderr(aOut, "%s>\n", aPrefix); 2903 } 2904 } 2905 2906 void nsContainerFrame::ExtraContainerFrameInfo(nsACString&, bool) const {} 2907 2908 #endif