ReflowInput.cpp (130145B)
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 /* struct containing the input to nsIFrame::Reflow */ 8 9 #include "mozilla/ReflowInput.h" 10 11 #include <algorithm> 12 13 #include "AnchorPositioningUtils.h" 14 #include "CounterStyleManager.h" 15 #include "LayoutLogging.h" 16 #include "PresShell.h" 17 #include "StickyScrollContainer.h" 18 #include "mozilla/ScrollContainerFrame.h" 19 #include "mozilla/WritingModes.h" 20 #include "mozilla/dom/HTMLInputElement.h" 21 #include "nsBlockFrame.h" 22 #include "nsFlexContainerFrame.h" 23 #include "nsFontInflationData.h" 24 #include "nsFontMetrics.h" 25 #include "nsGkAtoms.h" 26 #include "nsGridContainerFrame.h" 27 #include "nsIContent.h" 28 #include "nsIFrame.h" 29 #include "nsIFrameInlines.h" 30 #include "nsIPercentBSizeObserver.h" 31 #include "nsImageFrame.h" 32 #include "nsLayoutUtils.h" 33 #include "nsLineBox.h" 34 #include "nsPresContext.h" 35 #include "nsStyleConsts.h" 36 #include "nsTableFrame.h" 37 38 using namespace mozilla; 39 using namespace mozilla::css; 40 using namespace mozilla::dom; 41 using namespace mozilla::layout; 42 43 AnchorPosResolutionParams AnchorPosResolutionParams::From( 44 const mozilla::SizeComputationInput* aSizingInput, 45 bool aIgnorePositionArea) { 46 auto override = AutoResolutionOverrideParams{ 47 aSizingInput->mFrame, aSizingInput->mAnchorPosResolutionCache}; 48 if (aIgnorePositionArea) { 49 override.mPositionAreaInUse = false; 50 } 51 return {aSizingInput->mFrame, aSizingInput->mFrame->StyleDisplay()->mPosition, 52 aSizingInput->mAnchorPosResolutionCache, override}; 53 } 54 55 static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) { 56 nsIFrame* frameNext = aFrame->GetNextInFlow(); 57 nsIFrame* parentNext = aParent->GetNextInFlow(); 58 return frameNext && parentNext && frameNext->GetParent() == parentNext; 59 } 60 61 /** 62 * Adjusts the margin for a list (ol, ul), if necessary, depending on 63 * font inflation settings. Unfortunately, because bullets from a list are 64 * placed in the margin area, we only have ~40px in which to place the 65 * bullets. When they are inflated, however, this causes problems, since 66 * the text takes up more space than is available in the margin. 67 * 68 * This method will return a small amount (in app units) by which the 69 * margin can be adjusted, so that the space is available for list 70 * bullets to be rendered with font inflation enabled. 71 */ 72 static nscoord FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) { 73 // As an optimization we check this block frame specific bit up front before 74 // we even check if the frame is a block frame. That's only valid so long as 75 // we also have the `IsBlockFrameOrSubclass()` call below. Calling that is 76 // expensive though, and we want to avoid it if we know `HasMarker()` would 77 // return false. 78 if (!aFrame->HasAnyStateBits(NS_BLOCK_HAS_MARKER)) { 79 return 0; 80 } 81 82 // On desktop font inflation is disabled, so this will always early exit 83 // quickly, but checking the frame state bit is still quicker then this call 84 // and very likely to early exit on its own so we check this second. 85 float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); 86 if (inflation <= 1.0f) { 87 return 0; 88 } 89 90 if (!aFrame->IsBlockFrameOrSubclass()) { 91 return 0; 92 } 93 94 // We only want to adjust the margins if we're dealing with an ordered list. 95 // We already checked this above. 96 MOZ_ASSERT(static_cast<const nsBlockFrame*>(aFrame)->HasMarker()); 97 98 const auto* list = aFrame->StyleList(); 99 if (list->mListStyleType.IsNone()) { 100 return 0; 101 } 102 103 // The HTML spec states that the default padding for ordered lists 104 // begins at 40px, indicating that we have 40px of space to place a 105 // bullet. When performing font inflation calculations, we add space 106 // equivalent to this, but simply inflated at the same amount as the 107 // text, in app units. 108 auto margin = nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); 109 if (!list->mListStyleType.IsName()) { 110 return margin; 111 } 112 113 nsAtom* type = list->mListStyleType.AsName().AsAtom(); 114 if (type != nsGkAtoms::disc && type != nsGkAtoms::circle && 115 type != nsGkAtoms::square && type != nsGkAtoms::disclosure_closed && 116 type != nsGkAtoms::disclosure_open) { 117 return margin; 118 } 119 120 return 0; 121 } 122 123 SizeComputationInput::SizeComputationInput( 124 nsIFrame* aFrame, gfxContext* aRenderingContext, 125 AnchorPosResolutionCache* aAnchorPosResolutionCache) 126 : mFrame(aFrame), 127 mRenderingContext(aRenderingContext), 128 mAnchorPosResolutionCache(aAnchorPosResolutionCache), 129 mWritingMode(aFrame->GetWritingMode()), 130 mIsThemed(aFrame->IsThemed()), 131 mComputedMargin(mWritingMode), 132 mComputedBorderPadding(mWritingMode), 133 mComputedPadding(mWritingMode) { 134 MOZ_ASSERT(mFrame); 135 } 136 137 SizeComputationInput::SizeComputationInput( 138 nsIFrame* aFrame, gfxContext* aRenderingContext, 139 WritingMode aContainingBlockWritingMode, nscoord aContainingBlockISize, 140 const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding) 141 : SizeComputationInput(aFrame, aRenderingContext) { 142 MOZ_ASSERT(!mFrame->IsTableColFrame()); 143 InitOffsets(aContainingBlockWritingMode, aContainingBlockISize, 144 mFrame->Type(), {}, aBorder, aPadding); 145 } 146 147 // Initialize a <b>root</b> reflow input with a rendering context to 148 // use for measuring things. 149 ReflowInput::ReflowInput(nsPresContext* aPresContext, nsIFrame* aFrame, 150 gfxContext* aRenderingContext, 151 const LogicalSize& aAvailableSpace, InitFlags aFlags) 152 : SizeComputationInput(aFrame, aRenderingContext), 153 mAvailableSize(aAvailableSpace) { 154 MOZ_ASSERT(aRenderingContext, "no rendering context"); 155 MOZ_ASSERT(aPresContext, "no pres context"); 156 MOZ_ASSERT(aFrame, "no frame"); 157 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); 158 159 if (aFlags.contains(InitFlag::DummyParentReflowInput)) { 160 mFlags.mDummyParentReflowInput = true; 161 } 162 if (aFlags.contains(InitFlag::StaticPosIsCBOrigin)) { 163 mFlags.mStaticPosIsCBOrigin = true; 164 } 165 166 if (!aFlags.contains(InitFlag::CallerWillInit)) { 167 Init(aPresContext); 168 } 169 // When we encounter a PageContent frame this will be set to true. 170 mFlags.mCanHaveClassABreakpoints = false; 171 } 172 173 static nsSize GetICBSize(const nsPresContext* aPresContext, 174 const nsIFrame* aFrame) { 175 if (!aPresContext->IsPaginated()) { 176 return aPresContext->GetVisibleArea().Size(); 177 } 178 for (const nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { 179 if (f->IsPageContentFrame()) { 180 return f->GetSize(); 181 } 182 } 183 return aPresContext->GetPageSize(); 184 } 185 186 // Initialize a reflow input for a child frame's reflow. Some state 187 // is copied from the parent reflow input; the remaining state is 188 // computed. 189 ReflowInput::ReflowInput(nsPresContext* aPresContext, 190 const ReflowInput& aParentReflowInput, 191 nsIFrame* aFrame, const LogicalSize& aAvailableSpace, 192 const Maybe<LogicalSize>& aContainingBlockSize, 193 InitFlags aFlags, 194 const StyleSizeOverrides& aSizeOverrides, 195 ComputeSizeFlags aComputeSizeFlags, 196 AnchorPosResolutionCache* aAnchorPosResolutionCache) 197 : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext, 198 aAnchorPosResolutionCache), 199 mParentReflowInput(&aParentReflowInput), 200 mFloatManager(aParentReflowInput.mFloatManager), 201 mLineLayout(mFrame->IsLineParticipant() ? aParentReflowInput.mLineLayout 202 : nullptr), 203 mBreakType(aParentReflowInput.mBreakType), 204 mPercentBSizeObserver( 205 (aParentReflowInput.mPercentBSizeObserver && 206 aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this)) 207 ? aParentReflowInput.mPercentBSizeObserver 208 : nullptr), 209 mFlags(aParentReflowInput.mFlags), 210 mStyleSizeOverrides(aSizeOverrides), 211 mComputeSizeFlags(aComputeSizeFlags), 212 mReflowDepth(aParentReflowInput.mReflowDepth + 1), 213 mAvailableSize(aAvailableSpace) { 214 MOZ_ASSERT(aPresContext, "no pres context"); 215 MOZ_ASSERT(aFrame, "no frame"); 216 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); 217 MOZ_ASSERT(!mFlags.mSpecialBSizeReflow || !aFrame->IsSubtreeDirty(), 218 "frame should be clean when getting special bsize reflow"); 219 220 if (mWritingMode.IsOrthogonalTo(mParentReflowInput->GetWritingMode())) { 221 // If the block establishes an orthogonal flow, set up its AvailableISize 222 // per https://drafts.csswg.org/css-writing-modes/#orthogonal-auto 223 224 auto GetISizeConstraint = [this](const nsIFrame* aFrame, 225 bool* aFixed = nullptr) -> nscoord { 226 nscoord limit = NS_UNCONSTRAINEDSIZE; 227 const auto* pos = aFrame->StylePosition(); 228 // Don't add to anchor resolution cache, since this function is called for 229 // other frames. 230 const auto anchorResolutionParams = 231 AnchorPosResolutionParams::From(aFrame); 232 if (auto size = nsLayoutUtils::GetAbsoluteSize( 233 *pos->ISize(mWritingMode, anchorResolutionParams))) { 234 limit = size.value(); 235 if (aFixed) { 236 *aFixed = true; 237 } 238 } else if (auto maxSize = nsLayoutUtils::GetAbsoluteSize( 239 *pos->MaxISize(mWritingMode, anchorResolutionParams))) { 240 limit = maxSize.value(); 241 } 242 if (limit != NS_UNCONSTRAINEDSIZE) { 243 if (auto minSize = nsLayoutUtils::GetAbsoluteSize( 244 *pos->MinISize(mWritingMode, anchorResolutionParams))) { 245 limit = std::max(limit, minSize.value()); 246 } 247 } 248 return limit; 249 }; 250 251 // See if the containing block has a fixed size we should respect: 252 const nsIFrame* cb = mFrame->GetContainingBlock(); 253 bool isFixed = false; 254 nscoord cbLimit = aContainingBlockSize 255 ? aContainingBlockSize->ISize(mWritingMode) 256 : NS_UNCONSTRAINEDSIZE; 257 if (cbLimit != NS_UNCONSTRAINEDSIZE) { 258 isFixed = true; 259 } else { 260 cbLimit = GetISizeConstraint(cb, &isFixed); 261 } 262 263 if (isFixed) { 264 SetAvailableISize(cbLimit); 265 } else { 266 // If the CB size wasn't fixed, we consider the nearest scroll container 267 // and the ICB. 268 269 nscoord scLimit = NS_UNCONSTRAINEDSIZE; 270 // If the containing block was not a scroll container itself, look up the 271 // parent chain for a scroller size that we should respect. 272 // XXX Could maybe use nsLayoutUtils::GetNearestScrollContainerFrame here, 273 // but unsure if we need the additional complexity it supports? 274 if (!cb->IsScrollContainerFrame()) { 275 for (const nsIFrame* p = mFrame->GetParent(); p; p = p->GetParent()) { 276 if (p->IsScrollContainerFrame()) { 277 scLimit = GetISizeConstraint(p); 278 // Only the closest ancestor scroller is relevant, so quit as soon 279 // as we've found one (whether or not it had fixed sizing). 280 break; 281 } 282 } 283 } 284 285 LogicalSize icbSize(mWritingMode, GetICBSize(aPresContext, mFrame)); 286 nscoord icbLimit = icbSize.ISize(mWritingMode); 287 288 SetAvailableISize(std::min(icbLimit, std::min(scLimit, cbLimit))); 289 290 // Record that this frame needs to be invalidated on a resize reflow. 291 mFrame->PresShell()->AddOrthogonalFlow(mFrame); 292 } 293 } 294 295 // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in 296 // this constructor's init list, so the only flags that we need to explicitly 297 // initialize here are those that may need a value other than our parent's. 298 mFlags.mNextInFlowUntouched = 299 aParentReflowInput.mFlags.mNextInFlowUntouched && 300 CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame); 301 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; 302 mFlags.mIsColumnBalancing = false; 303 mFlags.mColumnSetWrapperHasNoBSizeLeft = false; 304 mFlags.mTreatBSizeAsIndefinite = false; 305 mFlags.mDummyParentReflowInput = false; 306 mFlags.mStaticPosIsCBOrigin = aFlags.contains(InitFlag::StaticPosIsCBOrigin); 307 mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false; 308 309 // We don't want the mOrthogonalCellFinalReflow flag to be inherited; it's up 310 // to the table row frame to set it for its direct children as needed. 311 mFlags.mOrthogonalCellFinalReflow = false; 312 313 // aPresContext->IsPaginated() and the named pages pref should have been 314 // checked when constructing the root ReflowInput. 315 if (aParentReflowInput.mFlags.mCanHaveClassABreakpoints) { 316 MOZ_ASSERT(aPresContext->IsPaginated(), 317 "mCanHaveClassABreakpoints set during non-paginated reflow."); 318 } 319 320 { 321 switch (mFrame->Type()) { 322 case LayoutFrameType::PageContent: 323 // PageContent requires paginated reflow. 324 MOZ_ASSERT(aPresContext->IsPaginated(), 325 "nsPageContentFrame should not be in non-paginated reflow"); 326 MOZ_ASSERT(!mFlags.mCanHaveClassABreakpoints, 327 "mFlags.mCanHaveClassABreakpoints should have been " 328 "initalized to false before we found nsPageContentFrame"); 329 mFlags.mCanHaveClassABreakpoints = true; 330 break; 331 case LayoutFrameType::Block: // FALLTHROUGH 332 case LayoutFrameType::Canvas: // FALLTHROUGH 333 case LayoutFrameType::FlexContainer: // FALLTHROUGH 334 case LayoutFrameType::GridContainer: 335 if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 336 // Never allow breakpoints inside of out-of-flow frames. 337 mFlags.mCanHaveClassABreakpoints = false; 338 break; 339 } 340 // This frame type can have class A breakpoints, inherit this flag 341 // from the parent (this is done for all flags during construction). 342 // This also includes Canvas frames, as each PageContent frame always 343 // has exactly one child which is a Canvas frame. 344 // Do NOT include the subclasses of BlockFrame here, as the ones for 345 // which this could be applicable (ColumnSetWrapper and the MathML 346 // frames) cannot have class A breakpoints. 347 MOZ_ASSERT(mFlags.mCanHaveClassABreakpoints == 348 aParentReflowInput.mFlags.mCanHaveClassABreakpoints); 349 break; 350 default: 351 mFlags.mCanHaveClassABreakpoints = false; 352 break; 353 } 354 } 355 356 if (aFlags.contains(InitFlag::DummyParentReflowInput) || 357 (mParentReflowInput->mFlags.mDummyParentReflowInput && 358 mFrame->IsTableFrame())) { 359 mFlags.mDummyParentReflowInput = true; 360 } 361 362 if (!aFlags.contains(InitFlag::CallerWillInit)) { 363 Init(aPresContext, aContainingBlockSize); 364 } 365 } 366 367 template <typename SizeOrMaxSize> 368 nscoord SizeComputationInput::ComputeISizeValue( 369 const LogicalSize& aContainingBlockSize, StyleBoxSizing aBoxSizing, 370 const SizeOrMaxSize& aSize) const { 371 WritingMode wm = GetWritingMode(); 372 const auto borderPadding = ComputedLogicalBorderPadding(wm); 373 const auto margin = ComputedLogicalMargin(wm); 374 const LogicalSize contentEdgeToBoxSizing = 375 aBoxSizing == StyleBoxSizing::Border ? borderPadding.Size(wm) 376 : LogicalSize(wm); 377 const nscoord boxSizingToMarginEdgeISize = borderPadding.IStartEnd(wm) + 378 margin.IStartEnd(wm) - 379 contentEdgeToBoxSizing.ISize(wm); 380 381 // Get the bSize with anchor functions resolved, and manually resolve 382 // 'stretch'-like sizes as well (we should do this a bit cleaner, 383 // see bug 2000035): 384 auto bSize = mFrame->StylePosition()->BSize( 385 wm, AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache)); 386 if (bSize->BehavesLikeStretchOnBlockAxis()) { 387 if (NS_UNCONSTRAINEDSIZE == aContainingBlockSize.BSize(wm)) { 388 bSize = AnchorResolvedSizeHelper::Auto(); 389 } else { 390 nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize( 391 aContainingBlockSize.BSize(wm), margin.BStartEnd(wm), 392 borderPadding.BStartEnd(wm), aBoxSizing); 393 bSize = AnchorResolvedSizeHelper::LengthPercentage( 394 StyleLengthPercentage::FromAppUnits(stretchBSize)); 395 } 396 } 397 398 return mFrame 399 ->ComputeISizeValue(mRenderingContext, wm, aContainingBlockSize, 400 contentEdgeToBoxSizing, boxSizingToMarginEdgeISize, 401 aSize, *bSize, mFrame->GetAspectRatio()) 402 .mISize; 403 } 404 405 template <typename SizeOrMaxSize> 406 nscoord SizeComputationInput::ComputeBSizeValueHandlingStretch( 407 nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing, 408 const SizeOrMaxSize& aSize) const { 409 if (aSize.BehavesLikeStretchOnBlockAxis()) { 410 WritingMode wm = GetWritingMode(); 411 return nsLayoutUtils::ComputeStretchContentBoxBSize( 412 aContainingBlockBSize, ComputedLogicalMargin(wm).Size(wm).BSize(wm), 413 ComputedLogicalBorderPadding(wm).Size(wm).BSize(wm)); 414 } 415 return ComputeBSizeValue(aContainingBlockBSize, aBoxSizing, 416 aSize.AsLengthPercentage()); 417 } 418 419 nscoord SizeComputationInput::ComputeBSizeValue( 420 nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing, 421 const LengthPercentage& aSize) const { 422 WritingMode wm = GetWritingMode(); 423 nscoord inside = 0; 424 if (aBoxSizing == StyleBoxSizing::Border) { 425 inside = ComputedLogicalBorderPadding(wm).BStartEnd(wm); 426 } 427 return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, inside, aSize); 428 } 429 430 WritingMode ReflowInput::GetCBWritingMode() const { 431 return mCBReflowInput ? mCBReflowInput->GetWritingMode() 432 : mFrame->GetContainingBlock()->GetWritingMode(); 433 } 434 435 // Return the physical border-box size by combining aContentBoxSize and 436 // aBorderPadding, with unconstrained dimensions replaced by zero. 437 static nsSize BorderBoxSizeAsContainerIfConstrained( 438 WritingMode aWM, const LogicalSize& aContentBoxSize, 439 const LogicalMargin& aBorderPadding) { 440 LogicalSize size = aContentBoxSize; 441 if (size.ISize(aWM) == NS_UNCONSTRAINEDSIZE) { 442 size.ISize(aWM) = 0; 443 } else { 444 size.ISize(aWM) += aBorderPadding.IStartEnd(aWM); 445 } 446 if (size.BSize(aWM) == NS_UNCONSTRAINEDSIZE) { 447 size.BSize(aWM) = 0; 448 } else { 449 size.BSize(aWM) += aBorderPadding.BStartEnd(aWM); 450 } 451 return size.GetPhysicalSize(aWM); 452 } 453 454 nsSize ReflowInput::ComputedSizeAsContainerIfConstrained() const { 455 return BorderBoxSizeAsContainerIfConstrained(mWritingMode, ComputedSize(), 456 mComputedBorderPadding); 457 } 458 459 bool ReflowInput::ShouldReflowAllKids() const { 460 // Note that we could make a stronger optimization for IsBResize if 461 // we use it in a ShouldReflowChild test that replaces the current 462 // checks of NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN, if it 463 // were tested there along with NS_FRAME_CONTAINS_RELATIVE_BSIZE. 464 // This would need to be combined with a slight change in which 465 // frames NS_FRAME_CONTAINS_RELATIVE_BSIZE is marked on. 466 return mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsIResize() || 467 (IsBResize() && 468 mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) || 469 mFlags.mIsInLastColumnBalancingReflow; 470 } 471 472 void ReflowInput::SetComputedISize(nscoord aComputedISize, 473 ResetResizeFlags aFlags) { 474 // It'd be nice to assert that |frame| is not in reflow, but this fails 475 // because viewport frames reset the computed isize on a copy of their reflow 476 // input when reflowing fixed-pos kids. In that case we actually don't want 477 // to mess with the resize flags, because comparing the frame's rect to the 478 // munged computed isize is pointless. 479 NS_WARNING_ASSERTION(aComputedISize >= 0, "Invalid computed inline-size!"); 480 if (ComputedISize() != aComputedISize) { 481 mComputedSize.ISize(mWritingMode) = std::max(0, aComputedISize); 482 if (aFlags == ResetResizeFlags::Yes) { 483 InitResizeFlags(mFrame->PresContext(), mFrame->Type()); 484 } 485 } 486 } 487 488 void ReflowInput::SetComputedBSize(nscoord aComputedBSize, 489 ResetResizeFlags aFlags) { 490 // It'd be nice to assert that |frame| is not in reflow, but this fails 491 // for the same reason as above. 492 NS_WARNING_ASSERTION(aComputedBSize >= 0, "Invalid computed block-size!"); 493 if (ComputedBSize() != aComputedBSize) { 494 mComputedSize.BSize(mWritingMode) = std::max(0, aComputedBSize); 495 if (aFlags == ResetResizeFlags::Yes) { 496 InitResizeFlags(mFrame->PresContext(), mFrame->Type()); 497 } 498 } 499 } 500 501 void ReflowInput::Init(nsPresContext* aPresContext, 502 const Maybe<LogicalSize>& aContainingBlockSize, 503 const Maybe<LogicalMargin>& aBorder, 504 const Maybe<LogicalMargin>& aPadding) { 505 LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE, 506 "have unconstrained inline-size; this should only " 507 "result from very large sizes, not attempts at " 508 "intrinsic inline-size calculation"); 509 510 mStylePosition = mFrame->StylePosition(); 511 mStyleDisplay = mFrame->StyleDisplay(); 512 mStyleBorder = mFrame->StyleBorder(); 513 mStyleMargin = mFrame->StyleMargin(); 514 515 InitCBReflowInput(); 516 517 LayoutFrameType type = mFrame->Type(); 518 if (type == LayoutFrameType::Placeholder) { 519 // Placeholders have a no-op Reflow method that doesn't need the rest of 520 // this initialization, so we bail out early. 521 mComputedSize.SizeTo(mWritingMode, 0, 0); 522 return; 523 } 524 525 mFlags.mIsReplaced = mFrame->IsReplaced(); 526 527 InitConstraints(aPresContext, aContainingBlockSize, aBorder, aPadding, type); 528 529 InitResizeFlags(aPresContext, type); 530 InitDynamicReflowRoot(); 531 532 nsIFrame* parent = mFrame->GetParent(); 533 if (parent && parent->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE) && 534 !(parent->IsScrollContainerFrame() && 535 parent->StyleDisplay()->mOverflowY != StyleOverflow::Hidden)) { 536 mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 537 } else if (type == LayoutFrameType::SVGForeignObject) { 538 // An SVG foreignObject frame is inherently constrained block-size. 539 mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 540 } else { 541 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 542 const auto bSizeCoord = 543 mStylePosition->BSize(mWritingMode, anchorResolutionParams); 544 const auto maxBSizeCoord = 545 mStylePosition->MaxBSize(mWritingMode, anchorResolutionParams); 546 if ((!bSizeCoord->BehavesLikeInitialValueOnBlockAxis() || 547 !maxBSizeCoord->BehavesLikeInitialValueOnBlockAxis()) && 548 // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements. 549 (mFrame->GetContent() && !(mFrame->GetContent()->IsAnyOfHTMLElements( 550 nsGkAtoms::body, nsGkAtoms::html)))) { 551 // If our block-size was specified as a percentage, then this could 552 // actually resolve to 'auto', based on: 553 // http://www.w3.org/TR/CSS21/visudet.html#the-height-property 554 nsIFrame* containingBlk = mFrame; 555 while (containingBlk) { 556 const nsStylePosition* stylePos = containingBlk->StylePosition(); 557 // It's for containing block, so don't add to anchor resolution cache 558 const auto containingBlkAnchorResolutionParams = 559 AnchorPosResolutionParams::From(containingBlk); 560 const auto bSizeCoord = 561 stylePos->BSize(mWritingMode, containingBlkAnchorResolutionParams); 562 const auto& maxBSizeCoord = stylePos->MaxBSize( 563 mWritingMode, containingBlkAnchorResolutionParams); 564 if ((bSizeCoord->IsLengthPercentage() && !bSizeCoord->HasPercent()) || 565 (maxBSizeCoord->IsLengthPercentage() && 566 !maxBSizeCoord->HasPercent())) { 567 mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 568 break; 569 } else if (bSizeCoord->HasPercent() || maxBSizeCoord->HasPercent()) { 570 if (!(containingBlk = containingBlk->GetContainingBlock())) { 571 // If we've reached the top of the tree, then we don't have 572 // a constrained block-size. 573 mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 574 break; 575 } 576 577 continue; 578 } else { 579 mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 580 break; 581 } 582 } 583 } else { 584 mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); 585 } 586 } 587 588 if (mParentReflowInput && 589 mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { 590 // Orthogonal frames are always reflowed with an unconstrained 591 // dimension to avoid incomplete reflow across an orthogonal 592 // boundary. Normally this is the block-size, but for column sets 593 // with auto-height it's the inline-size, so that they can add 594 // columns in the container's block direction 595 if (type == LayoutFrameType::ColumnSet && 596 mStylePosition 597 ->ISize(mWritingMode, AnchorPosResolutionParams::From(this)) 598 ->IsAuto()) { 599 SetComputedISize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No); 600 } else { 601 SetAvailableBSize(NS_UNCONSTRAINEDSIZE); 602 } 603 } 604 605 if (mFrame->GetContainSizeAxes().mBContained) { 606 // In the case that a box is size contained in block axis, we want to ensure 607 // that it is also monolithic. We do this by setting AvailableBSize() to an 608 // unconstrained size to avoid fragmentation. 609 SetAvailableBSize(NS_UNCONSTRAINEDSIZE); 610 } 611 612 LAYOUT_WARN_IF_FALSE( 613 (mStyleDisplay->IsInlineOutsideStyle() && !mFrame->IsReplaced()) || 614 type == LayoutFrameType::Text || 615 ComputedISize() != NS_UNCONSTRAINEDSIZE, 616 "have unconstrained inline-size; this should only " 617 "result from very large sizes, not attempts at " 618 "intrinsic inline-size calculation"); 619 } 620 621 static bool MightBeContainingBlockFor(nsIFrame* aMaybeContainingBlock, 622 nsIFrame* aFrame, 623 const nsStyleDisplay* aStyleDisplay) { 624 // Keep this in sync with nsIFrame::GetContainingBlock. 625 if (aFrame->IsAbsolutelyPositioned(aStyleDisplay) && 626 aMaybeContainingBlock == aFrame->GetParent()) { 627 return true; 628 } 629 return aMaybeContainingBlock->IsBlockContainer(); 630 } 631 632 void ReflowInput::InitCBReflowInput() { 633 mCBReflowInput = mParentReflowInput; 634 if (!mCBReflowInput || mParentReflowInput->mFlags.mDummyParentReflowInput) { 635 return; 636 } 637 // To avoid a long walk up the frame tree check if the parent frame can be a 638 // containing block for mFrame. 639 if (MightBeContainingBlockFor(mCBReflowInput->mFrame, mFrame, 640 mStyleDisplay) && 641 mCBReflowInput->mFrame == mFrame->GetContainingBlock(0, mStyleDisplay)) { 642 // Inner table frames need to use the containing block of the outer 643 // table frame. 644 if (mFrame->IsTableFrame()) { 645 MOZ_ASSERT(mParentReflowInput->mCBReflowInput, 646 "Inner table frames shouldn't be reflow roots"); 647 mCBReflowInput = mParentReflowInput->mCBReflowInput; 648 } 649 } else if (mParentReflowInput->mCBReflowInput) { 650 mCBReflowInput = mParentReflowInput->mCBReflowInput; 651 } 652 } 653 654 /* Check whether CalcQuirkContainingBlockHeight would stop on the 655 * given reflow input, using its block as a height. (essentially 656 * returns false for any case in which CalcQuirkContainingBlockHeight 657 * has a "continue" in its main loop.) 658 * 659 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses 660 * this function as well 661 */ 662 static bool IsQuirkContainingBlockHeight(const ReflowInput* rs, 663 LayoutFrameType aFrameType) { 664 if (LayoutFrameType::Block == aFrameType || 665 LayoutFrameType::ScrollContainer == aFrameType) { 666 // Note: This next condition could change due to a style change, 667 // but that would cause a style reflow anyway, which means we're ok. 668 if (NS_UNCONSTRAINEDSIZE == rs->ComputedHeight()) { 669 if (!rs->mFrame->IsAbsolutelyPositioned(rs->mStyleDisplay)) { 670 return false; 671 } 672 } 673 } 674 return true; 675 } 676 677 void ReflowInput::InitResizeFlags(nsPresContext* aPresContext, 678 LayoutFrameType aFrameType) { 679 SetIResize(false); 680 SetBResize(false); 681 SetBResizeForPercentages(false); 682 683 const WritingMode wm = mWritingMode; // just a shorthand 684 // We should report that we have a resize in the inline dimension if 685 // *either* the border-box size or the content-box size in that 686 // dimension has changed. It might not actually be necessary to do 687 // this if the border-box size has changed and the content-box size 688 // has not changed, but since we've historically used the flag to mean 689 // border-box size change, continue to do that. It's possible for 690 // the content-box size to change without a border-box size change or 691 // a style change given (1) a fixed width (possibly fixed by max-width 692 // or min-width), box-sizing:border-box, and percentage padding; 693 // (2) box-sizing:content-box, M% width, and calc(Npx - M%) padding. 694 // 695 // However, we don't actually have the information at this point to tell 696 // whether the content-box size has changed, since both style data and the 697 // UsedPaddingProperty() have already been updated in 698 // SizeComputationInput::InitOffsets(). So, we check the HasPaddingChange() 699 // bit for the cases where it's possible for the content-box size to have 700 // changed without either (a) a change in the border-box size or (b) an 701 // nsChangeHint_NeedDirtyReflow change hint due to change in border or 702 // padding. 703 // 704 // We don't clear the HasPaddingChange() bit here, since sometimes we 705 // construct reflow input (e.g. in nsBlockFrame::ReflowBlockFrame to compute 706 // margin collapsing) without reflowing the frame. Instead, we clear it in 707 // nsIFrame::DidReflow(). 708 bool isIResize = 709 // is the border-box resizing? 710 mFrame->ISize(wm) != 711 ComputedISize() + ComputedLogicalBorderPadding(wm).IStartEnd(wm) || 712 // or is the content-box resizing? (see comment above) 713 mFrame->HasPaddingChange(); 714 715 if (mFrame->HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT) && 716 nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { 717 // Create our font inflation data if we don't have it already, and 718 // give it our current width information. 719 bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) && 720 // Avoid running this at the box-to-block interface 721 // (where we shouldn't be inflating anyway, and where 722 // reflow input construction is probably to construct a 723 // dummy parent reflow input anyway). 724 !mFlags.mDummyParentReflowInput; 725 726 if (dirty || (!mFrame->GetParent() && isIResize)) { 727 // When font size inflation is enabled, a change in either: 728 // * the effective width of a font inflation flow root 729 // * the width of the frame 730 // needs to cause a dirty reflow since they change the font size 731 // inflation calculations, which in turn change the size of text, 732 // line-heights, etc. This is relatively similar to a classic 733 // case of style change reflow, except that because inflation 734 // doesn't affect the intrinsic sizing codepath, there's no need 735 // to invalidate intrinsic sizes. 736 // 737 // Note that this makes horizontal resizing a good bit more 738 // expensive. However, font size inflation is targeted at a set of 739 // devices (zoom-and-pan devices) where the main use case for 740 // horizontal resizing needing to be efficient (window resizing) is 741 // not present. It does still increase the cost of dynamic changes 742 // caused by script where a style or content change in one place 743 // causes a resize in another (e.g., rebalancing a table). 744 745 // FIXME: This isn't so great for the cases where 746 // ReflowInput::SetComputedWidth is called, if the first time 747 // we go through InitResizeFlags we set IsHResize() to true, and then 748 // the second time we'd set it to false even without the 749 // NS_FRAME_IS_DIRTY bit already set. 750 if (mFrame->IsSVGForeignObjectFrame()) { 751 // Foreign object frames use dirty bits in a special way. 752 mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 753 nsIFrame* kid = mFrame->PrincipalChildList().FirstChild(); 754 if (kid) { 755 kid->MarkSubtreeDirty(); 756 } 757 } else { 758 mFrame->MarkSubtreeDirty(); 759 } 760 761 // Mark intrinsic widths on all descendants dirty. We need to do 762 // this (1) since we're changing the size of text and need to 763 // clear text runs on text frames and (2) since we actually are 764 // changing some intrinsic widths, but only those that live inside 765 // of containers. 766 767 // It makes sense to do this for descendants but not ancestors 768 // (which is unusual) because we're only changing the unusual 769 // inflation-dependent intrinsic widths (i.e., ones computed with 770 // nsPresContext::mInflationDisabledForShrinkWrap set to false), 771 // which should never affect anything outside of their inflation 772 // flow root (or, for that matter, even their inflation 773 // container). 774 775 // This is also different from what PresShell::FrameNeedsReflow 776 // does because it doesn't go through placeholders. It doesn't 777 // need to because we're actually doing something that cares about 778 // frame tree geometry (the width on an ancestor) rather than 779 // style. 780 781 AutoTArray<nsIFrame*, 32> stack; 782 stack.AppendElement(mFrame); 783 784 do { 785 nsIFrame* f = stack.PopLastElement(); 786 for (const auto& childList : f->ChildLists()) { 787 for (nsIFrame* kid : childList.mList) { 788 kid->MarkIntrinsicISizesDirty(); 789 stack.AppendElement(kid); 790 } 791 } 792 } while (stack.Length() != 0); 793 } 794 } 795 796 SetIResize(!mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) && isIResize); 797 const auto anchorResolutionParams = 798 AnchorPosOffsetResolutionParams::UseCBFrameSize( 799 AnchorPosResolutionParams::From(this)); 800 801 const auto bSize = 802 mStylePosition->BSize(wm, anchorResolutionParams.mBaseParams); 803 const auto minBSize = 804 mStylePosition->MinBSize(wm, anchorResolutionParams.mBaseParams); 805 const auto maxBSize = 806 mStylePosition->MaxBSize(wm, anchorResolutionParams.mBaseParams); 807 // XXX Should we really need to null check mCBReflowInput? (We do for 808 // at least nsBoxFrame). 809 if (mFrame->HasBSizeChange()) { 810 // When we have an nsChangeHint_UpdateComputedBSize, we'll set a bit 811 // on the frame to indicate we're resizing. This might catch cases, 812 // such as a change between auto and a length, where the box doesn't 813 // actually resize but children with percentages resize (since those 814 // percentages become auto if their containing block is auto). 815 SetBResize(true); 816 SetBResizeForPercentages(true); 817 // We don't clear the HasBSizeChange state here, since sometimes we 818 // construct a ReflowInput (e.g. in nsBlockFrame::ReflowBlockFrame to 819 // compute margin collapsing) without reflowing the frame. Instead, we 820 // clear it in nsIFrame::DidReflow. 821 } else if (mCBReflowInput && 822 mCBReflowInput->IsBResizeForPercentagesForWM(wm) && 823 (bSize->HasPercent() || minBSize->HasPercent() || 824 maxBSize->HasPercent())) { 825 // We have a percentage (or calc-with-percentage) block-size, and the 826 // value it's relative to has changed. 827 SetBResize(true); 828 SetBResizeForPercentages(true); 829 } else if (aFrameType == LayoutFrameType::TableCell && 830 (mFlags.mSpecialBSizeReflow || 831 mFrame->FirstInFlow()->HasAnyStateBits( 832 NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && 833 mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { 834 // Need to set the bit on the cell so that 835 // mCBReflowInput->IsBResize() is set correctly below when 836 // reflowing descendant. 837 SetBResize(true); 838 SetBResizeForPercentages(true); 839 } else if (mCBReflowInput && mFrame->IsBlockWrapper()) { 840 // XXX Is this problematic for relatively positioned inlines acting 841 // as containing block for absolutely positioned elements? 842 // Possibly; in that case we should at least be checking 843 // IsSubtreeDirty(), I'd think. 844 SetBResize(mCBReflowInput->IsBResizeForWM(wm)); 845 SetBResizeForPercentages(mCBReflowInput->IsBResizeForPercentagesForWM(wm)); 846 } else if (ComputedBSize() == NS_UNCONSTRAINEDSIZE) { 847 // We have an 'auto' block-size. 848 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 849 mCBReflowInput) { 850 // FIXME: This should probably also check IsIResize(). 851 SetBResize(mCBReflowInput->IsBResizeForWM(wm)); 852 } else { 853 SetBResize(IsIResize()); 854 } 855 SetBResize(IsBResize() || mFrame->IsSubtreeDirty() || 856 // For an inner table frame, copy IsBResize from its wrapper. 857 (aFrameType == LayoutFrameType::Table && 858 mParentReflowInput->IsBResize())); 859 } else { 860 // We have a non-'auto' block-size, i.e., a length. Set the BResize 861 // flag to whether the size is actually different. 862 SetBResize(mFrame->BSize(wm) != 863 ComputedBSize() + 864 ComputedLogicalBorderPadding(wm).BStartEnd(wm)); 865 } 866 867 bool dependsOnCBBSize = 868 (nsStylePosition::BSizeDependsOnContainer(bSize) && 869 // FIXME: condition this on not-abspos? 870 !bSize->IsAuto()) || 871 nsStylePosition::MinBSizeDependsOnContainer(minBSize) || 872 nsStylePosition::MaxBSizeDependsOnContainer(maxBSize) || 873 mStylePosition 874 ->GetAnchorResolvedInset(LogicalSide::BStart, wm, 875 anchorResolutionParams) 876 ->HasPercent() || 877 !mStylePosition 878 ->GetAnchorResolvedInset(LogicalSide::BEnd, wm, 879 anchorResolutionParams) 880 ->IsAuto() || 881 // We assume orthogonal flows depend on the containing-block's BSize, 882 // as that will commonly provide the available inline size. This is not 883 // always strictly needed, but orthogonal flows are rare enough that 884 // attempting to be more precise seems overly complex. 885 (mCBReflowInput && mCBReflowInput->GetWritingMode().IsOrthogonalTo(wm)); 886 887 // If mFrame is a flex item, and mFrame's block axis is the flex container's 888 // main axis (e.g. in a column-oriented flex container with same 889 // writing-mode), then its block-size depends on its CB size, if its 890 // flex-basis has a percentage. 891 if (mFrame->IsFlexItem() && 892 !nsFlexContainerFrame::IsItemInlineAxisMainAxis(mFrame)) { 893 const auto& flexBasis = mStylePosition->mFlexBasis; 894 dependsOnCBBSize |= (flexBasis.IsSize() && flexBasis.AsSize().HasPercent()); 895 } 896 897 if (mFrame->StyleFont()->mLineHeight.IsMozBlockHeight()) { 898 // line-height depends on block bsize 899 mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 900 // but only on containing blocks if this frame is not a suitable block 901 dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame); 902 } 903 904 // If we're the descendant of a table cell that performs special bsize 905 // reflows and we could be the child that requires them, always set 906 // the block-axis resize in case this is the first pass before the 907 // special bsize reflow. However, don't do this if it actually is 908 // the special bsize reflow, since in that case it will already be 909 // set correctly above if we need it set. 910 if (!IsBResize() && mCBReflowInput && 911 (mCBReflowInput->mFrame->IsTableCellFrame() || 912 mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) && 913 !mCBReflowInput->mFlags.mSpecialBSizeReflow && dependsOnCBBSize) { 914 SetBResize(true); 915 mFlags.mHeightDependsOnAncestorCell = true; 916 } 917 918 // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed. 919 920 // It would be nice to check that |ComputedBSize != NS_UNCONSTRAINEDSIZE| 921 // &&ed with the percentage bsize check. However, this doesn't get 922 // along with table special bsize reflows, since a special bsize 923 // reflow (a quirk that makes such percentage height work on children 924 // of table cells) can cause not just a single percentage height to 925 // become fixed, but an entire descendant chain of percentage height 926 // to become fixed. 927 if (dependsOnCBBSize && mCBReflowInput) { 928 const ReflowInput* rs = this; 929 bool hitCBReflowInput = false; 930 do { 931 rs = rs->mParentReflowInput; 932 if (!rs) { 933 break; 934 } 935 936 if (rs->mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { 937 break; // no need to go further 938 } 939 rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 940 941 // Keep track of whether we've hit the containing block, because 942 // we need to go at least that far. 943 if (rs == mCBReflowInput) { 944 hitCBReflowInput = true; 945 } 946 947 // XXX What about orthogonal flows? It doesn't make sense to 948 // keep propagating this bit across an orthogonal boundary, 949 // where the meaning of BSize changes. Bug 1175517. 950 } while (!hitCBReflowInput || 951 (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 952 !IsQuirkContainingBlockHeight(rs, rs->mFrame->Type()))); 953 // Note: We actually don't need to set the 954 // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases 955 // where we hit the early break statements in 956 // CalcQuirkContainingBlockHeight. But it doesn't hurt 957 // us to set the bit in these cases. 958 } 959 if (mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) { 960 // If we're reflowing everything, then we'll find out if we need 961 // to re-set this. 962 mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 963 } 964 } 965 966 void ReflowInput::InitDynamicReflowRoot() { 967 if (mFrame->CanBeDynamicReflowRoot()) { 968 mFrame->AddStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT); 969 } else { 970 mFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT); 971 } 972 } 973 974 bool ReflowInput::ShouldApplyAutomaticMinimumOnBlockAxis() const { 975 MOZ_ASSERT(!mFrame->HasReplacedSizing()); 976 return mFlags.mIsBSizeSetByAspectRatio && 977 !mStyleDisplay->IsScrollableOverflow() && 978 mStylePosition 979 ->MinBSize(GetWritingMode(), AnchorPosResolutionParams::From(this)) 980 ->IsAuto() && 981 !mFrame->GetContainSizeAxes().mBContained; 982 } 983 984 bool ReflowInput::IsInFragmentedContext() const { 985 // We consider mFrame with a prev-in-flow being in a fragmented context 986 // because nsColumnSetFrame can reflow its last column with an unconstrained 987 // available block-size. 988 return AvailableBSize() != NS_UNCONSTRAINEDSIZE || mFrame->GetPrevInFlow(); 989 } 990 991 /* static */ 992 LogicalMargin ReflowInput::ComputeRelativeOffsets(WritingMode aWM, 993 nsIFrame* aFrame, 994 const LogicalSize& aCBSize) { 995 // In relative positioning, anchor functions are always invalid; 996 // anchor-resolved insets should no longer contain any reference to anchor 997 // functions. 998 LogicalMargin offsets(aWM); 999 const nsStylePosition* position = aFrame->StylePosition(); 1000 const auto anchorResolutionParams = 1001 AnchorPosOffsetResolutionParams::UseCBFrameSize( 1002 AnchorPosResolutionParams::From(aFrame)); 1003 1004 // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart' 1005 // moves the boxes to the end of the line, and 'inlineEnd' moves the 1006 // boxes to the start of the line. The computed values are always: 1007 // inlineStart=-inlineEnd 1008 const auto inlineStart = position->GetAnchorResolvedInset( 1009 LogicalSide::IStart, aWM, anchorResolutionParams); 1010 const auto inlineEnd = position->GetAnchorResolvedInset( 1011 LogicalSide::IEnd, aWM, anchorResolutionParams); 1012 bool inlineStartIsAuto = inlineStart->IsAuto(); 1013 bool inlineEndIsAuto = inlineEnd->IsAuto(); 1014 1015 // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're 1016 // over-constrained and we ignore one of them 1017 if (!inlineStartIsAuto && !inlineEndIsAuto) { 1018 inlineEndIsAuto = true; 1019 } 1020 1021 if (inlineStartIsAuto) { 1022 if (inlineEndIsAuto) { 1023 // If both are 'auto' (their initial values), the computed values are 0 1024 offsets.IStart(aWM) = offsets.IEnd(aWM) = 0; 1025 } else { 1026 // 'inlineEnd' isn't being treated as 'auto' so compute its value 1027 offsets.IEnd(aWM) = inlineEnd->IsAuto() 1028 ? 0 1029 : nsLayoutUtils::ComputeCBDependentValue( 1030 aCBSize.ISize(aWM), inlineEnd); 1031 1032 // Computed value for 'inlineStart' is minus the value of 'inlineEnd' 1033 offsets.IStart(aWM) = -offsets.IEnd(aWM); 1034 } 1035 1036 } else { 1037 NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint"); 1038 1039 // 'InlineStart' isn't 'auto' so compute its value 1040 offsets.IStart(aWM) = 1041 nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(aWM), inlineStart); 1042 1043 // Computed value for 'inlineEnd' is minus the value of 'inlineStart' 1044 offsets.IEnd(aWM) = -offsets.IStart(aWM); 1045 } 1046 1047 // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart' 1048 // and 'blockEnd' properties move relatively positioned elements in 1049 // the block progression direction. They also must be each other's 1050 // negative 1051 const auto blockStart = position->GetAnchorResolvedInset( 1052 LogicalSide::BStart, aWM, anchorResolutionParams); 1053 const auto blockEnd = position->GetAnchorResolvedInset( 1054 LogicalSide::BEnd, aWM, anchorResolutionParams); 1055 bool blockStartIsAuto = blockStart->IsAuto(); 1056 bool blockEndIsAuto = blockEnd->IsAuto(); 1057 1058 // Check for percentage based values and a containing block block-size 1059 // that depends on the content block-size. Treat them like 'auto' 1060 if (NS_UNCONSTRAINEDSIZE == aCBSize.BSize(aWM)) { 1061 if (blockStart->HasPercent()) { 1062 blockStartIsAuto = true; 1063 } 1064 if (blockEnd->HasPercent()) { 1065 blockEndIsAuto = true; 1066 } 1067 } 1068 1069 // If neither is 'auto', 'block-end' is ignored 1070 if (!blockStartIsAuto && !blockEndIsAuto) { 1071 blockEndIsAuto = true; 1072 } 1073 1074 if (blockStartIsAuto) { 1075 if (blockEndIsAuto) { 1076 // If both are 'auto' (their initial values), the computed values are 0 1077 offsets.BStart(aWM) = offsets.BEnd(aWM) = 0; 1078 } else { 1079 // 'blockEnd' isn't being treated as 'auto' so compute its value 1080 offsets.BEnd(aWM) = blockEnd->IsAuto() 1081 ? 0 1082 : nsLayoutUtils::ComputeCBDependentValue( 1083 aCBSize.BSize(aWM), blockEnd); 1084 1085 // Computed value for 'blockStart' is minus the value of 'blockEnd' 1086 offsets.BStart(aWM) = -offsets.BEnd(aWM); 1087 } 1088 1089 } else { 1090 NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint"); 1091 1092 // 'blockStart' isn't 'auto' so compute its value 1093 offsets.BStart(aWM) = 1094 nsLayoutUtils::ComputeCBDependentValue(aCBSize.BSize(aWM), blockStart); 1095 1096 // Computed value for 'blockEnd' is minus the value of 'blockStart' 1097 offsets.BEnd(aWM) = -offsets.BStart(aWM); 1098 } 1099 1100 // Convert the offsets to physical coordinates and store them on the frame 1101 const nsMargin physicalOffsets = offsets.GetPhysicalMargin(aWM); 1102 if (nsMargin* prop = 1103 aFrame->GetProperty(nsIFrame::ComputedOffsetProperty())) { 1104 *prop = physicalOffsets; 1105 } else { 1106 aFrame->AddProperty(nsIFrame::ComputedOffsetProperty(), 1107 new nsMargin(physicalOffsets)); 1108 } 1109 1110 NS_ASSERTION(offsets.IStart(aWM) == -offsets.IEnd(aWM) && 1111 offsets.BStart(aWM) == -offsets.BEnd(aWM), 1112 "ComputeRelativeOffsets should return valid results!"); 1113 1114 return offsets; 1115 } 1116 1117 /* static */ 1118 void ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame, 1119 const nsMargin& aComputedOffsets, 1120 nsPoint* aPosition) { 1121 if (!aFrame->IsRelativelyOrStickyPositioned()) { 1122 NS_ASSERTION(!aFrame->HasProperty(nsIFrame::NormalPositionProperty()), 1123 "We assume that changing the 'position' property causes " 1124 "frame reconstruction. If that ever changes, this code " 1125 "should call " 1126 "aFrame->RemoveProperty(nsIFrame::NormalPositionProperty())"); 1127 return; 1128 } 1129 1130 // Store the normal position 1131 aFrame->SetProperty(nsIFrame::NormalPositionProperty(), *aPosition); 1132 1133 const nsStyleDisplay* display = aFrame->StyleDisplay(); 1134 if (StylePositionProperty::Relative == display->mPosition) { 1135 *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); 1136 } 1137 // For sticky positioned elements, we'll leave them until the scroll container 1138 // reflows and calls StickyScrollContainer::UpdatePositions() to update their 1139 // positions. 1140 } 1141 1142 // static 1143 void ReflowInput::ComputeAbsPosInlineAutoMargin(nscoord aAvailMarginSpace, 1144 WritingMode aContainingBlockWM, 1145 bool aIsMarginIStartAuto, 1146 bool aIsMarginIEndAuto, 1147 LogicalMargin& aMargin) { 1148 if (aIsMarginIStartAuto) { 1149 if (aIsMarginIEndAuto) { 1150 if (aAvailMarginSpace < 0) { 1151 // Note that this case is different from the neither-'auto' 1152 // case below, where the spec says to ignore 'left'/'right'. 1153 // Ignore the specified value for 'margin-right'. 1154 aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace; 1155 } else { 1156 // Both 'margin-left' and 'margin-right' are 'auto', so they get 1157 // equal values 1158 aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace / 2; 1159 aMargin.IEnd(aContainingBlockWM) = 1160 aAvailMarginSpace - aMargin.IStart(aContainingBlockWM); 1161 } 1162 } else { 1163 // Just 'margin-left' is 'auto' 1164 aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace; 1165 } 1166 } else { 1167 if (aIsMarginIEndAuto) { 1168 // Just 'margin-right' is 'auto' 1169 aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace; 1170 } 1171 // Else, both margins are non-auto. This margin box would align to the 1172 // inset-reduced containing block, so it's not overconstrained. 1173 } 1174 } 1175 1176 // static 1177 void ReflowInput::ComputeAbsPosBlockAutoMargin(nscoord aAvailMarginSpace, 1178 WritingMode aContainingBlockWM, 1179 bool aIsMarginBStartAuto, 1180 bool aIsMarginBEndAuto, 1181 LogicalMargin& aMargin) { 1182 if (aIsMarginBStartAuto) { 1183 if (aIsMarginBEndAuto) { 1184 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get 1185 // equal values 1186 aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace / 2; 1187 aMargin.BEnd(aContainingBlockWM) = 1188 aAvailMarginSpace - aMargin.BStart(aContainingBlockWM); 1189 } else { 1190 // Just margin-block-start is 'auto' 1191 aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace; 1192 } 1193 } else { 1194 if (aIsMarginBEndAuto) { 1195 // Just margin-block-end is 'auto' 1196 aMargin.BEnd(aContainingBlockWM) = aAvailMarginSpace; 1197 } 1198 // Else, both margins are non-auto. See comment in the inline version. 1199 } 1200 } 1201 1202 void ReflowInput::ApplyRelativePositioning( 1203 nsIFrame* aFrame, WritingMode aWritingMode, 1204 const LogicalMargin& aComputedOffsets, LogicalPoint* aPosition, 1205 const nsSize& aContainerSize) { 1206 // Subtract the size of the frame from the container size that we 1207 // use for converting between the logical and physical origins of 1208 // the frame. This accounts for the fact that logical origins in RTL 1209 // coordinate systems are at the top right of the frame instead of 1210 // the top left. 1211 nsSize frameSize = aFrame->GetSize(); 1212 nsPoint pos = 1213 aPosition->GetPhysicalPoint(aWritingMode, aContainerSize - frameSize); 1214 ApplyRelativePositioning( 1215 aFrame, aComputedOffsets.GetPhysicalMargin(aWritingMode), &pos); 1216 *aPosition = LogicalPoint(aWritingMode, pos, aContainerSize - frameSize); 1217 } 1218 1219 ReflowInput::HypotheticalBoxContainerInfo 1220 ReflowInput::GetHypotheticalBoxContainer(const nsIFrame* aFrame) const { 1221 nsIFrame* cb = aFrame->GetContainingBlock(); 1222 NS_ASSERTION(cb != mFrame, "How did that happen?"); 1223 1224 // If cb is currently being reflowed, find its ReflowInput. 1225 const ReflowInput* ri = nullptr; 1226 if (cb->HasAnyStateBits(NS_FRAME_IN_REFLOW)) { 1227 ri = mParentReflowInput; 1228 while (ri && ri->mFrame != cb) { 1229 ri = ri->mParentReflowInput; 1230 } 1231 } 1232 1233 const WritingMode wm = cb->GetWritingMode(); 1234 if (ri) { 1235 return {cb, ri->ComputedLogicalBorderPadding(wm), ri->ComputedSize(wm)}; 1236 } 1237 1238 // Didn't find a ReflowInput for cb. Just compute the information we want, on 1239 // the assumption that cb already knows its size. This really ought to be true 1240 // by now. 1241 NS_ASSERTION(!cb->HasAnyStateBits(NS_FRAME_IN_REFLOW), 1242 "cb shouldn't be in reflow; we'll lie if it is"); 1243 return {cb, cb->GetLogicalUsedBorderAndPadding(wm), cb->ContentSize(wm)}; 1244 } 1245 1246 struct nsHypotheticalPosition { 1247 // offset from inline-start edge of containing block (which is a padding edge) 1248 nscoord mIStart = 0; 1249 // offset from block-start edge of containing block (which is a padding edge) 1250 nscoord mBStart = 0; 1251 WritingMode mWritingMode; 1252 }; 1253 1254 /** 1255 * aInsideBoxSizing returns the part of the padding, border, and margin 1256 * in the aAxis dimension that goes inside the edge given by box-sizing; 1257 * aOutsideBoxSizing returns the rest. 1258 */ 1259 void ReflowInput::CalculateBorderPaddingMargin( 1260 LogicalAxis aAxis, nscoord aContainingBlockSize, nscoord* aInsideBoxSizing, 1261 nscoord* aOutsideBoxSizing) const { 1262 WritingMode wm = GetWritingMode(); 1263 Side startSide = wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::Start)); 1264 Side endSide = wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::End)); 1265 1266 nsMargin styleBorder = mStyleBorder->GetComputedBorder(); 1267 nscoord borderStartEnd = 1268 styleBorder.Side(startSide) + styleBorder.Side(endSide); 1269 1270 nscoord paddingStartEnd, marginStartEnd; 1271 1272 // See if the style system can provide us the padding directly 1273 const auto* stylePadding = mFrame->StylePadding(); 1274 if (nsMargin padding; stylePadding->GetPadding(padding)) { 1275 paddingStartEnd = padding.Side(startSide) + padding.Side(endSide); 1276 } else { 1277 // We have to compute the start and end values 1278 const nscoord start = nsLayoutUtils::ComputeCBDependentValue( 1279 aContainingBlockSize, stylePadding->mPadding.Get(startSide)); 1280 const nscoord end = nsLayoutUtils::ComputeCBDependentValue( 1281 aContainingBlockSize, stylePadding->mPadding.Get(endSide)); 1282 paddingStartEnd = start + end; 1283 } 1284 1285 // See if the style system can provide us the margin directly 1286 if (nsMargin margin; mStyleMargin->GetMargin(margin)) { 1287 marginStartEnd = margin.Side(startSide) + margin.Side(endSide); 1288 } else { 1289 // If the margin is 'auto', ComputeCBDependentValue() will return 0. The 1290 // correct margin value will be computed later in InitAbsoluteConstraints 1291 // (which is caller of this function, via CalculateHypotheticalPosition). 1292 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 1293 const nscoord start = nsLayoutUtils::ComputeCBDependentValue( 1294 aContainingBlockSize, 1295 mStyleMargin->GetMargin(startSide, anchorResolutionParams)); 1296 const nscoord end = nsLayoutUtils::ComputeCBDependentValue( 1297 aContainingBlockSize, 1298 mStyleMargin->GetMargin(endSide, anchorResolutionParams)); 1299 marginStartEnd = start + end; 1300 } 1301 1302 nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd; 1303 nscoord inside = 0; 1304 if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) { 1305 inside = borderStartEnd + paddingStartEnd; 1306 } 1307 outside -= inside; 1308 *aInsideBoxSizing = inside; 1309 *aOutsideBoxSizing = outside; 1310 } 1311 1312 /** 1313 * Returns true iff a pre-order traversal of the normal child 1314 * frames rooted at aFrame finds no non-empty frame before aDescendant. 1315 */ 1316 static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, 1317 nsIFrame* aDescendant, 1318 bool* aFound) { 1319 if (aFrame == aDescendant) { 1320 *aFound = true; 1321 return true; 1322 } 1323 if (aFrame->IsPlaceholderFrame()) { 1324 auto ph = static_cast<nsPlaceholderFrame*>(aFrame); 1325 MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty()); 1326 ph->SetLineIsEmptySoFar(true); 1327 } else { 1328 if (!aFrame->IsSelfEmpty()) { 1329 *aFound = false; 1330 return false; 1331 } 1332 for (nsIFrame* f : aFrame->PrincipalChildList()) { 1333 bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); 1334 if (*aFound || !allEmpty) { 1335 return allEmpty; 1336 } 1337 } 1338 } 1339 *aFound = false; 1340 return true; 1341 } 1342 1343 // In the code below, |aCBReflowInput->mFrame| is the absolute containing block, 1344 // while |blockContainer| is the nearest block container of the placeholder 1345 // frame, which may be different from the absolute containing block. 1346 void ReflowInput::CalculateHypotheticalPosition( 1347 nsPlaceholderFrame* aPlaceholderFrame, const ReflowInput* aCBReflowInput, 1348 const LogicalSize& aCBPaddingBoxSize, 1349 nsHypotheticalPosition& aHypotheticalPos) const { 1350 NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None, 1351 "mOriginalDisplay has not been properly initialized"); 1352 1353 WritingMode cbwm = aCBReflowInput->GetWritingMode(); 1354 const auto [blockContainer, blockContainerBP, blockContainerContentBoxSize] = 1355 GetHypotheticalBoxContainer(aPlaceholderFrame); 1356 WritingMode wm = blockContainer->GetWritingMode(); 1357 const nscoord blockContainerContentIStart = blockContainerBP.IStart(wm); 1358 1359 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 1360 const auto styleISize = mStylePosition->ISize(wm, anchorResolutionParams); 1361 bool isAutoISize = styleISize->IsAuto(); 1362 1363 // If it's a replaced element and it has a 'auto' value for 'inline size', see 1364 // if we can get the intrinsic size. This will allow us to exactly determine 1365 // both the inline edges. 1366 Maybe<nsSize> intrinsicSize; 1367 if (mFlags.mIsReplaced && isAutoISize) { 1368 intrinsicSize = mFrame->GetIntrinsicSize().ToSize(); 1369 } 1370 1371 // See if we can calculate what the box inline size would have been if 1372 // the element had been in the flow 1373 Maybe<nscoord> boxISize; 1374 if (mStyleDisplay->IsOriginalDisplayInlineOutside() && !mFlags.mIsReplaced) { 1375 // For non-replaced inline-level elements the 'inline size' property 1376 // doesn't apply, so we don't know what the inline size would have 1377 // been without reflowing it 1378 } else { 1379 // It's either a replaced inline-level element or a block-level element 1380 1381 // Determine the total amount of inline direction 1382 // border/padding/margin that the element would have had if it had 1383 // been in the flow. Note that we ignore any 'auto' and 'inherit' 1384 // values 1385 nscoord contentEdgeToBoxSizingISize, boxSizingToMarginEdgeISize; 1386 CalculateBorderPaddingMargin( 1387 LogicalAxis::Inline, blockContainerContentBoxSize.ISize(wm), 1388 &contentEdgeToBoxSizingISize, &boxSizingToMarginEdgeISize); 1389 1390 if (mFlags.mIsReplaced && isAutoISize) { 1391 // It's a replaced element with an 'auto' inline size so the box inline 1392 // size is its intrinsic size plus any border/padding/margin 1393 if (intrinsicSize) { 1394 boxISize.emplace(LogicalSize(wm, *intrinsicSize).ISize(wm) + 1395 contentEdgeToBoxSizingISize + 1396 boxSizingToMarginEdgeISize); 1397 } 1398 } else if (isAutoISize) { 1399 // The box inline size is the block container's inline size 1400 boxISize.emplace(blockContainerContentBoxSize.ISize(wm)); 1401 } else { 1402 // We need to compute it. It's important we do this, because if it's 1403 // percentage based this computed value may be different from the computed 1404 // value calculated using the absolute containing block width 1405 nscoord contentEdgeToBoxSizingBSize, dummy; 1406 CalculateBorderPaddingMargin(LogicalAxis::Block, 1407 blockContainerContentBoxSize.ISize(wm), 1408 &contentEdgeToBoxSizingBSize, &dummy); 1409 1410 const auto contentISize = 1411 mFrame 1412 ->ComputeISizeValue( 1413 mRenderingContext, wm, blockContainerContentBoxSize, 1414 LogicalSize(wm, contentEdgeToBoxSizingISize, 1415 contentEdgeToBoxSizingBSize), 1416 boxSizingToMarginEdgeISize, *styleISize, 1417 *mStylePosition->BSize(wm, anchorResolutionParams), 1418 mFrame->GetAspectRatio()) 1419 .mISize; 1420 boxISize.emplace(contentISize + contentEdgeToBoxSizingISize + 1421 boxSizingToMarginEdgeISize); 1422 } 1423 } 1424 1425 // Get the placeholder offset in the coordinate space of its block container. 1426 // XXXbz the placeholder is not fully reflowed yet if our containing block is 1427 // relatively positioned... 1428 const nsSize blockContainerSize = BorderBoxSizeAsContainerIfConstrained( 1429 wm, blockContainerContentBoxSize, blockContainerBP); 1430 LogicalPoint placeholderOffset( 1431 wm, aPlaceholderFrame->GetOffsetToIgnoringScrolling(blockContainer), 1432 blockContainerSize); 1433 1434 // First, determine the hypothetical box's mBStart. We want to check the 1435 // content insertion frame of blockContainer for block-ness, but make 1436 // sure to compute all coordinates in the coordinate system of 1437 // blockContainer. 1438 nsBlockFrame* blockFrame = 1439 do_QueryFrame(blockContainer->GetContentInsertionFrame()); 1440 if (blockFrame) { 1441 // Use a null containerSize to convert a LogicalPoint functioning as a 1442 // vector into a physical nsPoint vector. 1443 const nsSize nullContainerSize; 1444 LogicalPoint blockOffset( 1445 wm, blockFrame->GetOffsetToIgnoringScrolling(blockContainer), 1446 nullContainerSize); 1447 bool isValid; 1448 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); 1449 if (!isValid) { 1450 // Give up. We're probably dealing with somebody using 1451 // position:absolute inside native-anonymous content anyway. 1452 aHypotheticalPos.mBStart = placeholderOffset.B(wm); 1453 } else { 1454 NS_ASSERTION(iter.GetContainer() == blockFrame, 1455 "Found placeholder in wrong block!"); 1456 nsBlockFrame::LineIterator lineBox = iter.GetLine(); 1457 1458 // How we determine the hypothetical box depends on whether the element 1459 // would have been inline-level or block-level 1460 LogicalRect lineBounds = lineBox->GetBounds().ConvertTo( 1461 wm, lineBox->mWritingMode, lineBox->mContainerSize); 1462 if (mStyleDisplay->IsOriginalDisplayInlineOutside()) { 1463 // Use the block-start of the inline box which the placeholder lives in 1464 // as the hypothetical box's block-start. 1465 aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); 1466 } else { 1467 // The element would have been block-level which means it would 1468 // be below the line containing the placeholder frame, unless 1469 // all the frames before it are empty. In that case, it would 1470 // have been just before this line. 1471 // XXXbz the line box is not fully reflowed yet if our 1472 // containing block is relatively positioned... 1473 if (lineBox != iter.End()) { 1474 nsIFrame* firstFrame = lineBox->mFirstChild; 1475 bool allEmpty = false; 1476 if (firstFrame == aPlaceholderFrame) { 1477 aPlaceholderFrame->SetLineIsEmptySoFar(true); 1478 allEmpty = true; 1479 } else { 1480 auto* prev = aPlaceholderFrame->GetPrevSibling(); 1481 if (prev && prev->IsPlaceholderFrame()) { 1482 auto* ph = static_cast<nsPlaceholderFrame*>(prev); 1483 if (ph->GetLineIsEmptySoFar(&allEmpty)) { 1484 aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); 1485 } 1486 } 1487 } 1488 if (!allEmpty) { 1489 bool found = false; 1490 while (firstFrame) { // See bug 223064 1491 allEmpty = AreAllEarlierInFlowFramesEmpty( 1492 firstFrame, aPlaceholderFrame, &found); 1493 if (found || !allEmpty) { 1494 break; 1495 } 1496 firstFrame = firstFrame->GetNextSibling(); 1497 } 1498 aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); 1499 } 1500 NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); 1501 1502 if (allEmpty) { 1503 // The top of the hypothetical box is the top of the line 1504 // containing the placeholder, since there is nothing in the 1505 // line before our placeholder except empty frames. 1506 aHypotheticalPos.mBStart = 1507 lineBounds.BStart(wm) + blockOffset.B(wm); 1508 } else { 1509 // The top of the hypothetical box is just below the line 1510 // containing the placeholder. 1511 aHypotheticalPos.mBStart = lineBounds.BEnd(wm) + blockOffset.B(wm); 1512 } 1513 } else { 1514 // Just use the placeholder's block-offset wrt the containing block 1515 aHypotheticalPos.mBStart = placeholderOffset.B(wm); 1516 } 1517 } 1518 } 1519 } else { 1520 // blockContainer is not a block, so it's probably something like a XUL box, 1521 // etc. Just use the placeholder's block-offset 1522 aHypotheticalPos.mBStart = placeholderOffset.B(wm); 1523 } 1524 1525 // Second, determine the hypothetical box's mIStart. 1526 // How we determine the hypothetical box depends on whether the element 1527 // would have been inline-level or block-level 1528 if (mStyleDisplay->IsOriginalDisplayInlineOutside() || 1529 mFlags.mIOffsetsNeedCSSAlign) { 1530 // The placeholder represents the IStart edge of the hypothetical box. 1531 // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart 1532 // edge of the Alignment Container.) 1533 aHypotheticalPos.mIStart = placeholderOffset.I(wm); 1534 } else { 1535 aHypotheticalPos.mIStart = blockContainerContentIStart; 1536 } 1537 1538 // The current coordinate space is that of the nearest block to the 1539 // placeholder. Convert to the coordinate space of the absolute containing 1540 // block. 1541 const nsIFrame* cbFrame = aCBReflowInput->mFrame; 1542 nsPoint cbOffset = blockContainer->GetOffsetToIgnoringScrolling(cbFrame); 1543 nsSize cbSize; 1544 if (cbFrame->IsViewportFrame()) { 1545 // When the containing block is the ViewportFrame, i.e. we are calculating 1546 // the static position for a fixed-positioned frame, we need to adjust the 1547 // origin to exclude the scrollbar or scrollbar-gutter area. The 1548 // ViewportFrame's containing block rect is adjusted in 1549 // AbsoluteContainingBlock::ReflowAbsoluteFrame(), and it will add the 1550 // rect's origin to the fixed-positioned frame's final position if needed. 1551 // 1552 // Note: The origin of the containing block rect is adjusted in 1553 // ViewportFrame::AdjustReflowInputForScrollbars(). Ensure the code there 1554 // remains in sync with the logic here. 1555 if (ScrollContainerFrame* sf = 1556 do_QueryFrame(cbFrame->PrincipalChildList().FirstChild())) { 1557 const nsMargin scrollbarSizes = sf->GetActualScrollbarSizes(); 1558 cbOffset.MoveBy(-scrollbarSizes.left, -scrollbarSizes.top); 1559 } 1560 1561 // ViewportFrame has no border or padding, so the padding-box size is equal 1562 // to the border-box size (cbSize) that we are computing. 1563 cbSize = aCBPaddingBoxSize.GetPhysicalSize(cbwm); 1564 } else { 1565 cbSize = aCBReflowInput->ComputedSizeAsContainerIfConstrained(); 1566 } 1567 1568 LogicalPoint logCBOffs(wm, cbOffset, cbSize - blockContainerSize); 1569 aHypotheticalPos.mIStart += logCBOffs.I(wm); 1570 aHypotheticalPos.mBStart += logCBOffs.B(wm); 1571 1572 // If block direction doesn't match (whether orthogonal or antiparallel), 1573 // we'll have to convert aHypotheticalPos to be in terms of cbwm. 1574 // This upcoming conversion must be taken into account for border offsets. 1575 const bool hypotheticalPosWillUseCbwm = 1576 cbwm.GetBlockDir() != wm.GetBlockDir(); 1577 // The specified offsets are relative to the absolute containing block's 1578 // padding edge and our current values are relative to the border edge, so 1579 // translate. 1580 const LogicalMargin border = aCBReflowInput->ComputedLogicalBorder(wm); 1581 if (hypotheticalPosWillUseCbwm && 1582 !wm.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, cbwm)) { 1583 aHypotheticalPos.mIStart += border.IEnd(wm); 1584 } else { 1585 aHypotheticalPos.mIStart -= border.IStart(wm); 1586 } 1587 1588 if (hypotheticalPosWillUseCbwm && 1589 !wm.ParallelAxisStartsOnSameSide(LogicalAxis::Block, cbwm)) { 1590 aHypotheticalPos.mBStart += border.BEnd(wm); 1591 } else { 1592 aHypotheticalPos.mBStart -= border.BStart(wm); 1593 } 1594 // At this point, we have computed aHypotheticalPos using the writing mode 1595 // of the placeholder's block container. 1596 1597 if (hypotheticalPosWillUseCbwm) { 1598 // If the block direction we used in calculating aHypotheticalPos does not 1599 // match the absolute containing block's, we need to convert here so that 1600 // aHypotheticalPos is usable in relation to the absolute containing block. 1601 // This requires computing or measuring the abspos frame's block-size, 1602 // which is not otherwise required/used here (as aHypotheticalPos 1603 // records only the block-start coordinate). 1604 1605 // This is similar to the inline-size calculation for a replaced 1606 // inline-level element or a block-level element (above), except that 1607 // 'auto' sizing is handled differently in the block direction for non- 1608 // replaced elements and replaced elements lacking an intrinsic size. 1609 1610 // Determine the total amount of block direction 1611 // border/padding/margin that the element would have had if it had 1612 // been in the flow. Note that we ignore any 'auto' and 'inherit' 1613 // values. 1614 nscoord insideBoxSizing, outsideBoxSizing; 1615 CalculateBorderPaddingMargin(LogicalAxis::Block, 1616 blockContainerContentBoxSize.BSize(wm), 1617 &insideBoxSizing, &outsideBoxSizing); 1618 1619 nscoord boxBSize; 1620 const auto styleBSize = mStylePosition->BSize(wm, anchorResolutionParams); 1621 const bool isAutoBSize = nsLayoutUtils::IsAutoBSize( 1622 *styleBSize, blockContainerContentBoxSize.BSize(wm)); 1623 if (isAutoBSize) { 1624 if (mFlags.mIsReplaced && intrinsicSize) { 1625 // It's a replaced element with an 'auto' block size so the box 1626 // block size is its intrinsic size plus any border/padding/margin 1627 boxBSize = LogicalSize(wm, *intrinsicSize).BSize(wm) + 1628 outsideBoxSizing + insideBoxSizing; 1629 } else { 1630 // XXX Bug 1191801 1631 // Figure out how to get the correct boxBSize here (need to reflow the 1632 // positioned frame?) 1633 boxBSize = 0; 1634 } 1635 } else if (styleBSize->BehavesLikeStretchOnBlockAxis()) { 1636 MOZ_ASSERT(blockContainerContentBoxSize.BSize(wm) != NS_UNCONSTRAINEDSIZE, 1637 "If we're 'stretch' with unconstrained size, isAutoBSize " 1638 "should be true which should make us skip this code"); 1639 // TODO(dholbert) The 'insideBoxSizing' and 'outsideBoxSizing' usages 1640 // here aren't quite right, because we're supposed to be passing margin 1641 // and borderPadding specifically. The arithmetic seems to work out in 1642 // testcases though. 1643 boxBSize = nsLayoutUtils::ComputeStretchContentBoxBSize( 1644 blockContainerContentBoxSize.BSize(wm), outsideBoxSizing, 1645 insideBoxSizing); 1646 } else { 1647 // We need to compute it. It's important we do this, because if it's 1648 // percentage-based this computed value may be different from the 1649 // computed value calculated using the absolute containing block height. 1650 boxBSize = nsLayoutUtils::ComputeBSizeValue( 1651 blockContainerContentBoxSize.BSize(wm), insideBoxSizing, 1652 styleBSize->AsLengthPercentage()) + 1653 insideBoxSizing + outsideBoxSizing; 1654 } 1655 1656 LogicalSize boxSize(wm, boxISize.valueOr(0), boxBSize); 1657 1658 LogicalPoint origin(wm, aHypotheticalPos.mIStart, aHypotheticalPos.mBStart); 1659 origin = origin.ConvertRectOriginTo(cbwm, wm, boxSize.GetPhysicalSize(wm), 1660 cbSize); 1661 1662 aHypotheticalPos.mIStart = origin.I(cbwm); 1663 aHypotheticalPos.mBStart = origin.B(cbwm); 1664 aHypotheticalPos.mWritingMode = cbwm; 1665 } else { 1666 aHypotheticalPos.mWritingMode = wm; 1667 } 1668 } 1669 1670 void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput, 1671 const LogicalSize& aCBSize) { 1672 WritingMode wm = GetWritingMode(); 1673 WritingMode cbwm = aCBReflowInput->GetWritingMode(); 1674 NS_WARNING_ASSERTION(aCBSize.BSize(cbwm) != NS_UNCONSTRAINEDSIZE, 1675 "containing block bsize must be constrained"); 1676 1677 NS_ASSERTION(!mFrame->IsTableFrame(), 1678 "InitAbsoluteConstraints should not be called on table frames"); 1679 MOZ_ASSERT( 1680 mFrame->IsAbsolutelyPositioned(mStyleDisplay), 1681 "InitAbsoluteConstraints should be called on abspos or fixedpos frames!"); 1682 1683 const auto anchorResolutionParams = 1684 AnchorPosOffsetResolutionParams::ExplicitCBFrameSize( 1685 AnchorPosResolutionParams::From(this), &aCBSize); 1686 const auto iStartOffset = mStylePosition->GetAnchorResolvedInset( 1687 LogicalSide::IStart, cbwm, anchorResolutionParams); 1688 const auto iEndOffset = mStylePosition->GetAnchorResolvedInset( 1689 LogicalSide::IEnd, cbwm, anchorResolutionParams); 1690 const auto bStartOffset = mStylePosition->GetAnchorResolvedInset( 1691 LogicalSide::BStart, cbwm, anchorResolutionParams); 1692 const auto bEndOffset = mStylePosition->GetAnchorResolvedInset( 1693 LogicalSide::BEnd, cbwm, anchorResolutionParams); 1694 bool iStartIsAuto = iStartOffset->IsAuto(); 1695 bool iEndIsAuto = iEndOffset->IsAuto(); 1696 bool bStartIsAuto = bStartOffset->IsAuto(); 1697 bool bEndIsAuto = bEndOffset->IsAuto(); 1698 1699 // If both 'inline-start' and 'inline-end' are 'auto' or both 'block-start' 1700 // and 'block-end' are 'auto', then compute the hypothetical box position 1701 // where the element would have if it were in the flow. 1702 nsHypotheticalPosition hypotheticalPos; 1703 if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) { 1704 nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame(); 1705 MOZ_ASSERT(placeholderFrame, "no placeholder frame"); 1706 nsIFrame* placeholderParent = placeholderFrame->GetParent(); 1707 MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders"); 1708 1709 if (placeholderFrame->HasAnyStateBits( 1710 PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) { 1711 MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(), 1712 "This flag should only be set on grid/flex children"); 1713 // If the (as-yet unknown) static position will determine the inline 1714 // and/or block offsets, set flags to note those offsets aren't valid 1715 // until we can do CSS Box Alignment on the OOF frame. 1716 mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto); 1717 mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto); 1718 } 1719 1720 if (mFlags.mStaticPosIsCBOrigin) { 1721 hypotheticalPos.mWritingMode = cbwm; 1722 if (placeholderParent->IsGridContainerFrame() && 1723 placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY | 1724 NS_STATE_GRID_IS_ROW_MASONRY)) { 1725 // Disable CSS alignment in Masonry layout since we don't have real grid 1726 // areas in that axis. We'll use the placeholder position instead as it 1727 // was calculated by nsGridContainerFrame::MasonryLayout. 1728 auto cbsz = aCBSize.GetPhysicalSize(cbwm); 1729 LogicalPoint pos = placeholderFrame->GetLogicalPosition(cbwm, cbsz); 1730 if (placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) { 1731 mFlags.mIOffsetsNeedCSSAlign = false; 1732 hypotheticalPos.mIStart = pos.I(cbwm); 1733 } else { 1734 mFlags.mBOffsetsNeedCSSAlign = false; 1735 hypotheticalPos.mBStart = pos.B(cbwm); 1736 } 1737 } 1738 } else { 1739 // XXXmats all this is broken for orthogonal writing-modes: bug 1521988. 1740 CalculateHypotheticalPosition(placeholderFrame, aCBReflowInput, aCBSize, 1741 hypotheticalPos); 1742 if (aCBReflowInput->mFrame->IsGridContainerFrame()) { 1743 // 'hypotheticalPos' is relative to the padding rect of the CB *frame*. 1744 // In grid layout the CB is the grid area rectangle, so we translate 1745 // 'hypotheticalPos' to be relative that rectangle here. 1746 nsRect cb = nsGridContainerFrame::GridItemCB(mFrame); 1747 nscoord left(0); 1748 nscoord right(0); 1749 if (cbwm.IsBidiLTR()) { 1750 left = cb.X(); 1751 } else { 1752 right = aCBReflowInput->ComputedWidth() + 1753 aCBReflowInput->ComputedPhysicalPadding().LeftRight() - 1754 cb.XMost(); 1755 } 1756 LogicalMargin offsets(cbwm, nsMargin(cb.Y(), right, nscoord(0), left)); 1757 hypotheticalPos.mIStart -= offsets.IStart(cbwm); 1758 hypotheticalPos.mBStart -= offsets.BStart(cbwm); 1759 } 1760 } 1761 } 1762 1763 LogicalMargin offsets(cbwm); 1764 1765 // Handle auto inset values, as per [1]. 1766 // Technically superceded by a new section [2], but none of the browsers seem 1767 // to follow this behaviour. 1768 // 1769 // [1] https://drafts.csswg.org/css-position-3/#abspos-old 1770 // [2] https://drafts.csswg.org/css-position-3/#resolving-insets 1771 if (iStartIsAuto) { 1772 offsets.IStart(cbwm) = 0; 1773 } else { 1774 offsets.IStart(cbwm) = nsLayoutUtils::ComputeCBDependentValue( 1775 aCBSize.ISize(cbwm), iStartOffset); 1776 } 1777 if (iEndIsAuto) { 1778 offsets.IEnd(cbwm) = 0; 1779 } else { 1780 offsets.IEnd(cbwm) = 1781 nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(cbwm), iEndOffset); 1782 } 1783 1784 if (iStartIsAuto && iEndIsAuto) { 1785 if (cbwm.IsInlineReversed() != 1786 hypotheticalPos.mWritingMode.IsInlineReversed()) { 1787 offsets.IEnd(cbwm) = hypotheticalPos.mIStart; 1788 iEndIsAuto = false; 1789 } else { 1790 offsets.IStart(cbwm) = hypotheticalPos.mIStart; 1791 iStartIsAuto = false; 1792 } 1793 } 1794 1795 if (bStartIsAuto) { 1796 offsets.BStart(cbwm) = 0; 1797 } else { 1798 offsets.BStart(cbwm) = nsLayoutUtils::ComputeCBDependentValue( 1799 aCBSize.BSize(cbwm), bStartOffset); 1800 } 1801 if (bEndIsAuto) { 1802 offsets.BEnd(cbwm) = 0; 1803 } else { 1804 offsets.BEnd(cbwm) = 1805 nsLayoutUtils::ComputeCBDependentValue(aCBSize.BSize(cbwm), bEndOffset); 1806 } 1807 1808 if (bStartIsAuto && bEndIsAuto) { 1809 // Treat 'top' like 'static-position' 1810 offsets.BStart(cbwm) = hypotheticalPos.mBStart; 1811 bStartIsAuto = false; 1812 } 1813 1814 SetComputedLogicalOffsets(cbwm, offsets); 1815 const bool isOrthogonal = wm.IsOrthogonalTo(cbwm); 1816 if (isOrthogonal) { 1817 if (bStartIsAuto || bEndIsAuto) { 1818 mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; 1819 } 1820 } else { 1821 if (iStartIsAuto || iEndIsAuto) { 1822 mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; 1823 } 1824 } 1825 1826 { 1827 AutoMaybeDisableFontInflation an(mFrame); 1828 1829 auto size = mFrame->ComputeSize( 1830 *this, wm, aCBSize.ConvertTo(wm, cbwm), 1831 aCBSize.ConvertTo(wm, cbwm).ISize(wm), // XXX or AvailableISize()? 1832 ComputedLogicalMargin(wm).Size(wm) + 1833 ComputedLogicalOffsets(wm).Size(wm), 1834 ComputedLogicalBorderPadding(wm).Size(wm), {}, mComputeSizeFlags); 1835 mComputedSize = size.mLogicalSize; 1836 NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); 1837 NS_ASSERTION( 1838 ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0, 1839 "Bogus block-size"); 1840 1841 mFlags.mIsBSizeSetByAspectRatio = 1842 size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize; 1843 } 1844 1845 LogicalMargin margin = ComputedLogicalMargin(cbwm); 1846 const LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbwm); 1847 const LogicalSize computedSize = mComputedSize.ConvertTo(cbwm, wm); 1848 1849 bool iSizeIsAuto = 1850 mStylePosition->ISize(cbwm, anchorResolutionParams.mBaseParams)->IsAuto(); 1851 if (iStartIsAuto) { 1852 // We know 'inset-inline-end' is not 'auto' anymore thanks to the 1853 // hypothetical box code above. Solve for 'inset-inline-start'. 1854 if (iSizeIsAuto) { 1855 offsets.IStart(cbwm) = NS_AUTOOFFSET; 1856 } else { 1857 offsets.IStart(cbwm) = aCBSize.ISize(cbwm) - offsets.IEnd(cbwm) - 1858 computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - 1859 borderPadding.IStartEnd(cbwm); 1860 } 1861 } else if (iEndIsAuto) { 1862 // We know 'inset-inline-start' is not 'auto' anymore thanks to the 1863 // hypothetical box code above. Solve for 'inset-inline-end'. 1864 if (iSizeIsAuto) { 1865 offsets.IEnd(cbwm) = NS_AUTOOFFSET; 1866 } else { 1867 offsets.IEnd(cbwm) = aCBSize.ISize(cbwm) - offsets.IStart(cbwm) - 1868 computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - 1869 borderPadding.IStartEnd(cbwm); 1870 } 1871 } 1872 1873 bool bSizeIsAuto = 1874 mStylePosition->BSize(cbwm, anchorResolutionParams.mBaseParams) 1875 ->BehavesLikeInitialValueOnBlockAxis(); 1876 if (bStartIsAuto) { 1877 // Solve for 'inset-block-start'. 1878 if (bSizeIsAuto) { 1879 offsets.BStart(cbwm) = NS_AUTOOFFSET; 1880 } else { 1881 offsets.BStart(cbwm) = aCBSize.BSize(cbwm) - margin.BStartEnd(cbwm) - 1882 borderPadding.BStartEnd(cbwm) - 1883 computedSize.BSize(cbwm) - offsets.BEnd(cbwm); 1884 } 1885 } else if (bEndIsAuto) { 1886 // Solve for 'inset-block-end'. 1887 if (bSizeIsAuto) { 1888 offsets.BEnd(cbwm) = NS_AUTOOFFSET; 1889 } else { 1890 offsets.BEnd(cbwm) = aCBSize.BSize(cbwm) - margin.BStartEnd(cbwm) - 1891 borderPadding.BStartEnd(cbwm) - 1892 computedSize.BSize(cbwm) - offsets.BStart(cbwm); 1893 } 1894 } 1895 1896 SetComputedLogicalOffsets(cbwm, offsets); 1897 } 1898 1899 // This will not be converted to abstract coordinates because it's only 1900 // used in CalcQuirkContainingBlockHeight 1901 static nscoord GetBlockMarginBorderPadding(const ReflowInput* aReflowInput) { 1902 nscoord result = 0; 1903 if (!aReflowInput) { 1904 return result; 1905 } 1906 1907 // zero auto margins 1908 nsMargin margin = aReflowInput->ComputedPhysicalMargin(); 1909 if (NS_AUTOMARGIN == margin.top) { 1910 margin.top = 0; 1911 } 1912 if (NS_AUTOMARGIN == margin.bottom) { 1913 margin.bottom = 0; 1914 } 1915 1916 result += margin.top + margin.bottom; 1917 result += aReflowInput->ComputedPhysicalBorderPadding().top + 1918 aReflowInput->ComputedPhysicalBorderPadding().bottom; 1919 1920 return result; 1921 } 1922 1923 /* Get the height based on the viewport of the containing block specified 1924 * in aReflowInput when the containing block has mComputedHeight == 1925 * NS_UNCONSTRAINEDSIZE This will walk up the chain of containing blocks looking 1926 * for a computed height until it finds the canvas frame, or it encounters a 1927 * frame that is not a block, area, or scroll frame. This handles compatibility 1928 * with IE (see bug 85016 and bug 219693) 1929 * 1930 * When we encounter scrolledContent block frames, we skip over them, 1931 * since they are guaranteed to not be useful for computing the containing 1932 * block. 1933 * 1934 * See also IsQuirkContainingBlockHeight. 1935 */ 1936 static nscoord CalcQuirkContainingBlockHeight( 1937 const ReflowInput* aCBReflowInput) { 1938 const ReflowInput* firstAncestorRI = nullptr; // a candidate for html frame 1939 const ReflowInput* secondAncestorRI = nullptr; // a candidate for body frame 1940 1941 // initialize the default to NS_UNCONSTRAINEDSIZE as this is the containings 1942 // block computed height when this function is called. It is possible that we 1943 // don't alter this height especially if we are restricted to one level 1944 nscoord result = NS_UNCONSTRAINEDSIZE; 1945 1946 const ReflowInput* ri = aCBReflowInput; 1947 for (; ri; ri = ri->mParentReflowInput) { 1948 LayoutFrameType frameType = ri->mFrame->Type(); 1949 // if the ancestor is auto height then skip it and continue up if it 1950 // is the first block frame and possibly the body/html 1951 if (LayoutFrameType::Block == frameType || 1952 LayoutFrameType::ScrollContainer == frameType) { 1953 secondAncestorRI = firstAncestorRI; 1954 firstAncestorRI = ri; 1955 1956 // If the current frame we're looking at is positioned, we don't want to 1957 // go any further (see bug 221784). The behavior we want here is: 1) If 1958 // not auto-height, use this as the percentage base. 2) If auto-height, 1959 // keep looking, unless the frame is positioned. 1960 if (NS_UNCONSTRAINEDSIZE == ri->ComputedHeight()) { 1961 if (ri->mFrame->IsAbsolutelyPositioned(ri->mStyleDisplay)) { 1962 break; 1963 } else { 1964 continue; 1965 } 1966 } 1967 } else if (LayoutFrameType::Canvas == frameType) { 1968 // Always continue on to the height calculation 1969 } else if (LayoutFrameType::PageContent == frameType) { 1970 nsIFrame* prevInFlow = ri->mFrame->GetPrevInFlow(); 1971 // only use the page content frame for a height basis if it is the first 1972 // in flow 1973 if (prevInFlow) { 1974 break; 1975 } 1976 } else { 1977 break; 1978 } 1979 1980 // if the ancestor is the page content frame then the percent base is 1981 // the avail height, otherwise it is the computed height 1982 result = (LayoutFrameType::PageContent == frameType) ? ri->AvailableHeight() 1983 : ri->ComputedHeight(); 1984 // if unconstrained - don't sutract borders - would result in huge height 1985 if (NS_UNCONSTRAINEDSIZE == result) { 1986 return result; 1987 } 1988 1989 // if we got to the canvas or page content frame, then subtract out 1990 // margin/border/padding for the BODY and HTML elements 1991 if ((LayoutFrameType::Canvas == frameType) || 1992 (LayoutFrameType::PageContent == frameType)) { 1993 result -= GetBlockMarginBorderPadding(firstAncestorRI); 1994 result -= GetBlockMarginBorderPadding(secondAncestorRI); 1995 1996 #ifdef DEBUG 1997 // make sure the first ancestor is the HTML and the second is the BODY 1998 if (firstAncestorRI) { 1999 nsIContent* frameContent = firstAncestorRI->mFrame->GetContent(); 2000 if (frameContent) { 2001 NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html), 2002 "First ancestor is not HTML"); 2003 } 2004 } 2005 if (secondAncestorRI) { 2006 nsIContent* frameContent = secondAncestorRI->mFrame->GetContent(); 2007 if (frameContent) { 2008 NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body), 2009 "Second ancestor is not BODY"); 2010 } 2011 } 2012 #endif 2013 2014 } 2015 // if we got to the html frame (a block child of the canvas) ... 2016 else if (LayoutFrameType::Block == frameType && ri->mParentReflowInput && 2017 ri->mParentReflowInput->mFrame->IsCanvasFrame()) { 2018 // ... then subtract out margin/border/padding for the BODY element 2019 result -= GetBlockMarginBorderPadding(secondAncestorRI); 2020 } 2021 break; 2022 } 2023 2024 // Make sure not to return a negative height here! 2025 return std::max(result, 0); 2026 } 2027 2028 LogicalSize ReflowInput::ComputeContainingBlockRectangle( 2029 nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const { 2030 LogicalSize cbSize = aContainingBlockRI->ComputedSize(); 2031 WritingMode wm = aContainingBlockRI->GetWritingMode(); 2032 2033 if (aContainingBlockRI->mFlags.mTreatBSizeAsIndefinite) { 2034 cbSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; 2035 } else if (aContainingBlockRI->mPercentageBasisInBlockAxis) { 2036 MOZ_ASSERT(cbSize.BSize(wm) == NS_UNCONSTRAINEDSIZE, 2037 "Why provide a percentage basis when the containing block's " 2038 "block-size is definite?"); 2039 cbSize.BSize(wm) = *aContainingBlockRI->mPercentageBasisInBlockAxis; 2040 } 2041 2042 auto IsQuirky = [](const StyleSize& aSize) -> bool { 2043 return aSize.ConvertsToPercentage() || 2044 aSize.BehavesLikeStretchOnBlockAxis(); 2045 }; 2046 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 2047 // In quirks mode, if an element has a percent height (or a 'stretch' height, 2048 // which is kinda like a special version of 100%), then it gets its 2049 // containing block by looking for an ancestor with a non-auto height. 2050 // Note: We don't emulate this quirk for percents in calc(), or in vertical 2051 // writing modes, or if the containing block is a flex or grid item. 2052 if (!wm.IsVertical() && NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { 2053 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 2054 !aContainingBlockRI->mFrame->IsFlexOrGridItem() && 2055 (IsQuirky(*mStylePosition->GetHeight(anchorResolutionParams)) || 2056 (mFrame->IsTableWrapperFrame() && 2057 IsQuirky(*mFrame->PrincipalChildList() 2058 .FirstChild() 2059 ->StylePosition() 2060 ->GetHeight(anchorResolutionParams))))) { 2061 cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI); 2062 } 2063 } 2064 2065 return cbSize.ConvertTo(GetWritingMode(), wm); 2066 } 2067 2068 // XXX refactor this code to have methods for each set of properties 2069 // we are computing: width,height,line-height; margin; offsets 2070 2071 void ReflowInput::InitConstraints( 2072 nsPresContext* aPresContext, const Maybe<LogicalSize>& aContainingBlockSize, 2073 const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding, 2074 LayoutFrameType aFrameType) { 2075 WritingMode wm = GetWritingMode(); 2076 LogicalSize cbSize = aContainingBlockSize.valueOr( 2077 LogicalSize(mWritingMode, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)); 2078 2079 // If this is a reflow root, then set the computed width and 2080 // height equal to the available space 2081 if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) { 2082 // XXXldb This doesn't mean what it used to! 2083 InitOffsets(wm, cbSize.ISize(wm), aFrameType, mComputeSizeFlags, aBorder, 2084 aPadding, mStyleDisplay); 2085 // Override mComputedMargin since reflow roots start from the 2086 // frame's boundary, which is inside the margin. 2087 SetComputedLogicalMargin(wm, LogicalMargin(wm)); 2088 SetComputedLogicalOffsets(wm, LogicalMargin(wm)); 2089 2090 const auto borderPadding = ComputedLogicalBorderPadding(wm); 2091 SetComputedISize( 2092 std::max(0, AvailableISize() - borderPadding.IStartEnd(wm)), 2093 ResetResizeFlags::No); 2094 SetComputedBSize( 2095 AvailableBSize() != NS_UNCONSTRAINEDSIZE 2096 ? std::max(0, AvailableBSize() - borderPadding.BStartEnd(wm)) 2097 : NS_UNCONSTRAINEDSIZE, 2098 ResetResizeFlags::No); 2099 2100 mComputedMinSize.SizeTo(mWritingMode, 0, 0); 2101 mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE, 2102 NS_UNCONSTRAINEDSIZE); 2103 } else { 2104 // Get the containing block's reflow input 2105 const ReflowInput* cbri = mCBReflowInput; 2106 MOZ_ASSERT(cbri, "no containing block"); 2107 MOZ_ASSERT(mFrame->GetParent()); 2108 2109 // If we weren't given a containing block size, then compute one. 2110 if (aContainingBlockSize.isNothing()) { 2111 cbSize = ComputeContainingBlockRectangle(aPresContext, cbri); 2112 } else if (aPresContext->FragmentainerAwarePositioningEnabled() && 2113 mFrame->IsAbsolutelyPositioned(mStyleDisplay) && 2114 mFrame->GetPrevInFlow()) { 2115 // AbsoluteContainingBlock always provides a containing-block size to 2116 // ReflowInput. However, if the delegating frame is a continuation or an 2117 // overflow container (i.e. it has zero block-size), we'll need a 2118 // containing-block size (padding-box size) suitable for resolving the 2119 // abspos continuation's percentage block-size. 2120 // 2121 // Bug 1998818 is to fix the containing-block size for resolving 2122 // percentage block-size for abspos's first-in-flow. 2123 cbSize = ComputeContainingBlockRectangle(aPresContext, cbri) + 2124 cbri->ComputedLogicalPadding(wm).Size(wm); 2125 } 2126 2127 // See if the containing block height is based on the size of its 2128 // content 2129 if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { 2130 // See if the containing block is a cell frame which needs 2131 // to use the mComputedHeight of the cell instead of what the cell block 2132 // passed in. 2133 // XXX It seems like this could lead to bugs with min-height and friends 2134 if (cbri->mParentReflowInput && cbri->mFrame->IsTableCellFrame()) { 2135 cbSize.BSize(wm) = cbri->ComputedSize(wm).BSize(wm); 2136 } 2137 } 2138 2139 // XXX Might need to also pass the CB height (not width) for page boxes, 2140 // too, if we implement them. 2141 2142 // For calculating positioning offsets, margins, borders and 2143 // padding, we use the writing mode of the containing block 2144 WritingMode cbwm = cbri->GetWritingMode(); 2145 InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm), aFrameType, 2146 mComputeSizeFlags, aBorder, aPadding, mStyleDisplay); 2147 2148 // For calculating the size of this box, we use its own writing mode 2149 auto blockSize = 2150 mStylePosition->BSize(wm, AnchorPosResolutionParams::From(this)); 2151 if (blockSize->BehavesLikeStretchOnBlockAxis()) { 2152 // Resolve 'stretch' to either 'auto' or the stretched bsize, depending 2153 // on whether our containing block has a definite bsize. 2154 // TODO(dholbert): remove this in bug 2000035. 2155 if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { 2156 blockSize = AnchorResolvedSizeHelper::Auto(); 2157 } else { 2158 nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize( 2159 cbSize.BSize(wm), ComputedLogicalMargin(wm).BStartEnd(wm), 2160 ComputedLogicalBorderPadding(wm).BStartEnd(wm), 2161 mStylePosition->mBoxSizing); 2162 blockSize = AnchorResolvedSizeHelper::LengthPercentage( 2163 StyleLengthPercentage::FromAppUnits(stretchBSize)); 2164 } 2165 } 2166 bool isAutoBSize = blockSize->BehavesLikeInitialValueOnBlockAxis(); 2167 2168 // Check for a percentage based block size and a containing block 2169 // block size that depends on the content block size 2170 if (blockSize->HasPercent()) { 2171 if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { 2172 // this if clause enables %-blockSize on replaced inline frames, 2173 // such as images. See bug 54119. The else clause "blockSizeUnit = 2174 // eStyleUnit_Auto;" used to be called exclusively. 2175 if (mFlags.mIsReplaced && mStyleDisplay->IsInlineOutsideStyle()) { 2176 // Get the containing block's reflow input 2177 NS_ASSERTION(cbri, "no containing block"); 2178 // in quirks mode, get the cb height using the special quirk method 2179 if (!wm.IsVertical() && 2180 eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { 2181 if (!cbri->mFrame->IsTableCellFrame() && 2182 !cbri->mFrame->IsFlexOrGridItem()) { 2183 cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri); 2184 if (cbSize.BSize(wm) == NS_UNCONSTRAINEDSIZE) { 2185 isAutoBSize = true; 2186 } 2187 } else { 2188 isAutoBSize = true; 2189 } 2190 } 2191 // in standard mode, use the cb block size. if it's "auto", 2192 // as will be the case by default in BODY, use auto block size 2193 // as per CSS2 spec. 2194 else { 2195 nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm); 2196 if (NS_UNCONSTRAINEDSIZE != computedBSize) { 2197 cbSize.BSize(wm) = computedBSize; 2198 } else { 2199 isAutoBSize = true; 2200 } 2201 } 2202 } else { 2203 // default to interpreting the blockSize like 'auto' 2204 isAutoBSize = true; 2205 } 2206 } 2207 } 2208 2209 // Compute our offsets if the element is relatively positioned. We 2210 // need the correct containing block inline-size and block-size 2211 // here, which is why we need to do it after all the quirks-n-such 2212 // above. (If the element is sticky positioned, we need to wait 2213 // until the scroll container knows its size, so we compute offsets 2214 // from StickyScrollContainer::UpdatePositions.) 2215 if (mStyleDisplay->IsRelativelyPositioned(mFrame)) { 2216 const LogicalMargin offsets = 2217 ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm)); 2218 SetComputedLogicalOffsets(cbwm, offsets); 2219 } else { 2220 // Initialize offsets to 0 2221 SetComputedLogicalOffsets(wm, LogicalMargin(wm)); 2222 } 2223 2224 // Calculate the computed values for min and max properties. Note that 2225 // this MUST come after we've computed our border and padding. 2226 ComputeMinMaxValues(cbSize); 2227 2228 // Calculate the computed inlineSize and blockSize. 2229 // This varies by frame type. 2230 2231 if (IsInternalTableFrame()) { 2232 // Internal table elements. The rules vary depending on the type. 2233 // Calculate the computed isize 2234 bool rowOrRowGroup = false; 2235 const auto inlineSize = 2236 mStylePosition->ISize(wm, AnchorPosResolutionParams::From(this)); 2237 bool isAutoISize = inlineSize->IsAuto(); 2238 if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) || 2239 (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) { 2240 // 'inlineSize' property doesn't apply to table rows and row groups 2241 isAutoISize = true; 2242 rowOrRowGroup = true; 2243 } 2244 2245 // calc() with both percentages and lengths act like auto on internal 2246 // table elements 2247 if (isAutoISize || inlineSize->HasLengthAndPercentage()) { 2248 if (AvailableISize() != NS_UNCONSTRAINEDSIZE && !rowOrRowGroup) { 2249 // Internal table elements don't have margins. Only tables and 2250 // cells have border and padding 2251 SetComputedISize( 2252 std::max(0, AvailableISize() - 2253 ComputedLogicalBorderPadding(wm).IStartEnd(wm)), 2254 ResetResizeFlags::No); 2255 } else { 2256 SetComputedISize(AvailableISize(), ResetResizeFlags::No); 2257 } 2258 NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize"); 2259 2260 } else { 2261 SetComputedISize( 2262 ComputeISizeValue(cbSize, mStylePosition->mBoxSizing, *inlineSize), 2263 ResetResizeFlags::No); 2264 } 2265 2266 // Calculate the computed block size 2267 if (StyleDisplay::TableColumn == mStyleDisplay->mDisplay || 2268 StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay) { 2269 // 'blockSize' property doesn't apply to table columns and column groups 2270 isAutoBSize = true; 2271 } 2272 // calc() with both percentages and lengths acts like 'auto' on internal 2273 // table elements 2274 if (isAutoBSize || blockSize->HasLengthAndPercentage()) { 2275 SetComputedBSize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No); 2276 } else { 2277 SetComputedBSize( 2278 ComputeBSizeValue(cbSize.BSize(wm), mStylePosition->mBoxSizing, 2279 blockSize->AsLengthPercentage()), 2280 ResetResizeFlags::No); 2281 } 2282 2283 // Doesn't apply to internal table elements 2284 mComputedMinSize.SizeTo(mWritingMode, 0, 0); 2285 mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE, 2286 NS_UNCONSTRAINEDSIZE); 2287 } else if (mFrame->IsAbsolutelyPositioned(mStyleDisplay) && 2288 // The absolute constraints are needed only for abspos 2289 // first-in-flow, not continuations. 2290 !mFrame->GetPrevInFlow()) { 2291 InitAbsoluteConstraints(cbri, 2292 cbSize.ConvertTo(cbri->GetWritingMode(), wm)); 2293 } else { 2294 AutoMaybeDisableFontInflation an(mFrame); 2295 2296 nsIFrame* const alignCB = [&] { 2297 nsIFrame* cb = mFrame->GetParent(); 2298 if (cb->IsTableWrapperFrame()) { 2299 nsIFrame* alignCBParent = cb->GetParent(); 2300 if (alignCBParent && alignCBParent->IsGridContainerFrame()) { 2301 return alignCBParent; 2302 } 2303 } 2304 return cb; 2305 }(); 2306 2307 const bool isInlineLevel = [&] { 2308 if (mFrame->IsTableFrame()) { 2309 // An inner table frame is not inline-level, even if it happens to 2310 // have 'display:inline-table'. (That makes its table-wrapper frame be 2311 // inline-level, but not the inner table frame) 2312 return false; 2313 } 2314 if (mStyleDisplay->IsInlineOutsideStyle()) { 2315 return true; 2316 } 2317 if (mFlags.mIsReplaced && (mStyleDisplay->IsInnerTableStyle() || 2318 mStyleDisplay->DisplayOutside() == 2319 StyleDisplayOutside::TableCaption)) { 2320 // Internal table values on replaced elements behave as inline 2321 // https://drafts.csswg.org/css-tables-3/#table-structure 2322 // 2323 // ... it is handled instead as though the author had declared 2324 // either 'block' (for 'table' display) or 'inline' (for all 2325 // other values)" 2326 // 2327 // FIXME(emilio): The only test that covers this is 2328 // table-anonymous-objects-211.xht, which fails on other browsers (but 2329 // differently to us, if you just remove this condition). 2330 return true; 2331 } 2332 if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && 2333 !mStyleDisplay->IsAbsolutelyPositionedStyle()) { 2334 // Floats are treated as inline-level and also shrink-wrap. 2335 return true; 2336 } 2337 return false; 2338 }(); 2339 2340 if (mParentReflowInput->mFlags.mOrthogonalCellFinalReflow) { 2341 // This is the "extra" reflow for the inner content of an orthogonal 2342 // table cell, after the row size has been determined; so we want to 2343 // respect the cell's size without further adjustment. Its rect may 2344 // not yet be correct, however, so we base our size on the parent 2345 // reflow input's available size, adjusted for border widths. 2346 MOZ_ASSERT(mFrame->GetParent()->IsTableCellFrame(), 2347 "unexpected mOrthogonalCellFinalReflow flag!"); 2348 cbSize = mParentReflowInput->AvailableSize().ConvertTo( 2349 wm, mParentReflowInput->GetWritingMode()); 2350 cbSize -= mParentReflowInput->ComputedLogicalBorder(wm).Size(wm); 2351 SetAvailableISize(cbSize.ISize(wm)); 2352 } else { 2353 const bool shouldShrinkWrap = [&] { 2354 if (isInlineLevel) { 2355 return true; 2356 } 2357 if (mFlags.mIsReplaced && !alignCB->IsFlexOrGridContainer()) { 2358 // Shrink-wrap replaced elements when in-flow (out of flows are 2359 // handled above). We exclude replaced elements in grid or flex 2360 // contexts, where we don't want to shrink-wrap unconditionally (so 2361 // that stretching can happen). When grid/flex explicitly want 2362 // shrink-wrapping, they can request it directly using the relevant 2363 // flag. 2364 return true; 2365 } 2366 if (!alignCB->IsGridContainerFrame() && 2367 mWritingMode.IsOrthogonalTo(alignCB->GetWritingMode())) { 2368 // Shrink-wrap blocks that are orthogonal to their container (unless 2369 // we're in a grid?) 2370 return true; 2371 } 2372 return false; 2373 }(); 2374 2375 if (shouldShrinkWrap) { 2376 mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; 2377 } 2378 2379 if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { 2380 // For orthogonal flows, where we found a parent orthogonal-limit for 2381 // AvailableISize() in Init(), we'll use the same here as well. 2382 cbSize.ISize(wm) = AvailableISize(); 2383 } 2384 } 2385 2386 auto size = mFrame->ComputeSize(*this, wm, cbSize, AvailableISize(), 2387 ComputedLogicalMargin(wm).Size(wm), 2388 ComputedLogicalBorderPadding(wm).Size(wm), 2389 mStyleSizeOverrides, mComputeSizeFlags); 2390 2391 mComputedSize = size.mLogicalSize; 2392 NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); 2393 NS_ASSERTION( 2394 ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0, 2395 "Bogus block-size"); 2396 2397 mFlags.mIsBSizeSetByAspectRatio = 2398 size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize; 2399 2400 const bool shouldCalculateBlockSideMargins = [&]() { 2401 if (isInlineLevel) { 2402 return false; 2403 } 2404 if (mFrame->IsTableFrame()) { 2405 return false; 2406 } 2407 if (alignCB->IsFlexOrGridContainer()) { 2408 // Exclude flex and grid items. 2409 return false; 2410 } 2411 const auto pseudoType = mFrame->Style()->GetPseudoType(); 2412 if (pseudoType == PseudoStyleType::marker && 2413 mFrame->GetParent()->StyleList()->mListStylePosition == 2414 StyleListStylePosition::Outside) { 2415 // Exclude outside ::markers. 2416 return false; 2417 } 2418 if (pseudoType == PseudoStyleType::columnContent) { 2419 // Exclude -moz-column-content since it cannot have any margin. 2420 return false; 2421 } 2422 return true; 2423 }(); 2424 2425 if (shouldCalculateBlockSideMargins) { 2426 CalculateBlockSideMargins(); 2427 } 2428 } 2429 } 2430 2431 // Save our containing block dimensions 2432 mContainingBlockSize = cbSize; 2433 } 2434 2435 static void UpdateProp(nsIFrame* aFrame, 2436 const FramePropertyDescriptor<nsMargin>* aProperty, 2437 bool aNeeded, const nsMargin& aNewValue) { 2438 if (aNeeded) { 2439 if (nsMargin* propValue = aFrame->GetProperty(aProperty)) { 2440 *propValue = aNewValue; 2441 } else { 2442 aFrame->AddProperty(aProperty, new nsMargin(aNewValue)); 2443 } 2444 } else { 2445 aFrame->RemoveProperty(aProperty); 2446 } 2447 } 2448 2449 void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis, 2450 LayoutFrameType aFrameType, 2451 ComputeSizeFlags aFlags, 2452 const Maybe<LogicalMargin>& aBorder, 2453 const Maybe<LogicalMargin>& aPadding, 2454 const nsStyleDisplay* aDisplay) { 2455 nsPresContext* presContext = mFrame->PresContext(); 2456 2457 // Compute margins from the specified margin style information. These 2458 // become the default computed values, and may be adjusted below 2459 // XXX fix to provide 0,0 for the top&bottom margins for 2460 // inline-non-replaced elements 2461 bool needMarginProp = ComputeMargin(aCBWM, aPercentBasis, aFrameType); 2462 // Note that ComputeMargin() simplistically resolves 'auto' margins to 0. 2463 // In formatting contexts where this isn't correct, some later code will 2464 // need to update the UsedMargin() property with the actual resolved value. 2465 // One example of this is ::CalculateBlockSideMargins(). 2466 ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp, 2467 ComputedPhysicalMargin()); 2468 2469 const WritingMode wm = GetWritingMode(); 2470 const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay); 2471 bool needPaddingProp; 2472 LayoutDeviceIntMargin widgetPadding; 2473 if (mIsThemed && presContext->Theme()->GetWidgetPadding( 2474 presContext->DeviceContext(), mFrame, 2475 disp->EffectiveAppearance(), &widgetPadding)) { 2476 const nsMargin padding = LayoutDevicePixel::ToAppUnits( 2477 widgetPadding, presContext->AppUnitsPerDevPixel()); 2478 SetComputedLogicalPadding(wm, LogicalMargin(wm, padding)); 2479 needPaddingProp = false; 2480 } else if (mFrame->IsInSVGTextSubtree()) { 2481 SetComputedLogicalPadding(wm, LogicalMargin(wm)); 2482 needPaddingProp = false; 2483 } else if (aPadding) { // padding is an input arg 2484 SetComputedLogicalPadding(wm, *aPadding); 2485 nsMargin stylePadding; 2486 // If the caller passes a padding that doesn't match our style (like 2487 // nsTextControlFrame might due due to theming), then we also need a 2488 // padding prop. 2489 needPaddingProp = !mFrame->StylePadding()->GetPadding(stylePadding) || 2490 aPadding->GetPhysicalMargin(wm) != stylePadding; 2491 } else { 2492 needPaddingProp = ComputePadding(aCBWM, aPercentBasis, aFrameType); 2493 } 2494 2495 // Add [align|justify]-content:baseline padding contribution. 2496 typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop; 2497 auto ApplyBaselinePadding = [this, wm, &needPaddingProp](LogicalAxis aAxis, 2498 Prop aProp) { 2499 bool found; 2500 nscoord val = mFrame->GetProperty(aProp, &found); 2501 if (found) { 2502 NS_ASSERTION(val != nscoord(0), "zero in this property is useless"); 2503 LogicalSide side; 2504 if (val > 0) { 2505 side = MakeLogicalSide(aAxis, LogicalEdge::Start); 2506 } else { 2507 side = MakeLogicalSide(aAxis, LogicalEdge::End); 2508 val = -val; 2509 } 2510 mComputedPadding.Side(side, wm) += val; 2511 needPaddingProp = true; 2512 if (aAxis == LogicalAxis::Block && val > 0) { 2513 // We have a baseline-adjusted block-axis start padding, so 2514 // we need this to mark lines dirty when mIsBResize is true: 2515 this->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 2516 } 2517 } 2518 }; 2519 if (!aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow)) { 2520 ApplyBaselinePadding(LogicalAxis::Block, nsIFrame::BBaselinePadProperty()); 2521 } 2522 if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap)) { 2523 ApplyBaselinePadding(LogicalAxis::Inline, nsIFrame::IBaselinePadProperty()); 2524 } 2525 2526 LogicalMargin border(wm); 2527 if (mIsThemed) { 2528 const LayoutDeviceIntMargin widgetBorder = 2529 presContext->Theme()->GetWidgetBorder( 2530 presContext->DeviceContext(), mFrame, disp->EffectiveAppearance()); 2531 border = LogicalMargin( 2532 wm, LayoutDevicePixel::ToAppUnits(widgetBorder, 2533 presContext->AppUnitsPerDevPixel())); 2534 } else if (mFrame->IsInSVGTextSubtree()) { 2535 // Do nothing since the border local variable is initialized all zero. 2536 } else if (aBorder) { // border is an input arg 2537 border = *aBorder; 2538 } else { 2539 border = LogicalMargin(wm, mFrame->StyleBorder()->GetComputedBorder()); 2540 } 2541 SetComputedLogicalBorderPadding(wm, border + ComputedLogicalPadding(wm)); 2542 2543 if (aFrameType == LayoutFrameType::Scrollbar) { 2544 // scrollbars may have had their width or height smashed to zero 2545 // by the associated scrollframe, in which case we must not report 2546 // any padding or border. 2547 nsSize size(mFrame->GetSize()); 2548 if (size.width == 0 || size.height == 0) { 2549 SetComputedLogicalPadding(wm, LogicalMargin(wm)); 2550 SetComputedLogicalBorderPadding(wm, LogicalMargin(wm)); 2551 } 2552 } 2553 2554 bool hasPaddingChange; 2555 if (nsMargin* oldPadding = 2556 mFrame->GetProperty(nsIFrame::UsedPaddingProperty())) { 2557 // Note: If a padding change is already detectable without resolving the 2558 // percentage, e.g. a padding is changing from 50px to 50%, 2559 // nsIFrame::DidSetComputedStyle() will cache the old padding in 2560 // UsedPaddingProperty(). 2561 hasPaddingChange = *oldPadding != ComputedPhysicalPadding(); 2562 } else { 2563 // Our padding may have changed, but we can't tell at this point. 2564 hasPaddingChange = needPaddingProp; 2565 } 2566 // Keep mHasPaddingChange bit set until we've done reflow. We'll clear it in 2567 // nsIFrame::DidReflow() 2568 mFrame->SetHasPaddingChange(mFrame->HasPaddingChange() || hasPaddingChange); 2569 2570 ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp, 2571 ComputedPhysicalPadding()); 2572 } 2573 2574 // This code enforces section 10.3.3 of the CSS2 spec for this formula: 2575 // 2576 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 2577 // 'padding-right' + 'border-right-width' + 'margin-right' 2578 // = width of containing block 2579 // 2580 // Note: the width unit is not auto when this is called 2581 void ReflowInput::CalculateBlockSideMargins() { 2582 MOZ_ASSERT(!mFrame->IsTableFrame(), 2583 "Inner table frame cannot have computed margins!"); 2584 2585 // Calculations here are done in the containing block's writing mode, 2586 // which is where margins will eventually be applied: we're calculating 2587 // margins that will be used by the container in its inline direction, 2588 // which in the case of an orthogonal contained block will correspond to 2589 // the block direction of this reflow input. So in the orthogonal-flow 2590 // case, "CalculateBlock*Side*Margins" will actually end up adjusting 2591 // the BStart/BEnd margins; those are the "sides" of the block from its 2592 // container's point of view. 2593 WritingMode cbWM = GetCBWritingMode(); 2594 2595 nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM); 2596 nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM); 2597 if (availISizeCBWM == NS_UNCONSTRAINEDSIZE || 2598 computedISizeCBWM == NS_UNCONSTRAINEDSIZE) { 2599 // For orthogonal flows, where we found a parent orthogonal-limit 2600 // for AvailableISize() in Init(), we don't have meaningful sizes to 2601 // adjust. Act like the sum is already correct (below). 2602 return; 2603 } 2604 2605 LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM && 2606 NS_UNCONSTRAINEDSIZE != availISizeCBWM, 2607 "have unconstrained inline-size; this should only " 2608 "result from very large sizes, not attempts at " 2609 "intrinsic inline-size calculation"); 2610 2611 LogicalMargin margin = ComputedLogicalMargin(cbWM); 2612 LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbWM); 2613 nscoord sum = margin.IStartEnd(cbWM) + borderPadding.IStartEnd(cbWM) + 2614 computedISizeCBWM; 2615 if (sum == availISizeCBWM) { 2616 // The sum is already correct 2617 return; 2618 } 2619 2620 // Determine the start and end margin values. The isize value 2621 // remains constant while we do this. 2622 2623 // Calculate how much space is available for margins 2624 nscoord availMarginSpace = availISizeCBWM - sum; 2625 2626 // If the available margin space is negative, then don't follow the 2627 // usual overconstraint rules. 2628 if (availMarginSpace < 0) { 2629 margin.IEnd(cbWM) += availMarginSpace; 2630 SetComputedLogicalMargin(cbWM, margin); 2631 return; 2632 } 2633 2634 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 2635 // The css2 spec clearly defines how block elements should behave 2636 // in section 10.3.3. 2637 bool isAutoStartMargin = 2638 mStyleMargin->GetMargin(LogicalSide::IStart, cbWM, anchorResolutionParams) 2639 ->IsAuto(); 2640 bool isAutoEndMargin = 2641 mStyleMargin->GetMargin(LogicalSide::IEnd, cbWM, anchorResolutionParams) 2642 ->IsAuto(); 2643 if (!isAutoStartMargin && !isAutoEndMargin) { 2644 // Neither margin is 'auto' so we're over constrained. Use the 2645 // 'direction' property of the parent to tell which margin to 2646 // ignore 2647 // First check if there is an HTML alignment that we should honor 2648 const StyleTextAlign* textAlign = 2649 mParentReflowInput 2650 ? &mParentReflowInput->mFrame->StyleText()->mTextAlign 2651 : nullptr; 2652 if (textAlign && (*textAlign == StyleTextAlign::MozLeft || 2653 *textAlign == StyleTextAlign::MozCenter || 2654 *textAlign == StyleTextAlign::MozRight)) { 2655 if (mParentReflowInput->mWritingMode.IsBidiLTR()) { 2656 isAutoStartMargin = *textAlign != StyleTextAlign::MozLeft; 2657 isAutoEndMargin = *textAlign != StyleTextAlign::MozRight; 2658 } else { 2659 isAutoStartMargin = *textAlign != StyleTextAlign::MozRight; 2660 isAutoEndMargin = *textAlign != StyleTextAlign::MozLeft; 2661 } 2662 } 2663 // Otherwise apply the CSS rules, and ignore one margin by forcing 2664 // it to 'auto', depending on 'direction'. 2665 else { 2666 isAutoEndMargin = true; 2667 } 2668 } 2669 2670 // Logic which is common to blocks and tables 2671 // The computed margins need not be zero because the 'auto' could come from 2672 // overconstraint or from HTML alignment so values need to be accumulated 2673 2674 if (isAutoStartMargin) { 2675 if (isAutoEndMargin) { 2676 // Both margins are 'auto' so the computed addition should be equal 2677 nscoord forStart = availMarginSpace / 2; 2678 margin.IStart(cbWM) += forStart; 2679 margin.IEnd(cbWM) += availMarginSpace - forStart; 2680 } else { 2681 margin.IStart(cbWM) += availMarginSpace; 2682 } 2683 } else if (isAutoEndMargin) { 2684 margin.IEnd(cbWM) += availMarginSpace; 2685 } 2686 SetComputedLogicalMargin(cbWM, margin); 2687 2688 if (isAutoStartMargin || isAutoEndMargin) { 2689 // Update the UsedMargin property if we were tracking it already. 2690 nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty()); 2691 if (propValue) { 2692 *propValue = margin.GetPhysicalMargin(cbWM); 2693 } 2694 } 2695 } 2696 2697 // For "normal" we use the font's normal line height (em height + leading). 2698 // If both internal leading and external leading specified by font itself are 2699 // zeros, we should compensate this by creating extra (external) leading. 2700 // This is necessary because without this compensation, normal line height might 2701 // look too tight. 2702 static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) { 2703 MOZ_ASSERT(aFontMetrics, "no font metrics"); 2704 nscoord externalLeading = aFontMetrics->ExternalLeading(); 2705 nscoord internalLeading = aFontMetrics->InternalLeading(); 2706 nscoord emHeight = aFontMetrics->EmHeight(); 2707 if (!internalLeading && !externalLeading) { 2708 return NSToCoordRound(static_cast<float>(emHeight) * 2709 ReflowInput::kNormalLineHeightFactor); 2710 } 2711 return emHeight + internalLeading + externalLeading; 2712 } 2713 2714 static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh, 2715 const nsFont& aFont, nsAtom* aLanguage, 2716 bool aExplicitLanguage, 2717 nsPresContext* aPresContext, 2718 bool aIsVertical, nscoord aBlockBSize, 2719 float aFontSizeInflation) { 2720 if (aLh.IsLength()) { 2721 nscoord result = aLh.AsLength().ToAppUnits(); 2722 if (aFontSizeInflation != 1.0f) { 2723 result = NSToCoordRound(static_cast<float>(result) * aFontSizeInflation); 2724 } 2725 return result; 2726 } 2727 2728 if (aLh.IsNumber()) { 2729 // For factor units the computed value of the line-height property 2730 // is found by multiplying the factor by the font's computed size 2731 // (adjusted for min-size prefs and text zoom). 2732 return aFont.size.ScaledBy(aLh.AsNumber() * aFontSizeInflation) 2733 .ToAppUnits(); 2734 } 2735 2736 MOZ_ASSERT(aLh.IsNormal() || aLh.IsMozBlockHeight()); 2737 if (aLh.IsMozBlockHeight() && aBlockBSize != NS_UNCONSTRAINEDSIZE) { 2738 return aBlockBSize; 2739 } 2740 2741 auto size = aFont.size; 2742 size.ScaleBy(aFontSizeInflation); 2743 2744 if (aPresContext) { 2745 nsFont font = aFont; 2746 font.size = size; 2747 nsFontMetrics::Params params; 2748 params.language = aLanguage; 2749 params.explicitLanguage = aExplicitLanguage; 2750 params.orientation = 2751 aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal; 2752 params.userFontSet = aPresContext->GetUserFontSet(); 2753 params.textPerf = aPresContext->GetTextPerfMetrics(); 2754 params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup(); 2755 RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params); 2756 return GetNormalLineHeight(fm); 2757 } 2758 // If we don't have a pres context, use a 1.2em fallback. 2759 size.ScaleBy(ReflowInput::kNormalLineHeightFactor); 2760 return size.ToAppUnits(); 2761 } 2762 2763 nscoord ReflowInput::GetLineHeight() const { 2764 if (mLineHeight != NS_UNCONSTRAINEDSIZE) { 2765 return mLineHeight; 2766 } 2767 2768 nscoord blockBSize = nsLayoutUtils::IsNonWrapperBlock(mFrame) 2769 ? ComputedBSize() 2770 : (mCBReflowInput ? mCBReflowInput->ComputedBSize() 2771 : NS_UNCONSTRAINEDSIZE); 2772 mLineHeight = CalcLineHeight(*mFrame->Style(), mFrame->PresContext(), 2773 mFrame->GetContent(), blockBSize, 2774 nsLayoutUtils::FontSizeInflationFor(mFrame)); 2775 return mLineHeight; 2776 } 2777 2778 void ReflowInput::SetLineHeight(nscoord aLineHeight) { 2779 MOZ_ASSERT(aLineHeight >= 0, "aLineHeight must be >= 0!"); 2780 2781 if (mLineHeight != aLineHeight) { 2782 mLineHeight = aLineHeight; 2783 // Setting used line height can change a frame's block-size if mFrame's 2784 // block-size behaves as auto. 2785 InitResizeFlags(mFrame->PresContext(), mFrame->Type()); 2786 } 2787 } 2788 2789 /* static */ 2790 nscoord ReflowInput::CalcLineHeight(const ComputedStyle& aStyle, 2791 nsPresContext* aPresContext, 2792 const nsIContent* aContent, 2793 nscoord aBlockBSize, 2794 float aFontSizeInflation) { 2795 const StyleLineHeight& lh = aStyle.StyleFont()->mLineHeight; 2796 WritingMode wm(&aStyle); 2797 const bool vertical = wm.IsVertical() && !wm.IsSideways(); 2798 return CalcLineHeight(lh, *aStyle.StyleFont(), aPresContext, vertical, 2799 aContent, aBlockBSize, aFontSizeInflation); 2800 } 2801 2802 nscoord ReflowInput::CalcLineHeight( 2803 const StyleLineHeight& aLh, const nsStyleFont& aRelativeToFont, 2804 nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent, 2805 nscoord aBlockBSize, float aFontSizeInflation) { 2806 nscoord lineHeight = 2807 ComputeLineHeight(aLh, aRelativeToFont.mFont, aRelativeToFont.mLanguage, 2808 aRelativeToFont.mExplicitLanguage, aPresContext, 2809 aIsVertical, aBlockBSize, aFontSizeInflation); 2810 2811 NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); 2812 2813 const auto* input = HTMLInputElement::FromNodeOrNull(aContent); 2814 if (input && input->IsSingleLineTextControl()) { 2815 // For Web-compatibility, single-line text input elements cannot 2816 // have a line-height smaller than 'normal'. 2817 if (!aLh.IsNormal()) { 2818 nscoord normal = ComputeLineHeight( 2819 StyleLineHeight::Normal(), aRelativeToFont.mFont, 2820 aRelativeToFont.mLanguage, aRelativeToFont.mExplicitLanguage, 2821 aPresContext, aIsVertical, aBlockBSize, aFontSizeInflation); 2822 if (lineHeight < normal) { 2823 lineHeight = normal; 2824 } 2825 } 2826 } 2827 2828 return lineHeight; 2829 } 2830 2831 nscoord ReflowInput::CalcLineHeightForCanvas(const StyleLineHeight& aLh, 2832 const nsFont& aRelativeToFont, 2833 nsAtom* aLanguage, 2834 bool aExplicitLanguage, 2835 nsPresContext* aPresContext, 2836 WritingMode aWM) { 2837 return ComputeLineHeight(aLh, aRelativeToFont, aLanguage, aExplicitLanguage, 2838 aPresContext, aWM.IsVertical() && !aWM.IsSideways(), 2839 NS_UNCONSTRAINEDSIZE, 1.0f); 2840 } 2841 2842 bool SizeComputationInput::ComputeMargin(WritingMode aCBWM, 2843 nscoord aPercentBasis, 2844 LayoutFrameType aFrameType) { 2845 // SVG text frames have no margin. 2846 if (mFrame->IsInSVGTextSubtree()) { 2847 return false; 2848 } 2849 2850 if (aFrameType == LayoutFrameType::Table) { 2851 // Table frame's margin is inherited to the table wrapper frame via the 2852 // ::-moz-table-wrapper rule in ua.css, so don't set any margins for it. 2853 SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode)); 2854 return false; 2855 } 2856 2857 // If style style can provide us the margin directly, then use it. 2858 const nsStyleMargin* styleMargin = mFrame->StyleMargin(); 2859 nsMargin margin; 2860 const bool isLayoutDependent = !styleMargin->GetMargin(margin); 2861 if (isLayoutDependent) { 2862 // We have to compute the value. Note that this calculation is 2863 // performed according to the writing mode of the containing block 2864 // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) 2865 if (aPercentBasis == NS_UNCONSTRAINEDSIZE) { 2866 aPercentBasis = 0; 2867 } 2868 LogicalMargin m(aCBWM); 2869 const auto anchorResolutionParams = 2870 AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache); 2871 for (const LogicalSide side : LogicalSides::All) { 2872 m.Side(side, aCBWM) = nsLayoutUtils::ComputeCBDependentValue( 2873 aPercentBasis, 2874 styleMargin->GetMargin(side, aCBWM, anchorResolutionParams)); 2875 } 2876 SetComputedLogicalMargin(aCBWM, m); 2877 } else { 2878 SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode, margin)); 2879 } 2880 2881 // ... but font-size-inflation-based margin adjustment uses the 2882 // frame's writing mode 2883 nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame); 2884 2885 if (marginAdjustment > 0) { 2886 LogicalMargin m = ComputedLogicalMargin(mWritingMode); 2887 m.IStart(mWritingMode) += marginAdjustment; 2888 SetComputedLogicalMargin(mWritingMode, m); 2889 } 2890 2891 return isLayoutDependent; 2892 } 2893 2894 bool SizeComputationInput::ComputePadding(WritingMode aCBWM, 2895 nscoord aPercentBasis, 2896 LayoutFrameType aFrameType) { 2897 // If style can provide us the padding directly, then use it. 2898 const nsStylePadding* stylePadding = mFrame->StylePadding(); 2899 nsMargin padding; 2900 bool isCBDependent = !stylePadding->GetPadding(padding); 2901 // a table row/col group, row/col doesn't have padding 2902 // XXXldb Neither do border-collapse tables. 2903 if (LayoutFrameType::TableRowGroup == aFrameType || 2904 LayoutFrameType::TableColGroup == aFrameType || 2905 LayoutFrameType::TableRow == aFrameType || 2906 LayoutFrameType::TableCol == aFrameType) { 2907 SetComputedLogicalPadding(mWritingMode, LogicalMargin(mWritingMode)); 2908 } else if (isCBDependent) { 2909 // We have to compute the value. This calculation is performed 2910 // according to the writing mode of the containing block 2911 // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) 2912 // clamp negative calc() results to 0 2913 if (aPercentBasis == NS_UNCONSTRAINEDSIZE) { 2914 aPercentBasis = 0; 2915 } 2916 LogicalMargin p(aCBWM); 2917 for (const LogicalSide side : LogicalSides::All) { 2918 p.Side(side, aCBWM) = std::max( 2919 0, nsLayoutUtils::ComputeCBDependentValue( 2920 aPercentBasis, stylePadding->mPadding.Get(side, aCBWM))); 2921 } 2922 SetComputedLogicalPadding(aCBWM, p); 2923 } else { 2924 SetComputedLogicalPadding(mWritingMode, 2925 LogicalMargin(mWritingMode, padding)); 2926 } 2927 return isCBDependent; 2928 } 2929 2930 void ReflowInput::ComputeMinMaxValues(const LogicalSize& aCBSize) { 2931 WritingMode wm = GetWritingMode(); 2932 2933 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this); 2934 const auto minISize = mStylePosition->MinISize(wm, anchorResolutionParams); 2935 const auto maxISize = mStylePosition->MaxISize(wm, anchorResolutionParams); 2936 const auto minBSize = mStylePosition->MinBSize(wm, anchorResolutionParams); 2937 const auto maxBSize = mStylePosition->MaxBSize(wm, anchorResolutionParams); 2938 2939 LogicalSize minWidgetSize(wm); 2940 if (mIsThemed) { 2941 nsPresContext* pc = mFrame->PresContext(); 2942 const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize( 2943 pc, mFrame, mStyleDisplay->EffectiveAppearance()); 2944 2945 // Convert themed widget's physical dimensions to logical coords. 2946 minWidgetSize = { 2947 wm, LayoutDeviceIntSize::ToAppUnits(widget, pc->AppUnitsPerDevPixel())}; 2948 2949 // GetMinimumWidgetSize() returns border-box; we need content-box. 2950 minWidgetSize -= ComputedLogicalBorderPadding(wm).Size(wm); 2951 } 2952 2953 // NOTE: min-width:auto resolves to 0, except on a flex item. (But 2954 // even there, it's supposed to be ignored (i.e. treated as 0) until 2955 // the flex container explicitly resolves & considers it.) 2956 if (minISize->IsAuto()) { 2957 SetComputedMinISize(0); 2958 } else { 2959 SetComputedMinISize( 2960 ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, *minISize)); 2961 } 2962 2963 if (mIsThemed) { 2964 SetComputedMinISize(std::max(ComputedMinISize(), minWidgetSize.ISize(wm))); 2965 } 2966 2967 if (maxISize->IsNone()) { 2968 // Specified value of 'none' 2969 SetComputedMaxISize(NS_UNCONSTRAINEDSIZE); 2970 } else { 2971 SetComputedMaxISize( 2972 ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, *maxISize)); 2973 } 2974 2975 // If the computed value of 'min-width' is greater than the value of 2976 // 'max-width', 'max-width' is set to the value of 'min-width' 2977 if (ComputedMinISize() > ComputedMaxISize()) { 2978 SetComputedMaxISize(ComputedMinISize()); 2979 } 2980 2981 // Check for percentage based values and a containing block height that 2982 // depends on the content height. Treat them like the initial value. 2983 // Likewise, check for calc() with percentages on internal table elements; 2984 // that's treated as the initial value too. 2985 const bool isInternalTableFrame = IsInternalTableFrame(); 2986 const nscoord& bPercentageBasis = aCBSize.BSize(wm); 2987 auto BSizeBehavesAsInitialValue = [&](const auto& aBSize) { 2988 if (nsLayoutUtils::IsAutoBSize(aBSize, bPercentageBasis)) { 2989 return true; 2990 } 2991 if (isInternalTableFrame) { 2992 return aBSize.HasLengthAndPercentage(); 2993 } 2994 return false; 2995 }; 2996 2997 // NOTE: min-height:auto resolves to 0, except on a flex item. (But 2998 // even there, it's supposed to be ignored (i.e. treated as 0) until 2999 // the flex container explicitly resolves & considers it.) 3000 if (BSizeBehavesAsInitialValue(*minBSize)) { 3001 SetComputedMinBSize(0); 3002 } else { 3003 SetComputedMinBSize(ComputeBSizeValueHandlingStretch( 3004 bPercentageBasis, mStylePosition->mBoxSizing, *minBSize)); 3005 } 3006 3007 if (mIsThemed) { 3008 SetComputedMinBSize(std::max(ComputedMinBSize(), minWidgetSize.BSize(wm))); 3009 } 3010 3011 if (BSizeBehavesAsInitialValue(*maxBSize)) { 3012 // Specified value of 'none' 3013 SetComputedMaxBSize(NS_UNCONSTRAINEDSIZE); 3014 } else { 3015 SetComputedMaxBSize(ComputeBSizeValueHandlingStretch( 3016 bPercentageBasis, mStylePosition->mBoxSizing, *maxBSize)); 3017 } 3018 3019 // If the computed value of 'min-height' is greater than the value of 3020 // 'max-height', 'max-height' is set to the value of 'min-height' 3021 if (ComputedMinBSize() > ComputedMaxBSize()) { 3022 SetComputedMaxBSize(ComputedMinBSize()); 3023 } 3024 } 3025 3026 bool ReflowInput::IsInternalTableFrame() const { 3027 return mFrame->IsTableRowGroupFrame() || mFrame->IsTableColGroupFrame() || 3028 mFrame->IsTableRowFrame() || mFrame->IsTableCellFrame(); 3029 }