1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsHTMLReflowState.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2681 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* struct containing the input to nsIFrame::Reflow */ 1.10 + 1.11 +#include "nsHTMLReflowState.h" 1.12 + 1.13 +#include "nsStyleConsts.h" 1.14 +#include "nsCSSAnonBoxes.h" 1.15 +#include "nsFrame.h" 1.16 +#include "nsIContent.h" 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsPresContext.h" 1.19 +#include "nsIPresShell.h" 1.20 +#include "nsFontMetrics.h" 1.21 +#include "nsBlockFrame.h" 1.22 +#include "nsLineBox.h" 1.23 +#include "nsFlexContainerFrame.h" 1.24 +#include "nsImageFrame.h" 1.25 +#include "nsTableFrame.h" 1.26 +#include "nsTableCellFrame.h" 1.27 +#include "nsIPercentHeightObserver.h" 1.28 +#include "nsLayoutUtils.h" 1.29 +#include "mozilla/Preferences.h" 1.30 +#include "nsFontInflationData.h" 1.31 +#include "StickyScrollContainer.h" 1.32 +#include "nsIFrameInlines.h" 1.33 +#include <algorithm> 1.34 +#include "mozilla/dom/HTMLInputElement.h" 1.35 + 1.36 +#ifdef DEBUG 1.37 +#undef NOISY_VERTICAL_ALIGN 1.38 +#else 1.39 +#undef NOISY_VERTICAL_ALIGN 1.40 +#endif 1.41 + 1.42 +using namespace mozilla; 1.43 +using namespace mozilla::css; 1.44 +using namespace mozilla::dom; 1.45 +using namespace mozilla::layout; 1.46 + 1.47 +enum eNormalLineHeightControl { 1.48 + eUninitialized = -1, 1.49 + eNoExternalLeading = 0, // does not include external leading 1.50 + eIncludeExternalLeading, // use whatever value font vendor provides 1.51 + eCompensateLeading // compensate leading if leading provided by font vendor is not enough 1.52 +}; 1.53 + 1.54 +static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized; 1.55 + 1.56 +// Initialize a <b>root</b> reflow state with a rendering context to 1.57 +// use for measuring things. 1.58 +nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, 1.59 + nsIFrame* aFrame, 1.60 + nsRenderingContext* aRenderingContext, 1.61 + const nsSize& aAvailableSpace, 1.62 + uint32_t aFlags) 1.63 + : nsCSSOffsetState(aFrame, aRenderingContext) 1.64 + , mBlockDelta(0) 1.65 + , mReflowDepth(0) 1.66 +{ 1.67 + NS_PRECONDITION(aRenderingContext, "no rendering context"); 1.68 + MOZ_ASSERT(aPresContext, "no pres context"); 1.69 + MOZ_ASSERT(aFrame, "no frame"); 1.70 + MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); 1.71 + parentReflowState = nullptr; 1.72 + AvailableWidth() = aAvailableSpace.width; 1.73 + AvailableHeight() = aAvailableSpace.height; 1.74 + mFloatManager = nullptr; 1.75 + mLineLayout = nullptr; 1.76 + memset(&mFlags, 0, sizeof(mFlags)); 1.77 + mDiscoveredClearance = nullptr; 1.78 + mPercentHeightObserver = nullptr; 1.79 + 1.80 + if (aFlags & DUMMY_PARENT_REFLOW_STATE) { 1.81 + mFlags.mDummyParentReflowState = true; 1.82 + } 1.83 + 1.84 + if (!(aFlags & CALLER_WILL_INIT)) { 1.85 + Init(aPresContext); 1.86 + } 1.87 +} 1.88 + 1.89 +static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) 1.90 +{ 1.91 + nsIFrame* frameNext = aFrame->GetNextInFlow(); 1.92 + nsIFrame* parentNext = aParent->GetNextInFlow(); 1.93 + return frameNext && parentNext && frameNext->GetParent() == parentNext; 1.94 +} 1.95 + 1.96 +/** 1.97 + * Adjusts the margin for a list (ol, ul), if necessary, depending on 1.98 + * font inflation settings. Unfortunately, because bullets from a list are 1.99 + * placed in the margin area, we only have ~40px in which to place the 1.100 + * bullets. When they are inflated, however, this causes problems, since 1.101 + * the text takes up more space than is available in the margin. 1.102 + * 1.103 + * This method will return a small amount (in app units) by which the 1.104 + * margin can be adjusted, so that the space is available for list 1.105 + * bullets to be rendered with font inflation enabled. 1.106 + */ 1.107 +static nscoord 1.108 +FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) 1.109 +{ 1.110 + float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); 1.111 + if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) { 1.112 + const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame); 1.113 + const nsStyleList* styleList = aFrame->StyleList(); 1.114 + 1.115 + // We only want to adjust the margins if we're dealing with an ordered 1.116 + // list. 1.117 + if (inflation > 1.0f && 1.118 + blockFrame->HasBullet() && 1.119 + styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE && 1.120 + styleList->mListStyleType != NS_STYLE_LIST_STYLE_DISC && 1.121 + styleList->mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE && 1.122 + styleList->mListStyleType != NS_STYLE_LIST_STYLE_SQUARE && 1.123 + inflation > 1.0f) { 1.124 + 1.125 + // The HTML spec states that the default padding for ordered lists begins 1.126 + // at 40px, indicating that we have 40px of space to place a bullet. When 1.127 + // performing font inflation calculations, we add space equivalent to this, 1.128 + // but simply inflated at the same amount as the text, in app units. 1.129 + return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); 1.130 + } 1.131 + } 1.132 + 1.133 + return 0; 1.134 +} 1.135 + 1.136 +// NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid 1.137 +// item, we need to make it take the containing-block height as well as the 1.138 +// width, since flex items and grid items resolve vertical percent margins 1.139 +// and padding against the containing-block height, rather than its width. 1.140 +nsCSSOffsetState::nsCSSOffsetState(nsIFrame *aFrame, 1.141 + nsRenderingContext *aRenderingContext, 1.142 + nscoord aContainingBlockWidth) 1.143 + : frame(aFrame) 1.144 + , rendContext(aRenderingContext) 1.145 + , mWritingMode(aFrame->GetWritingMode()) 1.146 +{ 1.147 + MOZ_ASSERT(!aFrame->IsFlexItem(), 1.148 + "We're about to resolve vertical percent margin & padding " 1.149 + "values against CB width, which is incorrect for flex items"); 1.150 + InitOffsets(aContainingBlockWidth, aContainingBlockWidth, frame->GetType()); 1.151 +} 1.152 + 1.153 +// Initialize a reflow state for a child frame's reflow. Some state 1.154 +// is copied from the parent reflow state; the remaining state is 1.155 +// computed. 1.156 +nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, 1.157 + const nsHTMLReflowState& aParentReflowState, 1.158 + nsIFrame* aFrame, 1.159 + const nsSize& aAvailableSpace, 1.160 + nscoord aContainingBlockWidth, 1.161 + nscoord aContainingBlockHeight, 1.162 + uint32_t aFlags) 1.163 + : nsCSSOffsetState(aFrame, aParentReflowState.rendContext) 1.164 + , mBlockDelta(0) 1.165 + , mReflowDepth(aParentReflowState.mReflowDepth + 1) 1.166 + , mFlags(aParentReflowState.mFlags) 1.167 +{ 1.168 + MOZ_ASSERT(aPresContext, "no pres context"); 1.169 + MOZ_ASSERT(aFrame, "no frame"); 1.170 + MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); 1.171 + NS_PRECONDITION((aContainingBlockWidth == -1) == 1.172 + (aContainingBlockHeight == -1), 1.173 + "cb width and height should only be non-default together"); 1.174 + NS_PRECONDITION(!mFlags.mSpecialHeightReflow || 1.175 + !NS_SUBTREE_DIRTY(aFrame), 1.176 + "frame should be clean when getting special height reflow"); 1.177 + 1.178 + parentReflowState = &aParentReflowState; 1.179 + 1.180 + // If the parent is dirty, then the child is as well. 1.181 + // XXX Are the other cases where the parent reflows a child a second 1.182 + // time, as a resize? 1.183 + if (!mFlags.mSpecialHeightReflow) 1.184 + frame->AddStateBits(parentReflowState->frame->GetStateBits() & 1.185 + NS_FRAME_IS_DIRTY); 1.186 + 1.187 + AvailableWidth() = aAvailableSpace.width; 1.188 + AvailableHeight() = aAvailableSpace.height; 1.189 + 1.190 + mFloatManager = aParentReflowState.mFloatManager; 1.191 + if (frame->IsFrameOfType(nsIFrame::eLineParticipant)) 1.192 + mLineLayout = aParentReflowState.mLineLayout; 1.193 + else 1.194 + mLineLayout = nullptr; 1.195 + 1.196 + // Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in 1.197 + // this constructor's init list, so the only flags that we need to explicitly 1.198 + // initialize here are those that may need a value other than our parent's. 1.199 + mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched && 1.200 + CheckNextInFlowParenthood(aFrame, aParentReflowState.frame); 1.201 + mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; 1.202 + mFlags.mHasClearance = false; 1.203 + mFlags.mIsColumnBalancing = false; 1.204 + mFlags.mIsFlexContainerMeasuringHeight = false; 1.205 + mFlags.mDummyParentReflowState = false; 1.206 + 1.207 + mDiscoveredClearance = nullptr; 1.208 + mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 1.209 + aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 1.210 + ? aParentReflowState.mPercentHeightObserver : nullptr; 1.211 + 1.212 + if (aFlags & DUMMY_PARENT_REFLOW_STATE) { 1.213 + mFlags.mDummyParentReflowState = true; 1.214 + } 1.215 + 1.216 + if (!(aFlags & CALLER_WILL_INIT)) { 1.217 + Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight); 1.218 + } 1.219 +} 1.220 + 1.221 +inline nscoord 1.222 +nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, 1.223 + nscoord aContentEdgeToBoxSizing, 1.224 + nscoord aBoxSizingToMarginEdge, 1.225 + const nsStyleCoord& aCoord) 1.226 +{ 1.227 + return nsLayoutUtils::ComputeWidthValue(rendContext, frame, 1.228 + aContainingBlockWidth, 1.229 + aContentEdgeToBoxSizing, 1.230 + aBoxSizingToMarginEdge, 1.231 + aCoord); 1.232 +} 1.233 + 1.234 +nscoord 1.235 +nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, 1.236 + uint8_t aBoxSizing, 1.237 + const nsStyleCoord& aCoord) 1.238 +{ 1.239 + nscoord inside = 0, outside = ComputedPhysicalBorderPadding().LeftRight() + 1.240 + ComputedPhysicalMargin().LeftRight(); 1.241 + switch (aBoxSizing) { 1.242 + case NS_STYLE_BOX_SIZING_BORDER: 1.243 + inside = ComputedPhysicalBorderPadding().LeftRight(); 1.244 + break; 1.245 + case NS_STYLE_BOX_SIZING_PADDING: 1.246 + inside = ComputedPhysicalPadding().LeftRight(); 1.247 + break; 1.248 + } 1.249 + outside -= inside; 1.250 + 1.251 + return ComputeWidthValue(aContainingBlockWidth, inside, 1.252 + outside, aCoord); 1.253 +} 1.254 + 1.255 +nscoord 1.256 +nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight, 1.257 + uint8_t aBoxSizing, 1.258 + const nsStyleCoord& aCoord) 1.259 +{ 1.260 + nscoord inside = 0; 1.261 + switch (aBoxSizing) { 1.262 + case NS_STYLE_BOX_SIZING_BORDER: 1.263 + inside = ComputedPhysicalBorderPadding().TopBottom(); 1.264 + break; 1.265 + case NS_STYLE_BOX_SIZING_PADDING: 1.266 + inside = ComputedPhysicalPadding().TopBottom(); 1.267 + break; 1.268 + } 1.269 + return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight, 1.270 + inside, aCoord); 1.271 +} 1.272 + 1.273 +void 1.274 +nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth) 1.275 +{ 1.276 + NS_ASSERTION(frame, "Must have a frame!"); 1.277 + // It'd be nice to assert that |frame| is not in reflow, but this fails for 1.278 + // two reasons: 1.279 + // 1.280 + // 1) Viewport frames reset the computed width on a copy of their reflow 1.281 + // state when reflowing fixed-pos kids. In that case we actually don't 1.282 + // want to mess with the resize flags, because comparing the frame's rect 1.283 + // to the munged computed width is pointless. 1.284 + // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow 1.285 + // state is not used to reflow the parent, but just as a parent for the 1.286 + // frame's own reflow state. So given a nsBoxFrame inside some non-XUL 1.287 + // (like a text control, for example), we'll end up creating a reflow 1.288 + // state for the parent while the parent is reflowing. 1.289 + 1.290 + NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width"); 1.291 + if (ComputedWidth() != aComputedWidth) { 1.292 + ComputedWidth() = aComputedWidth; 1.293 + nsIAtom* frameType = frame->GetType(); 1.294 + if (frameType != nsGkAtoms::viewportFrame) { // Or check GetParent()? 1.295 + InitResizeFlags(frame->PresContext(), frameType); 1.296 + } 1.297 + } 1.298 +} 1.299 + 1.300 +void 1.301 +nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight) 1.302 +{ 1.303 + NS_ASSERTION(frame, "Must have a frame!"); 1.304 + // It'd be nice to assert that |frame| is not in reflow, but this fails 1.305 + // because: 1.306 + // 1.307 + // nsFrame::BoxReflow creates a reflow state for its parent. This reflow 1.308 + // state is not used to reflow the parent, but just as a parent for the 1.309 + // frame's own reflow state. So given a nsBoxFrame inside some non-XUL 1.310 + // (like a text control, for example), we'll end up creating a reflow 1.311 + // state for the parent while the parent is reflowing. 1.312 + 1.313 + NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height"); 1.314 + if (ComputedHeight() != aComputedHeight) { 1.315 + ComputedHeight() = aComputedHeight; 1.316 + InitResizeFlags(frame->PresContext(), frame->GetType()); 1.317 + } 1.318 +} 1.319 + 1.320 +void 1.321 +nsHTMLReflowState::Init(nsPresContext* aPresContext, 1.322 + nscoord aContainingBlockWidth, 1.323 + nscoord aContainingBlockHeight, 1.324 + const nsMargin* aBorder, 1.325 + const nsMargin* aPadding) 1.326 +{ 1.327 + NS_WARN_IF_FALSE(AvailableWidth() != NS_UNCONSTRAINEDSIZE, 1.328 + "have unconstrained width; this should only result from " 1.329 + "very large sizes, not attempts at intrinsic width " 1.330 + "calculation"); 1.331 + 1.332 + mStylePosition = frame->StylePosition(); 1.333 + mStyleDisplay = frame->StyleDisplay(); 1.334 + mStyleVisibility = frame->StyleVisibility(); 1.335 + mStyleBorder = frame->StyleBorder(); 1.336 + mStyleMargin = frame->StyleMargin(); 1.337 + mStylePadding = frame->StylePadding(); 1.338 + mStyleText = frame->StyleText(); 1.339 + 1.340 + nsIAtom* type = frame->GetType(); 1.341 + 1.342 + InitFrameType(type); 1.343 + InitCBReflowState(); 1.344 + 1.345 + InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, 1.346 + aBorder, aPadding, type); 1.347 + 1.348 + InitResizeFlags(aPresContext, type); 1.349 + 1.350 + nsIFrame *parent = frame->GetParent(); 1.351 + if (parent && 1.352 + (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) && 1.353 + !(parent->GetType() == nsGkAtoms::scrollFrame && 1.354 + parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) { 1.355 + frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.356 + } else if (type == nsGkAtoms::svgForeignObjectFrame) { 1.357 + // An SVG foreignObject frame is inherently constrained height. 1.358 + frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.359 + } else if ((mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto || 1.360 + mStylePosition->mMaxHeight.GetUnit() != eStyleUnit_None) && 1.361 + // Don't set NS_FRAME_IN_CONSTRAINED_HEIGHT on body or html 1.362 + // elements. 1.363 + (frame->GetContent() && 1.364 + !(frame->GetContent()->IsHTML(nsGkAtoms::body) || 1.365 + frame->GetContent()->IsHTML(nsGkAtoms::html)))) { 1.366 + 1.367 + // If our height was specified as a percentage, then this could 1.368 + // actually resolve to 'auto', based on: 1.369 + // http://www.w3.org/TR/CSS21/visudet.html#the-height-property 1.370 + nsIFrame* containingBlk = frame; 1.371 + while (containingBlk) { 1.372 + const nsStylePosition* stylePos = containingBlk->StylePosition(); 1.373 + if ((stylePos->mHeight.IsCoordPercentCalcUnit() && 1.374 + !stylePos->mHeight.HasPercent()) || 1.375 + (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && 1.376 + !stylePos->mMaxHeight.HasPercent())) { 1.377 + frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.378 + break; 1.379 + } else if ((stylePos->mHeight.IsCoordPercentCalcUnit() && 1.380 + stylePos->mHeight.HasPercent()) || 1.381 + (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && 1.382 + stylePos->mMaxHeight.HasPercent())) { 1.383 + if (!(containingBlk = containingBlk->GetContainingBlock())) { 1.384 + // If we've reached the top of the tree, then we don't have 1.385 + // a constrained height. 1.386 + frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.387 + break; 1.388 + } 1.389 + 1.390 + continue; 1.391 + } else { 1.392 + frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.393 + break; 1.394 + } 1.395 + } 1.396 + } else { 1.397 + frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); 1.398 + } 1.399 + 1.400 + NS_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE && 1.401 + !frame->IsFrameOfType(nsIFrame::eReplaced)) || 1.402 + type == nsGkAtoms::textFrame || 1.403 + ComputedWidth() != NS_UNCONSTRAINEDSIZE, 1.404 + "have unconstrained width; this should only result from " 1.405 + "very large sizes, not attempts at intrinsic width " 1.406 + "calculation"); 1.407 +} 1.408 + 1.409 +void nsHTMLReflowState::InitCBReflowState() 1.410 +{ 1.411 + if (!parentReflowState) { 1.412 + mCBReflowState = nullptr; 1.413 + return; 1.414 + } 1.415 + 1.416 + if (parentReflowState->frame == frame->GetContainingBlock()) { 1.417 + // Inner table frames need to use the containing block of the outer 1.418 + // table frame. 1.419 + if (frame->GetType() == nsGkAtoms::tableFrame) { 1.420 + mCBReflowState = parentReflowState->mCBReflowState; 1.421 + } else { 1.422 + mCBReflowState = parentReflowState; 1.423 + } 1.424 + } else { 1.425 + mCBReflowState = parentReflowState->mCBReflowState; 1.426 + } 1.427 +} 1.428 + 1.429 +/* Check whether CalcQuirkContainingBlockHeight would stop on the 1.430 + * given reflow state, using its block as a height. (essentially 1.431 + * returns false for any case in which CalcQuirkContainingBlockHeight 1.432 + * has a "continue" in its main loop.) 1.433 + * 1.434 + * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses 1.435 + * this function as well 1.436 + */ 1.437 +static bool 1.438 +IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType) 1.439 +{ 1.440 + if (nsGkAtoms::blockFrame == aFrameType || 1.441 +#ifdef MOZ_XUL 1.442 + nsGkAtoms::XULLabelFrame == aFrameType || 1.443 +#endif 1.444 + nsGkAtoms::scrollFrame == aFrameType) { 1.445 + // Note: This next condition could change due to a style change, 1.446 + // but that would cause a style reflow anyway, which means we're ok. 1.447 + if (NS_AUTOHEIGHT == rs->ComputedHeight()) { 1.448 + if (!rs->frame->IsAbsolutelyPositioned()) { 1.449 + return false; 1.450 + } 1.451 + } 1.452 + } 1.453 + return true; 1.454 +} 1.455 + 1.456 + 1.457 +void 1.458 +nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType) 1.459 +{ 1.460 + bool isHResize = (frame->GetSize().width != 1.461 + ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) || 1.462 + aPresContext->PresShell()->IsReflowOnZoomPending(); 1.463 + 1.464 + if ((frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) && 1.465 + nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { 1.466 + // Create our font inflation data if we don't have it already, and 1.467 + // give it our current width information. 1.468 + bool dirty = nsFontInflationData::UpdateFontInflationDataWidthFor(*this) && 1.469 + // Avoid running this at the box-to-block interface 1.470 + // (where we shouldn't be inflating anyway, and where 1.471 + // reflow state construction is probably to construct a 1.472 + // dummy parent reflow state anyway). 1.473 + !mFlags.mDummyParentReflowState; 1.474 + 1.475 + if (dirty || (!frame->GetParent() && isHResize)) { 1.476 + // When font size inflation is enabled, a change in either: 1.477 + // * the effective width of a font inflation flow root 1.478 + // * the width of the frame 1.479 + // needs to cause a dirty reflow since they change the font size 1.480 + // inflation calculations, which in turn change the size of text, 1.481 + // line-heights, etc. This is relatively similar to a classic 1.482 + // case of style change reflow, except that because inflation 1.483 + // doesn't affect the intrinsic sizing codepath, there's no need 1.484 + // to invalidate intrinsic sizes. 1.485 + // 1.486 + // Note that this makes horizontal resizing a good bit more 1.487 + // expensive. However, font size inflation is targeted at a set of 1.488 + // devices (zoom-and-pan devices) where the main use case for 1.489 + // horizontal resizing needing to be efficient (window resizing) is 1.490 + // not present. It does still increase the cost of dynamic changes 1.491 + // caused by script where a style or content change in one place 1.492 + // causes a resize in another (e.g., rebalancing a table). 1.493 + 1.494 + // FIXME: This isn't so great for the cases where 1.495 + // nsHTMLReflowState::SetComputedWidth is called, if the first time 1.496 + // we go through InitResizeFlags we set mHResize to true, and then 1.497 + // the second time we'd set it to false even without the 1.498 + // NS_FRAME_IS_DIRTY bit already set. 1.499 + if (frame->GetType() == nsGkAtoms::svgForeignObjectFrame) { 1.500 + // Foreign object frames use dirty bits in a special way. 1.501 + frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 1.502 + nsIFrame *kid = frame->GetFirstPrincipalChild(); 1.503 + if (kid) { 1.504 + kid->AddStateBits(NS_FRAME_IS_DIRTY); 1.505 + } 1.506 + } else { 1.507 + frame->AddStateBits(NS_FRAME_IS_DIRTY); 1.508 + } 1.509 + 1.510 + // Mark intrinsic widths on all descendants dirty. We need to do 1.511 + // this (1) since we're changing the size of text and need to 1.512 + // clear text runs on text frames and (2) since we actually are 1.513 + // changing some intrinsic widths, but only those that live inside 1.514 + // of containers. 1.515 + 1.516 + // It makes sense to do this for descendants but not ancestors 1.517 + // (which is unusual) because we're only changing the unusual 1.518 + // inflation-dependent intrinsic widths (i.e., ones computed with 1.519 + // nsPresContext::mInflationDisabledForShrinkWrap set to false), 1.520 + // which should never affect anything outside of their inflation 1.521 + // flow root (or, for that matter, even their inflation 1.522 + // container). 1.523 + 1.524 + // This is also different from what PresShell::FrameNeedsReflow 1.525 + // does because it doesn't go through placeholders. It doesn't 1.526 + // need to because we're actually doing something that cares about 1.527 + // frame tree geometry (the width on an ancestor) rather than 1.528 + // style. 1.529 + 1.530 + nsAutoTArray<nsIFrame*, 32> stack; 1.531 + stack.AppendElement(frame); 1.532 + 1.533 + do { 1.534 + nsIFrame *f = stack.ElementAt(stack.Length() - 1); 1.535 + stack.RemoveElementAt(stack.Length() - 1); 1.536 + 1.537 + nsIFrame::ChildListIterator lists(f); 1.538 + for (; !lists.IsDone(); lists.Next()) { 1.539 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.540 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.541 + nsIFrame* kid = childFrames.get(); 1.542 + kid->MarkIntrinsicWidthsDirty(); 1.543 + stack.AppendElement(kid); 1.544 + } 1.545 + } 1.546 + } while (stack.Length() != 0); 1.547 + } 1.548 + } 1.549 + 1.550 + mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) && 1.551 + isHResize; 1.552 + 1.553 + // XXX Should we really need to null check mCBReflowState? (We do for 1.554 + // at least nsBoxFrame). 1.555 + if (IS_TABLE_CELL(aFrameType) && 1.556 + (mFlags.mSpecialHeightReflow || 1.557 + (frame->FirstInFlow()->GetStateBits() & 1.558 + NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && 1.559 + (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { 1.560 + // Need to set the bit on the cell so that 1.561 + // mCBReflowState->mFlags.mVResize is set correctly below when 1.562 + // reflowing descendant. 1.563 + mFlags.mVResize = true; 1.564 + } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) { 1.565 + // XXX Is this problematic for relatively positioned inlines acting 1.566 + // as containing block for absolutely positioned elements? 1.567 + // Possibly; in that case we should at least be checking 1.568 + // NS_SUBTREE_DIRTY, I'd think. 1.569 + mFlags.mVResize = mCBReflowState->mFlags.mVResize; 1.570 + } else if (ComputedHeight() == NS_AUTOHEIGHT) { 1.571 + if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 1.572 + mCBReflowState) { 1.573 + mFlags.mVResize = mCBReflowState->mFlags.mVResize; 1.574 + } else { 1.575 + mFlags.mVResize = mFlags.mHResize; 1.576 + } 1.577 + mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame); 1.578 + } else { 1.579 + // not 'auto' height 1.580 + mFlags.mVResize = frame->GetSize().height != 1.581 + ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom(); 1.582 + } 1.583 + 1.584 + bool dependsOnCBHeight = 1.585 + (mStylePosition->HeightDependsOnContainer() && 1.586 + // FIXME: condition this on not-abspos? 1.587 + mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) || 1.588 + mStylePosition->MinHeightDependsOnContainer() || 1.589 + mStylePosition->MaxHeightDependsOnContainer() || 1.590 + mStylePosition->OffsetHasPercent(NS_SIDE_TOP) || 1.591 + mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto || 1.592 + frame->IsBoxFrame(); 1.593 + 1.594 + if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { 1.595 + NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() == 1.596 + NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, 1.597 + "bad line-height value"); 1.598 + 1.599 + // line-height depends on block height 1.600 + frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.601 + // but only on containing blocks if this frame is not a suitable block 1.602 + dependsOnCBHeight |= !nsLayoutUtils::IsNonWrapperBlock(frame); 1.603 + } 1.604 + 1.605 + // If we're the descendant of a table cell that performs special height 1.606 + // reflows and we could be the child that requires them, always set 1.607 + // the vertical resize in case this is the first pass before the 1.608 + // special height reflow. However, don't do this if it actually is 1.609 + // the special height reflow, since in that case it will already be 1.610 + // set correctly above if we need it set. 1.611 + if (!mFlags.mVResize && mCBReflowState && 1.612 + (IS_TABLE_CELL(mCBReflowState->frame->GetType()) || 1.613 + mCBReflowState->mFlags.mHeightDependsOnAncestorCell) && 1.614 + !mCBReflowState->mFlags.mSpecialHeightReflow && 1.615 + dependsOnCBHeight) { 1.616 + mFlags.mVResize = true; 1.617 + mFlags.mHeightDependsOnAncestorCell = true; 1.618 + } 1.619 + 1.620 + // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed. 1.621 + 1.622 + // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT| 1.623 + // &&ed with the percentage height check. However, this doesn't get 1.624 + // along with table special height reflows, since a special height 1.625 + // reflow (a quirk that makes such percentage heights work on children 1.626 + // of table cells) can cause not just a single percentage height to 1.627 + // become fixed, but an entire descendant chain of percentage heights 1.628 + // to become fixed. 1.629 + if (dependsOnCBHeight && mCBReflowState) { 1.630 + const nsHTMLReflowState *rs = this; 1.631 + bool hitCBReflowState = false; 1.632 + do { 1.633 + rs = rs->parentReflowState; 1.634 + if (!rs) { 1.635 + break; 1.636 + } 1.637 + 1.638 + if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) 1.639 + break; // no need to go further 1.640 + rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.641 + 1.642 + // Keep track of whether we've hit the containing block, because 1.643 + // we need to go at least that far. 1.644 + if (rs == mCBReflowState) { 1.645 + hitCBReflowState = true; 1.646 + } 1.647 + 1.648 + } while (!hitCBReflowState || 1.649 + (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 1.650 + !IsQuirkContainingBlockHeight(rs, rs->frame->GetType()))); 1.651 + // Note: We actually don't need to set the 1.652 + // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases 1.653 + // where we hit the early break statements in 1.654 + // CalcQuirkContainingBlockHeight. But it doesn't hurt 1.655 + // us to set the bit in these cases. 1.656 + 1.657 + } 1.658 + if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) { 1.659 + // If we're reflowing everything, then we'll find out if we need 1.660 + // to re-set this. 1.661 + frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.662 + } 1.663 +} 1.664 + 1.665 +/* static */ 1.666 +nscoord 1.667 +nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState) 1.668 +{ 1.669 + const nsHTMLReflowState* rs = aReflowState->mCBReflowState; 1.670 + if (!rs) 1.671 + return 0; 1.672 + return rs->ComputedWidth(); 1.673 +} 1.674 + 1.675 +void 1.676 +nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) 1.677 +{ 1.678 + const nsStyleDisplay *disp = mStyleDisplay; 1.679 + nsCSSFrameType frameType; 1.680 + 1.681 + // Section 9.7 of the CSS2 spec indicates that absolute position 1.682 + // takes precedence over float which takes precedence over display. 1.683 + // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right? 1.684 + // Make sure the frame was actually moved out of the flow, and don't 1.685 + // just assume what the style says, because we might not have had a 1.686 + // useful float/absolute containing block 1.687 + 1.688 + DISPLAY_INIT_TYPE(frame, this); 1.689 + 1.690 + if (aFrameType == nsGkAtoms::tableFrame) { 1.691 + mFrameType = NS_CSS_FRAME_TYPE_BLOCK; 1.692 + return; 1.693 + } 1.694 + 1.695 + NS_ASSERTION(frame->StyleDisplay()->IsAbsolutelyPositionedStyle() == 1.696 + disp->IsAbsolutelyPositionedStyle(), 1.697 + "Unexpected position style"); 1.698 + NS_ASSERTION(frame->StyleDisplay()->IsFloatingStyle() == 1.699 + disp->IsFloatingStyle(), "Unexpected float style"); 1.700 + if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { 1.701 + if (disp->IsAbsolutelyPositioned(frame)) { 1.702 + frameType = NS_CSS_FRAME_TYPE_ABSOLUTE; 1.703 + //XXXfr hack for making frames behave properly when in overflow container lists 1.704 + // see bug 154892; need to revisit later 1.705 + if (frame->GetPrevInFlow()) 1.706 + frameType = NS_CSS_FRAME_TYPE_BLOCK; 1.707 + } 1.708 + else if (disp->IsFloating(frame)) { 1.709 + frameType = NS_CSS_FRAME_TYPE_FLOATING; 1.710 + } else { 1.711 + NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP, 1.712 + "unknown out of flow frame type"); 1.713 + frameType = NS_CSS_FRAME_TYPE_UNKNOWN; 1.714 + } 1.715 + } 1.716 + else { 1.717 + switch (GetDisplay()) { 1.718 + case NS_STYLE_DISPLAY_BLOCK: 1.719 + case NS_STYLE_DISPLAY_LIST_ITEM: 1.720 + case NS_STYLE_DISPLAY_TABLE: 1.721 + case NS_STYLE_DISPLAY_TABLE_CAPTION: 1.722 + case NS_STYLE_DISPLAY_FLEX: 1.723 + frameType = NS_CSS_FRAME_TYPE_BLOCK; 1.724 + break; 1.725 + 1.726 + case NS_STYLE_DISPLAY_INLINE: 1.727 + case NS_STYLE_DISPLAY_INLINE_BLOCK: 1.728 + case NS_STYLE_DISPLAY_INLINE_TABLE: 1.729 + case NS_STYLE_DISPLAY_INLINE_BOX: 1.730 + case NS_STYLE_DISPLAY_INLINE_XUL_GRID: 1.731 + case NS_STYLE_DISPLAY_INLINE_STACK: 1.732 + case NS_STYLE_DISPLAY_INLINE_FLEX: 1.733 + frameType = NS_CSS_FRAME_TYPE_INLINE; 1.734 + break; 1.735 + 1.736 + case NS_STYLE_DISPLAY_TABLE_CELL: 1.737 + case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: 1.738 + case NS_STYLE_DISPLAY_TABLE_COLUMN: 1.739 + case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP: 1.740 + case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: 1.741 + case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: 1.742 + case NS_STYLE_DISPLAY_TABLE_ROW: 1.743 + frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE; 1.744 + break; 1.745 + 1.746 + case NS_STYLE_DISPLAY_NONE: 1.747 + default: 1.748 + frameType = NS_CSS_FRAME_TYPE_UNKNOWN; 1.749 + break; 1.750 + } 1.751 + } 1.752 + 1.753 + // See if the frame is replaced 1.754 + if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) { 1.755 + frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType); 1.756 + } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) { 1.757 + frameType = NS_FRAME_REPLACED(frameType); 1.758 + } 1.759 + 1.760 + mFrameType = frameType; 1.761 +} 1.762 + 1.763 +/* static */ void 1.764 +nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection, 1.765 + nsIFrame* aFrame, 1.766 + nscoord aContainingBlockWidth, 1.767 + nscoord aContainingBlockHeight, 1.768 + nsMargin& aComputedOffsets) 1.769 +{ 1.770 + const nsStylePosition* position = aFrame->StylePosition(); 1.771 + 1.772 + // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right, 1.773 + // and 'right' moves the boxes to the left. The computed values are always: 1.774 + // left=-right 1.775 + bool leftIsAuto = eStyleUnit_Auto == position->mOffset.GetLeftUnit(); 1.776 + bool rightIsAuto = eStyleUnit_Auto == position->mOffset.GetRightUnit(); 1.777 + 1.778 + // If neither 'left' not 'right' are auto, then we're over-constrained and 1.779 + // we ignore one of them 1.780 + if (!leftIsAuto && !rightIsAuto) { 1.781 + if (aCBDirection == NS_STYLE_DIRECTION_RTL) { 1.782 + leftIsAuto = true; 1.783 + } else { 1.784 + rightIsAuto = true; 1.785 + } 1.786 + } 1.787 + 1.788 + if (leftIsAuto) { 1.789 + if (rightIsAuto) { 1.790 + // If both are 'auto' (their initial values), the computed values are 0 1.791 + aComputedOffsets.left = aComputedOffsets.right = 0; 1.792 + } else { 1.793 + // 'Right' isn't 'auto' so compute its value 1.794 + aComputedOffsets.right = nsLayoutUtils:: 1.795 + ComputeCBDependentValue(aContainingBlockWidth, 1.796 + position->mOffset.GetRight()); 1.797 + 1.798 + // Computed value for 'left' is minus the value of 'right' 1.799 + aComputedOffsets.left = -aComputedOffsets.right; 1.800 + } 1.801 + 1.802 + } else { 1.803 + NS_ASSERTION(rightIsAuto, "unexpected specified constraint"); 1.804 + 1.805 + // 'Left' isn't 'auto' so compute its value 1.806 + aComputedOffsets.left = nsLayoutUtils:: 1.807 + ComputeCBDependentValue(aContainingBlockWidth, 1.808 + position->mOffset.GetLeft()); 1.809 + 1.810 + // Computed value for 'right' is minus the value of 'left' 1.811 + aComputedOffsets.right = -aComputedOffsets.left; 1.812 + } 1.813 + 1.814 + // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties 1.815 + // move relatively positioned elements up and down. They also must be each 1.816 + // other's negative 1.817 + bool topIsAuto = eStyleUnit_Auto == position->mOffset.GetTopUnit(); 1.818 + bool bottomIsAuto = eStyleUnit_Auto == position->mOffset.GetBottomUnit(); 1.819 + 1.820 + // Check for percentage based values and a containing block height that 1.821 + // depends on the content height. Treat them like 'auto' 1.822 + if (NS_AUTOHEIGHT == aContainingBlockHeight) { 1.823 + if (position->OffsetHasPercent(NS_SIDE_TOP)) { 1.824 + topIsAuto = true; 1.825 + } 1.826 + if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) { 1.827 + bottomIsAuto = true; 1.828 + } 1.829 + } 1.830 + 1.831 + // If neither is 'auto', 'bottom' is ignored 1.832 + if (!topIsAuto && !bottomIsAuto) { 1.833 + bottomIsAuto = true; 1.834 + } 1.835 + 1.836 + if (topIsAuto) { 1.837 + if (bottomIsAuto) { 1.838 + // If both are 'auto' (their initial values), the computed values are 0 1.839 + aComputedOffsets.top = aComputedOffsets.bottom = 0; 1.840 + } else { 1.841 + // 'Bottom' isn't 'auto' so compute its value 1.842 + aComputedOffsets.bottom = nsLayoutUtils:: 1.843 + ComputeHeightDependentValue(aContainingBlockHeight, 1.844 + position->mOffset.GetBottom()); 1.845 + 1.846 + // Computed value for 'top' is minus the value of 'bottom' 1.847 + aComputedOffsets.top = -aComputedOffsets.bottom; 1.848 + } 1.849 + 1.850 + } else { 1.851 + NS_ASSERTION(bottomIsAuto, "unexpected specified constraint"); 1.852 + 1.853 + // 'Top' isn't 'auto' so compute its value 1.854 + aComputedOffsets.top = nsLayoutUtils:: 1.855 + ComputeHeightDependentValue(aContainingBlockHeight, 1.856 + position->mOffset.GetTop()); 1.857 + 1.858 + // Computed value for 'bottom' is minus the value of 'top' 1.859 + aComputedOffsets.bottom = -aComputedOffsets.top; 1.860 + } 1.861 + 1.862 + // Store the offset 1.863 + FrameProperties props = aFrame->Properties(); 1.864 + nsMargin* offsets = static_cast<nsMargin*> 1.865 + (props.Get(nsIFrame::ComputedOffsetProperty())); 1.866 + if (offsets) { 1.867 + *offsets = aComputedOffsets; 1.868 + } else { 1.869 + props.Set(nsIFrame::ComputedOffsetProperty(), 1.870 + new nsMargin(aComputedOffsets)); 1.871 + } 1.872 +} 1.873 + 1.874 +/* static */ void 1.875 +nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, 1.876 + const nsMargin& aComputedOffsets, 1.877 + nsPoint* aPosition) 1.878 +{ 1.879 + if (!aFrame->IsRelativelyPositioned()) { 1.880 + NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()), 1.881 + "We assume that changing the 'position' property causes " 1.882 + "frame reconstruction. If that ever changes, this code " 1.883 + "should call " 1.884 + "props.Delete(nsIFrame::NormalPositionProperty())"); 1.885 + return; 1.886 + } 1.887 + 1.888 + // Store the normal position 1.889 + FrameProperties props = aFrame->Properties(); 1.890 + nsPoint* normalPosition = static_cast<nsPoint*> 1.891 + (props.Get(nsIFrame::NormalPositionProperty())); 1.892 + if (normalPosition) { 1.893 + *normalPosition = *aPosition; 1.894 + } else { 1.895 + props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition)); 1.896 + } 1.897 + 1.898 + const nsStyleDisplay* display = aFrame->StyleDisplay(); 1.899 + if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { 1.900 + *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); 1.901 + } else if (NS_STYLE_POSITION_STICKY == display->mPosition && 1.902 + !aFrame->GetNextContinuation() && 1.903 + !aFrame->GetPrevContinuation() && 1.904 + !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) { 1.905 + // Sticky positioning for elements with multiple frames needs to be 1.906 + // computed all at once. We can't safely do that here because we might be 1.907 + // partway through (re)positioning the frames, so leave it until the scroll 1.908 + // container reflows and calls StickyScrollContainer::UpdatePositions. 1.909 + // For single-frame sticky positioned elements, though, go ahead and apply 1.910 + // it now to avoid unnecessary overflow updates later. 1.911 + StickyScrollContainer* ssc = 1.912 + StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); 1.913 + if (ssc) { 1.914 + *aPosition = ssc->ComputePosition(aFrame); 1.915 + } 1.916 + } 1.917 +} 1.918 + 1.919 +nsIFrame* 1.920 +nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, 1.921 + nscoord& aCBLeftEdge, 1.922 + nscoord& aCBWidth) 1.923 +{ 1.924 + aFrame = aFrame->GetContainingBlock(); 1.925 + NS_ASSERTION(aFrame != frame, "How did that happen?"); 1.926 + 1.927 + /* Now aFrame is the containing block we want */ 1.928 + 1.929 + /* Check whether the containing block is currently being reflowed. 1.930 + If so, use the info from the reflow state. */ 1.931 + const nsHTMLReflowState* state; 1.932 + if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { 1.933 + for (state = parentReflowState; state && state->frame != aFrame; 1.934 + state = state->parentReflowState) { 1.935 + /* do nothing */ 1.936 + } 1.937 + } else { 1.938 + state = nullptr; 1.939 + } 1.940 + 1.941 + if (state) { 1.942 + aCBLeftEdge = state->ComputedPhysicalBorderPadding().left; 1.943 + aCBWidth = state->ComputedWidth(); 1.944 + } else { 1.945 + /* Didn't find a reflow state for aFrame. Just compute the information we 1.946 + want, on the assumption that aFrame already knows its size. This really 1.947 + ought to be true by now. */ 1.948 + NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), 1.949 + "aFrame shouldn't be in reflow; we'll lie if it is"); 1.950 + nsMargin borderPadding = aFrame->GetUsedBorderAndPadding(); 1.951 + aCBLeftEdge = borderPadding.left; 1.952 + aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight(); 1.953 + } 1.954 + 1.955 + return aFrame; 1.956 +} 1.957 + 1.958 +// When determining the hypothetical box that would have been if the element 1.959 +// had been in the flow we may not be able to exactly determine both the left 1.960 +// and right edges. For example, if the element is a non-replaced inline-level 1.961 +// element we would have to reflow it in order to determine it desired width. 1.962 +// In that case depending on the progression direction either the left or 1.963 +// right edge would be marked as not being exact 1.964 +struct nsHypotheticalBox { 1.965 + // offsets from left edge of containing block (which is a padding edge) 1.966 + nscoord mLeft, mRight; 1.967 + // offset from top edge of containing block (which is a padding edge) 1.968 + nscoord mTop; 1.969 +#ifdef DEBUG 1.970 + bool mLeftIsExact, mRightIsExact; 1.971 +#endif 1.972 + 1.973 + nsHypotheticalBox() { 1.974 +#ifdef DEBUG 1.975 + mLeftIsExact = mRightIsExact = false; 1.976 +#endif 1.977 + } 1.978 +}; 1.979 + 1.980 +static bool 1.981 +GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType) 1.982 +{ 1.983 + // See if it is an image frame 1.984 + bool success = false; 1.985 + 1.986 + // Currently the only type of replaced frame that we can get the intrinsic 1.987 + // size for is an image frame 1.988 + // XXX We should add back the GetReflowMetrics() function and one of the 1.989 + // things should be the intrinsic size... 1.990 + if (aFrameType == nsGkAtoms::imageFrame) { 1.991 + nsImageFrame* imageFrame = (nsImageFrame*)aFrame; 1.992 + 1.993 + if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) { 1.994 + success = (aIntrinsicSize != nsSize(0, 0)); 1.995 + } 1.996 + } 1.997 + return success; 1.998 +} 1.999 + 1.1000 +/** 1.1001 + * aInsideBoxSizing returns the part of the horizontal padding, border, 1.1002 + * and margin that goes inside the edge given by box-sizing; 1.1003 + * aOutsideBoxSizing returns the rest. 1.1004 + */ 1.1005 +void 1.1006 +nsHTMLReflowState::CalculateHorizBorderPaddingMargin( 1.1007 + nscoord aContainingBlockWidth, 1.1008 + nscoord* aInsideBoxSizing, 1.1009 + nscoord* aOutsideBoxSizing) 1.1010 +{ 1.1011 + const nsMargin& border = mStyleBorder->GetComputedBorder(); 1.1012 + nsMargin padding, margin; 1.1013 + 1.1014 + // See if the style system can provide us the padding directly 1.1015 + if (!mStylePadding->GetPadding(padding)) { 1.1016 + // We have to compute the left and right values 1.1017 + padding.left = nsLayoutUtils:: 1.1018 + ComputeCBDependentValue(aContainingBlockWidth, 1.1019 + mStylePadding->mPadding.GetLeft()); 1.1020 + padding.right = nsLayoutUtils:: 1.1021 + ComputeCBDependentValue(aContainingBlockWidth, 1.1022 + mStylePadding->mPadding.GetRight()); 1.1023 + } 1.1024 + 1.1025 + // See if the style system can provide us the margin directly 1.1026 + if (!mStyleMargin->GetMargin(margin)) { 1.1027 + // We have to compute the left and right values 1.1028 + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) { 1.1029 + // XXX FIXME (or does CalculateBlockSideMargins do this?) 1.1030 + margin.left = 0; // just ignore 1.1031 + } else { 1.1032 + margin.left = nsLayoutUtils:: 1.1033 + ComputeCBDependentValue(aContainingBlockWidth, 1.1034 + mStyleMargin->mMargin.GetLeft()); 1.1035 + } 1.1036 + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) { 1.1037 + // XXX FIXME (or does CalculateBlockSideMargins do this?) 1.1038 + margin.right = 0; // just ignore 1.1039 + } else { 1.1040 + margin.right = nsLayoutUtils:: 1.1041 + ComputeCBDependentValue(aContainingBlockWidth, 1.1042 + mStyleMargin->mMargin.GetRight()); 1.1043 + } 1.1044 + } 1.1045 + 1.1046 + nscoord outside = 1.1047 + padding.LeftRight() + border.LeftRight() + margin.LeftRight(); 1.1048 + nscoord inside = 0; 1.1049 + switch (mStylePosition->mBoxSizing) { 1.1050 + case NS_STYLE_BOX_SIZING_BORDER: 1.1051 + inside += border.LeftRight(); 1.1052 + // fall through 1.1053 + case NS_STYLE_BOX_SIZING_PADDING: 1.1054 + inside += padding.LeftRight(); 1.1055 + } 1.1056 + outside -= inside; 1.1057 + *aInsideBoxSizing = inside; 1.1058 + *aOutsideBoxSizing = outside; 1.1059 + return; 1.1060 +} 1.1061 + 1.1062 +/** 1.1063 + * Returns true iff a pre-order traversal of the normal child 1.1064 + * frames rooted at aFrame finds no non-empty frame before aDescendant. 1.1065 + */ 1.1066 +static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, 1.1067 + nsIFrame* aDescendant, bool* aFound) { 1.1068 + if (aFrame == aDescendant) { 1.1069 + *aFound = true; 1.1070 + return true; 1.1071 + } 1.1072 + if (!aFrame->IsSelfEmpty()) { 1.1073 + *aFound = false; 1.1074 + return false; 1.1075 + } 1.1076 + for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) { 1.1077 + bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); 1.1078 + if (*aFound || !allEmpty) { 1.1079 + return allEmpty; 1.1080 + } 1.1081 + } 1.1082 + *aFound = false; 1.1083 + return true; 1.1084 +} 1.1085 + 1.1086 +// Calculate the hypothetical box that the element would have if it were in 1.1087 +// the flow. The values returned are relative to the padding edge of the 1.1088 +// absolute containing block 1.1089 +// aContainingBlock is the placeholder's containing block (XXX rename it?) 1.1090 +// cbrs->frame is the actual containing block 1.1091 +void 1.1092 +nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, 1.1093 + nsIFrame* aPlaceholderFrame, 1.1094 + nsIFrame* aContainingBlock, 1.1095 + nscoord aBlockLeftContentEdge, 1.1096 + nscoord aBlockContentWidth, 1.1097 + const nsHTMLReflowState* cbrs, 1.1098 + nsHypotheticalBox& aHypotheticalBox, 1.1099 + nsIAtom* aFrameType) 1.1100 +{ 1.1101 + NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, 1.1102 + "mOriginalDisplay has not been properly initialized"); 1.1103 + 1.1104 + // If it's a replaced element and it has a 'auto' value for 'width', see if we 1.1105 + // can get the intrinsic size. This will allow us to exactly determine both the 1.1106 + // left and right edges 1.1107 + bool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto; 1.1108 + nsSize intrinsicSize; 1.1109 + bool knowIntrinsicSize = false; 1.1110 + if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { 1.1111 + // See if we can get the intrinsic size of the element 1.1112 + knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType); 1.1113 + } 1.1114 + 1.1115 + // See if we can calculate what the box width would have been if the 1.1116 + // element had been in the flow 1.1117 + nscoord boxWidth; 1.1118 + bool knowBoxWidth = false; 1.1119 + if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) && 1.1120 + !NS_FRAME_IS_REPLACED(mFrameType)) { 1.1121 + // For non-replaced inline-level elements the 'width' property doesn't apply, 1.1122 + // so we don't know what the width would have been without reflowing it 1.1123 + 1.1124 + } else { 1.1125 + // It's either a replaced inline-level element or a block-level element 1.1126 + 1.1127 + // Determine the total amount of horizontal border/padding/margin that 1.1128 + // the element would have had if it had been in the flow. Note that we 1.1129 + // ignore any 'auto' and 'inherit' values 1.1130 + nscoord insideBoxSizing, outsideBoxSizing; 1.1131 + CalculateHorizBorderPaddingMargin(aBlockContentWidth, 1.1132 + &insideBoxSizing, &outsideBoxSizing); 1.1133 + 1.1134 + if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { 1.1135 + // It's a replaced element with an 'auto' width so the box width is 1.1136 + // its intrinsic size plus any border/padding/margin 1.1137 + if (knowIntrinsicSize) { 1.1138 + boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing; 1.1139 + knowBoxWidth = true; 1.1140 + } 1.1141 + 1.1142 + } else if (isAutoWidth) { 1.1143 + // The box width is the containing block width 1.1144 + boxWidth = aBlockContentWidth; 1.1145 + knowBoxWidth = true; 1.1146 + 1.1147 + } else { 1.1148 + // We need to compute it. It's important we do this, because if it's 1.1149 + // percentage based this computed value may be different from the computed 1.1150 + // value calculated using the absolute containing block width 1.1151 + boxWidth = ComputeWidthValue(aBlockContentWidth, 1.1152 + insideBoxSizing, outsideBoxSizing, 1.1153 + mStylePosition->mWidth) + 1.1154 + insideBoxSizing + outsideBoxSizing; 1.1155 + knowBoxWidth = true; 1.1156 + } 1.1157 + } 1.1158 + 1.1159 + // Get the 'direction' of the block 1.1160 + const nsStyleVisibility* blockVis = aContainingBlock->StyleVisibility(); 1.1161 + 1.1162 + // Get the placeholder x-offset and y-offset in the coordinate 1.1163 + // space of its containing block 1.1164 + // XXXbz the placeholder is not fully reflowed yet if our containing block is 1.1165 + // relatively positioned... 1.1166 + nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock); 1.1167 + 1.1168 + // First, determine the hypothetical box's mTop. We want to check the 1.1169 + // content insertion frame of aContainingBlock for block-ness, but make 1.1170 + // sure to compute all coordinates in the coordinate system of 1.1171 + // aContainingBlock. 1.1172 + nsBlockFrame* blockFrame = 1.1173 + nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame()); 1.1174 + if (blockFrame) { 1.1175 + nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y; 1.1176 + bool isValid; 1.1177 + nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); 1.1178 + if (!isValid) { 1.1179 + // Give up. We're probably dealing with somebody using 1.1180 + // position:absolute inside native-anonymous content anyway. 1.1181 + aHypotheticalBox.mTop = placeholderOffset.y; 1.1182 + } else { 1.1183 + NS_ASSERTION(iter.GetContainer() == blockFrame, 1.1184 + "Found placeholder in wrong block!"); 1.1185 + nsBlockFrame::line_iterator lineBox = iter.GetLine(); 1.1186 + 1.1187 + // How we determine the hypothetical box depends on whether the element 1.1188 + // would have been inline-level or block-level 1.1189 + if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { 1.1190 + // Use the top of the inline box which the placeholder lives in 1.1191 + // as the hypothetical box's top. 1.1192 + aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; 1.1193 + } else { 1.1194 + // The element would have been block-level which means it would 1.1195 + // be below the line containing the placeholder frame, unless 1.1196 + // all the frames before it are empty. In that case, it would 1.1197 + // have been just before this line. 1.1198 + // XXXbz the line box is not fully reflowed yet if our 1.1199 + // containing block is relatively positioned... 1.1200 + if (lineBox != iter.End()) { 1.1201 + nsIFrame * firstFrame = lineBox->mFirstChild; 1.1202 + bool found = false; 1.1203 + bool allEmpty = true; 1.1204 + while (firstFrame) { // See bug 223064 1.1205 + allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, 1.1206 + aPlaceholderFrame, &found); 1.1207 + if (found || !allEmpty) 1.1208 + break; 1.1209 + firstFrame = firstFrame->GetNextSibling(); 1.1210 + } 1.1211 + NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); 1.1212 + 1.1213 + if (allEmpty) { 1.1214 + // The top of the hypothetical box is the top of the line 1.1215 + // containing the placeholder, since there is nothing in the 1.1216 + // line before our placeholder except empty frames. 1.1217 + aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; 1.1218 + } else { 1.1219 + // The top of the hypothetical box is just below the line 1.1220 + // containing the placeholder. 1.1221 + aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().YMost() + blockYOffset; 1.1222 + } 1.1223 + } else { 1.1224 + // Just use the placeholder's y-offset wrt the containing block 1.1225 + aHypotheticalBox.mTop = placeholderOffset.y; 1.1226 + } 1.1227 + } 1.1228 + } 1.1229 + } else { 1.1230 + // The containing block is not a block, so it's probably something 1.1231 + // like a XUL box, etc. 1.1232 + // Just use the placeholder's y-offset 1.1233 + aHypotheticalBox.mTop = placeholderOffset.y; 1.1234 + } 1.1235 + 1.1236 + // Second, determine the hypothetical box's mLeft & mRight 1.1237 + // To determine the left and right offsets we need to look at the block's 'direction' 1.1238 + if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) { 1.1239 + // How we determine the hypothetical box depends on whether the element 1.1240 + // would have been inline-level or block-level 1.1241 + if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { 1.1242 + // The placeholder represents the left edge of the hypothetical box 1.1243 + aHypotheticalBox.mLeft = placeholderOffset.x; 1.1244 + } else { 1.1245 + aHypotheticalBox.mLeft = aBlockLeftContentEdge; 1.1246 + } 1.1247 +#ifdef DEBUG 1.1248 + aHypotheticalBox.mLeftIsExact = true; 1.1249 +#endif 1.1250 + 1.1251 + if (knowBoxWidth) { 1.1252 + aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth; 1.1253 +#ifdef DEBUG 1.1254 + aHypotheticalBox.mRightIsExact = true; 1.1255 +#endif 1.1256 + } else { 1.1257 + // We can't compute the right edge because we don't know the desired 1.1258 + // width. So instead use the right content edge of the block parent, 1.1259 + // but remember it's not exact 1.1260 + aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; 1.1261 +#ifdef DEBUG 1.1262 + aHypotheticalBox.mRightIsExact = false; 1.1263 +#endif 1.1264 + } 1.1265 + 1.1266 + } else { 1.1267 + // The placeholder represents the right edge of the hypothetical box 1.1268 + if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { 1.1269 + aHypotheticalBox.mRight = placeholderOffset.x; 1.1270 + } else { 1.1271 + aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; 1.1272 + } 1.1273 +#ifdef DEBUG 1.1274 + aHypotheticalBox.mRightIsExact = true; 1.1275 +#endif 1.1276 + 1.1277 + if (knowBoxWidth) { 1.1278 + aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth; 1.1279 +#ifdef DEBUG 1.1280 + aHypotheticalBox.mLeftIsExact = true; 1.1281 +#endif 1.1282 + } else { 1.1283 + // We can't compute the left edge because we don't know the desired 1.1284 + // width. So instead use the left content edge of the block parent, 1.1285 + // but remember it's not exact 1.1286 + aHypotheticalBox.mLeft = aBlockLeftContentEdge; 1.1287 +#ifdef DEBUG 1.1288 + aHypotheticalBox.mLeftIsExact = false; 1.1289 +#endif 1.1290 + } 1.1291 + 1.1292 + } 1.1293 + 1.1294 + // The current coordinate space is that of the nearest block to the placeholder. 1.1295 + // Convert to the coordinate space of the absolute containing block 1.1296 + // One weird thing here is that for fixed-positioned elements we want to do 1.1297 + // the conversion incorrectly; specifically we want to ignore any scrolling 1.1298 + // that may have happened; 1.1299 + nsPoint cbOffset; 1.1300 + if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED && 1.1301 + // Exclude cases inside -moz-transform where fixed is like absolute. 1.1302 + nsLayoutUtils::IsReallyFixedPos(frame)) { 1.1303 + // In this case, cbrs->frame will always be an ancestor of 1.1304 + // aContainingBlock, so can just walk our way up the frame tree. 1.1305 + // Make sure to not add positions of frames whose parent is a 1.1306 + // scrollFrame, since we're doing fixed positioning, which assumes 1.1307 + // everything is scrolled to (0,0). 1.1308 + cbOffset.MoveTo(0, 0); 1.1309 + do { 1.1310 + NS_ASSERTION(aContainingBlock, 1.1311 + "Should hit cbrs->frame before we run off the frame tree!"); 1.1312 + cbOffset += aContainingBlock->GetPositionIgnoringScrolling(); 1.1313 + aContainingBlock = aContainingBlock->GetParent(); 1.1314 + } while (aContainingBlock != cbrs->frame); 1.1315 + } else { 1.1316 + // XXXldb We need to either ignore scrolling for the absolute 1.1317 + // positioning case too (and take the incompatibility) or figure out 1.1318 + // how to make these positioned elements actually *move* when we 1.1319 + // scroll, and thus avoid the resulting incremental reflow bugs. 1.1320 + cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame); 1.1321 + } 1.1322 + aHypotheticalBox.mLeft += cbOffset.x; 1.1323 + aHypotheticalBox.mTop += cbOffset.y; 1.1324 + aHypotheticalBox.mRight += cbOffset.x; 1.1325 + 1.1326 + // The specified offsets are relative to the absolute containing block's 1.1327 + // padding edge and our current values are relative to the border edge, so 1.1328 + // translate. 1.1329 + nsMargin border = cbrs->ComputedPhysicalBorderPadding() - cbrs->ComputedPhysicalPadding(); 1.1330 + aHypotheticalBox.mLeft -= border.left; 1.1331 + aHypotheticalBox.mRight -= border.left; 1.1332 + aHypotheticalBox.mTop -= border.top; 1.1333 +} 1.1334 + 1.1335 +void 1.1336 +nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, 1.1337 + const nsHTMLReflowState* cbrs, 1.1338 + nscoord containingBlockWidth, 1.1339 + nscoord containingBlockHeight, 1.1340 + nsIAtom* aFrameType) 1.1341 +{ 1.1342 + NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT, 1.1343 + "containing block height must be constrained"); 1.1344 + 1.1345 + NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame, 1.1346 + "InitAbsoluteConstraints should not be called on table frames"); 1.1347 + NS_ASSERTION(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW, 1.1348 + "Why are we here?"); 1.1349 + 1.1350 + // Get the placeholder frame 1.1351 + nsIFrame* placeholderFrame; 1.1352 + 1.1353 + placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(frame); 1.1354 + NS_ASSERTION(nullptr != placeholderFrame, "no placeholder frame"); 1.1355 + 1.1356 + // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are 1.1357 + // 'auto', then compute the hypothetical box of where the element would 1.1358 + // have been if it had been in the flow 1.1359 + nsHypotheticalBox hypotheticalBox; 1.1360 + if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) && 1.1361 + (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) || 1.1362 + ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) && 1.1363 + (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) { 1.1364 + // Find the nearest containing block frame to the placeholder frame, 1.1365 + // and return its left edge and width. 1.1366 + nscoord cbLeftEdge, cbWidth; 1.1367 + nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame, 1.1368 + cbLeftEdge, 1.1369 + cbWidth); 1.1370 + 1.1371 + CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame, 1.1372 + cbLeftEdge, cbWidth, cbrs, hypotheticalBox, aFrameType); 1.1373 + } 1.1374 + 1.1375 + // Initialize the 'left' and 'right' computed offsets 1.1376 + // XXX Handle new 'static-position' value... 1.1377 + bool leftIsAuto = false, rightIsAuto = false; 1.1378 + if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) { 1.1379 + ComputedPhysicalOffsets().left = 0; 1.1380 + leftIsAuto = true; 1.1381 + } else { 1.1382 + ComputedPhysicalOffsets().left = nsLayoutUtils:: 1.1383 + ComputeCBDependentValue(containingBlockWidth, 1.1384 + mStylePosition->mOffset.GetLeft()); 1.1385 + } 1.1386 + if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) { 1.1387 + ComputedPhysicalOffsets().right = 0; 1.1388 + rightIsAuto = true; 1.1389 + } else { 1.1390 + ComputedPhysicalOffsets().right = nsLayoutUtils:: 1.1391 + ComputeCBDependentValue(containingBlockWidth, 1.1392 + mStylePosition->mOffset.GetRight()); 1.1393 + } 1.1394 + 1.1395 + // Use the horizontal component of the hypothetical box in the cases 1.1396 + // where it's needed. 1.1397 + if (leftIsAuto && rightIsAuto) { 1.1398 + // Use the direction of the original ("static-position") containing block 1.1399 + // to dictate whether 'left' or 'right' is treated like 'static-position'. 1.1400 + if (NS_STYLE_DIRECTION_LTR == placeholderFrame->GetContainingBlock() 1.1401 + ->StyleVisibility()->mDirection) { 1.1402 + NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have " 1.1403 + "exact value on containing block's start side"); 1.1404 + ComputedPhysicalOffsets().left = hypotheticalBox.mLeft; 1.1405 + leftIsAuto = false; 1.1406 + } else { 1.1407 + NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have " 1.1408 + "exact value on containing block's start side"); 1.1409 + ComputedPhysicalOffsets().right = containingBlockWidth - hypotheticalBox.mRight; 1.1410 + rightIsAuto = false; 1.1411 + } 1.1412 + } 1.1413 + 1.1414 + // Initialize the 'top' and 'bottom' computed offsets 1.1415 + bool topIsAuto = false, bottomIsAuto = false; 1.1416 + if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) { 1.1417 + ComputedPhysicalOffsets().top = 0; 1.1418 + topIsAuto = true; 1.1419 + } else { 1.1420 + ComputedPhysicalOffsets().top = nsLayoutUtils:: 1.1421 + ComputeHeightDependentValue(containingBlockHeight, 1.1422 + mStylePosition->mOffset.GetTop()); 1.1423 + } 1.1424 + if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) { 1.1425 + ComputedPhysicalOffsets().bottom = 0; 1.1426 + bottomIsAuto = true; 1.1427 + } else { 1.1428 + ComputedPhysicalOffsets().bottom = nsLayoutUtils:: 1.1429 + ComputeHeightDependentValue(containingBlockHeight, 1.1430 + mStylePosition->mOffset.GetBottom()); 1.1431 + } 1.1432 + 1.1433 + if (topIsAuto && bottomIsAuto) { 1.1434 + // Treat 'top' like 'static-position' 1.1435 + ComputedPhysicalOffsets().top = hypotheticalBox.mTop; 1.1436 + topIsAuto = false; 1.1437 + } 1.1438 + 1.1439 + bool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(); 1.1440 + bool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit(); 1.1441 + 1.1442 + uint32_t computeSizeFlags = 0; 1.1443 + if (leftIsAuto || rightIsAuto) { 1.1444 + computeSizeFlags |= nsIFrame::eShrinkWrap; 1.1445 + } 1.1446 + 1.1447 + { 1.1448 + AutoMaybeDisableFontInflation an(frame); 1.1449 + 1.1450 + nsSize size = 1.1451 + frame->ComputeSize(rendContext, 1.1452 + nsSize(containingBlockWidth, 1.1453 + containingBlockHeight), 1.1454 + containingBlockWidth, // XXX or mAvailableWidth? 1.1455 + nsSize(ComputedPhysicalMargin().LeftRight() + 1.1456 + ComputedPhysicalOffsets().LeftRight(), 1.1457 + ComputedPhysicalMargin().TopBottom() + 1.1458 + ComputedPhysicalOffsets().TopBottom()), 1.1459 + nsSize(ComputedPhysicalBorderPadding().LeftRight() - 1.1460 + ComputedPhysicalPadding().LeftRight(), 1.1461 + ComputedPhysicalBorderPadding().TopBottom() - 1.1462 + ComputedPhysicalPadding().TopBottom()), 1.1463 + nsSize(ComputedPhysicalPadding().LeftRight(), 1.1464 + ComputedPhysicalPadding().TopBottom()), 1.1465 + computeSizeFlags); 1.1466 + ComputedWidth() = size.width; 1.1467 + ComputedHeight() = size.height; 1.1468 + } 1.1469 + NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); 1.1470 + NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || 1.1471 + ComputedHeight() >= 0, "Bogus height"); 1.1472 + 1.1473 + // XXX Now that we have ComputeSize, can we condense many of the 1.1474 + // branches off of widthIsAuto? 1.1475 + 1.1476 + if (leftIsAuto) { 1.1477 + // We know 'right' is not 'auto' anymore thanks to the hypothetical 1.1478 + // box code above. 1.1479 + // Solve for 'left'. 1.1480 + if (widthIsAuto) { 1.1481 + // XXXldb This, and the corresponding code in 1.1482 + // nsAbsoluteContainingBlock.cpp, could probably go away now that 1.1483 + // we always compute widths. 1.1484 + ComputedPhysicalOffsets().left = NS_AUTOOFFSET; 1.1485 + } else { 1.1486 + ComputedPhysicalOffsets().left = containingBlockWidth - ComputedPhysicalMargin().left - 1.1487 + ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - 1.1488 + ComputedPhysicalMargin().right - ComputedPhysicalOffsets().right; 1.1489 + 1.1490 + } 1.1491 + } else if (rightIsAuto) { 1.1492 + // We know 'left' is not 'auto' anymore thanks to the hypothetical 1.1493 + // box code above. 1.1494 + // Solve for 'right'. 1.1495 + if (widthIsAuto) { 1.1496 + // XXXldb This, and the corresponding code in 1.1497 + // nsAbsoluteContainingBlock.cpp, could probably go away now that 1.1498 + // we always compute widths. 1.1499 + ComputedPhysicalOffsets().right = NS_AUTOOFFSET; 1.1500 + } else { 1.1501 + ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left - 1.1502 + ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() - 1.1503 + ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right; 1.1504 + } 1.1505 + } else { 1.1506 + // Neither 'left' nor 'right' is 'auto'. However, the width might 1.1507 + // still not fill all the available space (even though we didn't 1.1508 + // shrink-wrap) in case: 1.1509 + // * width was specified 1.1510 + // * we're dealing with a replaced element 1.1511 + // * width was constrained by min-width or max-width. 1.1512 + 1.1513 + nscoord availMarginSpace = containingBlockWidth - 1.1514 + ComputedPhysicalOffsets().LeftRight() - 1.1515 + ComputedPhysicalMargin().LeftRight() - 1.1516 + ComputedPhysicalBorderPadding().LeftRight() - 1.1517 + ComputedWidth(); 1.1518 + bool marginLeftIsAuto = 1.1519 + eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); 1.1520 + bool marginRightIsAuto = 1.1521 + eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); 1.1522 + 1.1523 + if (marginLeftIsAuto) { 1.1524 + if (marginRightIsAuto) { 1.1525 + if (availMarginSpace < 0) { 1.1526 + // Note that this case is different from the neither-'auto' 1.1527 + // case below, where the spec says to ignore 'left'/'right'. 1.1528 + if (cbrs && 1.1529 + NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { 1.1530 + // Ignore the specified value for 'margin-left'. 1.1531 + ComputedPhysicalMargin().left = availMarginSpace; 1.1532 + } else { 1.1533 + // Ignore the specified value for 'margin-right'. 1.1534 + ComputedPhysicalMargin().right = availMarginSpace; 1.1535 + } 1.1536 + } else { 1.1537 + // Both 'margin-left' and 'margin-right' are 'auto', so they get 1.1538 + // equal values 1.1539 + ComputedPhysicalMargin().left = availMarginSpace / 2; 1.1540 + ComputedPhysicalMargin().right = availMarginSpace - ComputedPhysicalMargin().left; 1.1541 + } 1.1542 + } else { 1.1543 + // Just 'margin-left' is 'auto' 1.1544 + ComputedPhysicalMargin().left = availMarginSpace; 1.1545 + } 1.1546 + } else { 1.1547 + if (marginRightIsAuto) { 1.1548 + // Just 'margin-right' is 'auto' 1.1549 + ComputedPhysicalMargin().right = availMarginSpace; 1.1550 + } else { 1.1551 + // We're over-constrained so use the direction of the containing 1.1552 + // block to dictate which value to ignore. (And note that the 1.1553 + // spec says to ignore 'left' or 'right' rather than 1.1554 + // 'margin-left' or 'margin-right'.) 1.1555 + // Note that this case is different from the both-'auto' case 1.1556 + // above, where the spec says to ignore 1.1557 + // 'margin-left'/'margin-right'. 1.1558 + if (cbrs && 1.1559 + NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { 1.1560 + // Ignore the specified value for 'left'. 1.1561 + ComputedPhysicalOffsets().left += availMarginSpace; 1.1562 + } else { 1.1563 + // Ignore the specified value for 'right'. 1.1564 + ComputedPhysicalOffsets().right += availMarginSpace; 1.1565 + } 1.1566 + } 1.1567 + } 1.1568 + } 1.1569 + 1.1570 + if (topIsAuto) { 1.1571 + // solve for 'top' 1.1572 + if (heightIsAuto) { 1.1573 + ComputedPhysicalOffsets().top = NS_AUTOOFFSET; 1.1574 + } else { 1.1575 + ComputedPhysicalOffsets().top = containingBlockHeight - ComputedPhysicalMargin().top - 1.1576 + ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom - 1.1577 + ComputedPhysicalMargin().bottom - ComputedPhysicalOffsets().bottom; 1.1578 + } 1.1579 + } else if (bottomIsAuto) { 1.1580 + // solve for 'bottom' 1.1581 + if (heightIsAuto) { 1.1582 + ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET; 1.1583 + } else { 1.1584 + ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top - 1.1585 + ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() - 1.1586 + ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom; 1.1587 + } 1.1588 + } else { 1.1589 + // Neither 'top' nor 'bottom' is 'auto'. 1.1590 + nscoord autoHeight = containingBlockHeight - 1.1591 + ComputedPhysicalOffsets().TopBottom() - 1.1592 + ComputedPhysicalMargin().TopBottom() - 1.1593 + ComputedPhysicalBorderPadding().TopBottom(); 1.1594 + if (autoHeight < 0) { 1.1595 + autoHeight = 0; 1.1596 + } 1.1597 + 1.1598 + if (ComputedHeight() == NS_UNCONSTRAINEDSIZE) { 1.1599 + // For non-replaced elements with 'height' auto, the 'height' 1.1600 + // fills the remaining space. 1.1601 + ComputedHeight() = autoHeight; 1.1602 + 1.1603 + // XXX Do these need box-sizing adjustments? 1.1604 + if (ComputedHeight() > ComputedMaxHeight()) 1.1605 + ComputedHeight() = ComputedMaxHeight(); 1.1606 + if (ComputedHeight() < ComputedMinHeight()) 1.1607 + ComputedHeight() = ComputedMinHeight(); 1.1608 + } 1.1609 + 1.1610 + // The height might still not fill all the available space in case: 1.1611 + // * height was specified 1.1612 + // * we're dealing with a replaced element 1.1613 + // * height was constrained by min-height or max-height. 1.1614 + nscoord availMarginSpace = autoHeight - ComputedHeight(); 1.1615 + bool marginTopIsAuto = 1.1616 + eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit(); 1.1617 + bool marginBottomIsAuto = 1.1618 + eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit(); 1.1619 + 1.1620 + if (marginTopIsAuto) { 1.1621 + if (marginBottomIsAuto) { 1.1622 + if (availMarginSpace < 0) { 1.1623 + // FIXME: Note that the spec doesn't actually say we should do this! 1.1624 + ComputedPhysicalMargin().bottom = availMarginSpace; 1.1625 + } else { 1.1626 + // Both 'margin-top' and 'margin-bottom' are 'auto', so they get 1.1627 + // equal values 1.1628 + ComputedPhysicalMargin().top = availMarginSpace / 2; 1.1629 + ComputedPhysicalMargin().bottom = availMarginSpace - ComputedPhysicalMargin().top; 1.1630 + } 1.1631 + } else { 1.1632 + // Just 'margin-top' is 'auto' 1.1633 + ComputedPhysicalMargin().top = availMarginSpace; 1.1634 + } 1.1635 + } else { 1.1636 + if (marginBottomIsAuto) { 1.1637 + // Just 'margin-bottom' is 'auto' 1.1638 + ComputedPhysicalMargin().bottom = availMarginSpace; 1.1639 + } else { 1.1640 + // We're over-constrained so ignore the specified value for 1.1641 + // 'bottom'. (And note that the spec says to ignore 'bottom' 1.1642 + // rather than 'margin-bottom'.) 1.1643 + ComputedPhysicalOffsets().bottom += availMarginSpace; 1.1644 + } 1.1645 + } 1.1646 + } 1.1647 +} 1.1648 + 1.1649 +nscoord 1.1650 +GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState) 1.1651 +{ 1.1652 + nscoord result = 0; 1.1653 + if (!aReflowState) return result; 1.1654 + 1.1655 + // zero auto margins 1.1656 + nsMargin margin = aReflowState->ComputedPhysicalMargin(); 1.1657 + if (NS_AUTOMARGIN == margin.top) 1.1658 + margin.top = 0; 1.1659 + if (NS_AUTOMARGIN == margin.bottom) 1.1660 + margin.bottom = 0; 1.1661 + 1.1662 + result += margin.top + margin.bottom; 1.1663 + result += aReflowState->ComputedPhysicalBorderPadding().top + 1.1664 + aReflowState->ComputedPhysicalBorderPadding().bottom; 1.1665 + 1.1666 + return result; 1.1667 +} 1.1668 + 1.1669 +/* Get the height based on the viewport of the containing block specified 1.1670 + * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT 1.1671 + * This will walk up the chain of containing blocks looking for a computed height 1.1672 + * until it finds the canvas frame, or it encounters a frame that is not a block, 1.1673 + * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693) 1.1674 + * 1.1675 + * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block. 1.1676 + * 1.1677 + * See also IsQuirkContainingBlockHeight. 1.1678 + */ 1.1679 +static nscoord 1.1680 +CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) 1.1681 +{ 1.1682 + const nsHTMLReflowState* firstAncestorRS = nullptr; // a candidate for html frame 1.1683 + const nsHTMLReflowState* secondAncestorRS = nullptr; // a candidate for body frame 1.1684 + 1.1685 + // initialize the default to NS_AUTOHEIGHT as this is the containings block 1.1686 + // computed height when this function is called. It is possible that we 1.1687 + // don't alter this height especially if we are restricted to one level 1.1688 + nscoord result = NS_AUTOHEIGHT; 1.1689 + 1.1690 + const nsHTMLReflowState* rs = aCBReflowState; 1.1691 + for (; rs; rs = rs->parentReflowState) { 1.1692 + nsIAtom* frameType = rs->frame->GetType(); 1.1693 + // if the ancestor is auto height then skip it and continue up if it 1.1694 + // is the first block frame and possibly the body/html 1.1695 + if (nsGkAtoms::blockFrame == frameType || 1.1696 +#ifdef MOZ_XUL 1.1697 + nsGkAtoms::XULLabelFrame == frameType || 1.1698 +#endif 1.1699 + nsGkAtoms::scrollFrame == frameType) { 1.1700 + 1.1701 + secondAncestorRS = firstAncestorRS; 1.1702 + firstAncestorRS = rs; 1.1703 + 1.1704 + // If the current frame we're looking at is positioned, we don't want to 1.1705 + // go any further (see bug 221784). The behavior we want here is: 1) If 1.1706 + // not auto-height, use this as the percentage base. 2) If auto-height, 1.1707 + // keep looking, unless the frame is positioned. 1.1708 + if (NS_AUTOHEIGHT == rs->ComputedHeight()) { 1.1709 + if (rs->frame->IsAbsolutelyPositioned()) { 1.1710 + break; 1.1711 + } else { 1.1712 + continue; 1.1713 + } 1.1714 + } 1.1715 + } 1.1716 + else if (nsGkAtoms::canvasFrame == frameType) { 1.1717 + // Always continue on to the height calculation 1.1718 + } 1.1719 + else if (nsGkAtoms::pageContentFrame == frameType) { 1.1720 + nsIFrame* prevInFlow = rs->frame->GetPrevInFlow(); 1.1721 + // only use the page content frame for a height basis if it is the first in flow 1.1722 + if (prevInFlow) 1.1723 + break; 1.1724 + } 1.1725 + else { 1.1726 + break; 1.1727 + } 1.1728 + 1.1729 + // if the ancestor is the page content frame then the percent base is 1.1730 + // the avail height, otherwise it is the computed height 1.1731 + result = (nsGkAtoms::pageContentFrame == frameType) 1.1732 + ? rs->AvailableHeight() : rs->ComputedHeight(); 1.1733 + // if unconstrained - don't sutract borders - would result in huge height 1.1734 + if (NS_AUTOHEIGHT == result) return result; 1.1735 + 1.1736 + // if we got to the canvas or page content frame, then subtract out 1.1737 + // margin/border/padding for the BODY and HTML elements 1.1738 + if ((nsGkAtoms::canvasFrame == frameType) || 1.1739 + (nsGkAtoms::pageContentFrame == frameType)) { 1.1740 + 1.1741 + result -= GetVerticalMarginBorderPadding(firstAncestorRS); 1.1742 + result -= GetVerticalMarginBorderPadding(secondAncestorRS); 1.1743 + 1.1744 +#ifdef DEBUG 1.1745 + // make sure the first ancestor is the HTML and the second is the BODY 1.1746 + if (firstAncestorRS) { 1.1747 + nsIContent* frameContent = firstAncestorRS->frame->GetContent(); 1.1748 + if (frameContent) { 1.1749 + nsIAtom *contentTag = frameContent->Tag(); 1.1750 + NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML"); 1.1751 + } 1.1752 + } 1.1753 + if (secondAncestorRS) { 1.1754 + nsIContent* frameContent = secondAncestorRS->frame->GetContent(); 1.1755 + if (frameContent) { 1.1756 + nsIAtom *contentTag = frameContent->Tag(); 1.1757 + NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY"); 1.1758 + } 1.1759 + } 1.1760 +#endif 1.1761 + 1.1762 + } 1.1763 + // if we got to the html frame (a block child of the canvas) ... 1.1764 + else if (nsGkAtoms::blockFrame == frameType && 1.1765 + rs->parentReflowState && 1.1766 + nsGkAtoms::canvasFrame == 1.1767 + rs->parentReflowState->frame->GetType()) { 1.1768 + // ... then subtract out margin/border/padding for the BODY element 1.1769 + result -= GetVerticalMarginBorderPadding(secondAncestorRS); 1.1770 + } 1.1771 + break; 1.1772 + } 1.1773 + 1.1774 + // Make sure not to return a negative height here! 1.1775 + return std::max(result, 0); 1.1776 +} 1.1777 + 1.1778 +// Called by InitConstraints() to compute the containing block rectangle for 1.1779 +// the element. Handles the special logic for absolutely positioned elements 1.1780 +void 1.1781 +nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext, 1.1782 + const nsHTMLReflowState* aContainingBlockRS, 1.1783 + nscoord& aContainingBlockWidth, 1.1784 + nscoord& aContainingBlockHeight) 1.1785 +{ 1.1786 + // Unless the element is absolutely positioned, the containing block is 1.1787 + // formed by the content edge of the nearest block-level ancestor 1.1788 + aContainingBlockWidth = aContainingBlockRS->ComputedWidth(); 1.1789 + aContainingBlockHeight = aContainingBlockRS->ComputedHeight(); 1.1790 + 1.1791 + // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to 1.1792 + // special case them here. 1.1793 + if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE || 1.1794 + (frame->GetType() == nsGkAtoms::tableFrame && 1.1795 + frame->IsAbsolutelyPositioned() && 1.1796 + (frame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) { 1.1797 + // See if the ancestor is block-level or inline-level 1.1798 + if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) { 1.1799 + // Base our size on the actual size of the frame. In cases when this is 1.1800 + // completely bogus (eg initial reflow), this code shouldn't even be 1.1801 + // called, since the code in nsInlineFrame::Reflow will pass in 1.1802 + // the containing block dimensions to our constructor. 1.1803 + // XXXbz we should be taking the in-flows into account too, but 1.1804 + // that's very hard. 1.1805 + nsMargin computedBorder = aContainingBlockRS->ComputedPhysicalBorderPadding() - 1.1806 + aContainingBlockRS->ComputedPhysicalPadding(); 1.1807 + aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width - 1.1808 + computedBorder.LeftRight(); 1.1809 + NS_ASSERTION(aContainingBlockWidth >= 0, 1.1810 + "Negative containing block width!"); 1.1811 + aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height - 1.1812 + computedBorder.TopBottom(); 1.1813 + NS_ASSERTION(aContainingBlockHeight >= 0, 1.1814 + "Negative containing block height!"); 1.1815 + } else { 1.1816 + // If the ancestor is block-level, the containing block is formed by the 1.1817 + // padding edge of the ancestor 1.1818 + aContainingBlockWidth += aContainingBlockRS->ComputedPhysicalPadding().LeftRight(); 1.1819 + aContainingBlockHeight += aContainingBlockRS->ComputedPhysicalPadding().TopBottom(); 1.1820 + } 1.1821 + } else { 1.1822 + // an element in quirks mode gets a containing block based on looking for a 1.1823 + // parent with a non-auto height if the element has a percent height 1.1824 + // Note: We don't emulate this quirk for percents in calc(). 1.1825 + if (NS_AUTOHEIGHT == aContainingBlockHeight) { 1.1826 + if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && 1.1827 + mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) { 1.1828 + aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS); 1.1829 + } 1.1830 + } 1.1831 + } 1.1832 +} 1.1833 + 1.1834 +static eNormalLineHeightControl GetNormalLineHeightCalcControl(void) 1.1835 +{ 1.1836 + if (sNormalLineHeightControl == eUninitialized) { 1.1837 + // browser.display.normal_lineheight_calc_control is not user 1.1838 + // changeable, so no need to register callback for it. 1.1839 + int32_t val = 1.1840 + Preferences::GetInt("browser.display.normal_lineheight_calc_control", 1.1841 + eNoExternalLeading); 1.1842 + sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val); 1.1843 + } 1.1844 + return sNormalLineHeightControl; 1.1845 +} 1.1846 + 1.1847 +static inline bool 1.1848 +IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay) 1.1849 +{ 1.1850 + if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION) 1.1851 + return false; 1.1852 + uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide; 1.1853 + return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || 1.1854 + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; 1.1855 +} 1.1856 + 1.1857 +static nsFlexContainerFrame* 1.1858 +GetFlexContainer(nsIFrame* aFrame) 1.1859 +{ 1.1860 + nsIFrame* parent = aFrame->GetParent(); 1.1861 + if (!parent || 1.1862 + parent->GetType() != nsGkAtoms::flexContainerFrame) { 1.1863 + return nullptr; 1.1864 + } 1.1865 + 1.1866 + return static_cast<nsFlexContainerFrame*>(parent); 1.1867 +} 1.1868 + 1.1869 +// Flex items resolve percentage margin & padding against the flex 1.1870 +// container's height (which is the containing block height). 1.1871 +// For everything else: the CSS21 spec requires that margin and padding 1.1872 +// percentage values are calculated with respect to the *width* of the 1.1873 +// containing block, even for margin & padding in the vertical axis. 1.1874 +static nscoord 1.1875 +VerticalOffsetPercentBasis(const nsIFrame* aFrame, 1.1876 + nscoord aContainingBlockWidth, 1.1877 + nscoord aContainingBlockHeight) 1.1878 +{ 1.1879 + if (!aFrame->IsFlexItem()) { 1.1880 + return aContainingBlockWidth; 1.1881 + } 1.1882 + 1.1883 + if (aContainingBlockHeight == NS_AUTOHEIGHT) { 1.1884 + return 0; 1.1885 + } 1.1886 + 1.1887 + return aContainingBlockHeight; 1.1888 +} 1.1889 + 1.1890 +// XXX refactor this code to have methods for each set of properties 1.1891 +// we are computing: width,height,line-height; margin; offsets 1.1892 + 1.1893 +void 1.1894 +nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, 1.1895 + nscoord aContainingBlockWidth, 1.1896 + nscoord aContainingBlockHeight, 1.1897 + const nsMargin* aBorder, 1.1898 + const nsMargin* aPadding, 1.1899 + nsIAtom* aFrameType) 1.1900 +{ 1.1901 + DISPLAY_INIT_CONSTRAINTS(frame, this, 1.1902 + aContainingBlockWidth, aContainingBlockHeight, 1.1903 + aBorder, aPadding); 1.1904 + 1.1905 + // If this is a reflow root, then set the computed width and 1.1906 + // height equal to the available space 1.1907 + if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) { 1.1908 + // XXXldb This doesn't mean what it used to! 1.1909 + InitOffsets(aContainingBlockWidth, 1.1910 + VerticalOffsetPercentBasis(frame, aContainingBlockWidth, 1.1911 + aContainingBlockHeight), 1.1912 + aFrameType, aBorder, aPadding); 1.1913 + // Override mComputedMargin since reflow roots start from the 1.1914 + // frame's boundary, which is inside the margin. 1.1915 + ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); 1.1916 + ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); 1.1917 + 1.1918 + ComputedWidth() = AvailableWidth() - ComputedPhysicalBorderPadding().LeftRight(); 1.1919 + if (ComputedWidth() < 0) 1.1920 + ComputedWidth() = 0; 1.1921 + if (AvailableHeight() != NS_UNCONSTRAINEDSIZE) { 1.1922 + ComputedHeight() = AvailableHeight() - ComputedPhysicalBorderPadding().TopBottom(); 1.1923 + if (ComputedHeight() < 0) 1.1924 + ComputedHeight() = 0; 1.1925 + } else { 1.1926 + ComputedHeight() = NS_UNCONSTRAINEDSIZE; 1.1927 + } 1.1928 + 1.1929 + ComputedMinWidth() = ComputedMinHeight() = 0; 1.1930 + ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; 1.1931 + } else { 1.1932 + // Get the containing block reflow state 1.1933 + const nsHTMLReflowState* cbrs = mCBReflowState; 1.1934 + NS_ASSERTION(nullptr != cbrs, "no containing block"); 1.1935 + 1.1936 + // If we weren't given a containing block width and height, then 1.1937 + // compute one 1.1938 + if (aContainingBlockWidth == -1) { 1.1939 + ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, 1.1940 + aContainingBlockHeight); 1.1941 + } 1.1942 + 1.1943 + // See if the containing block height is based on the size of its 1.1944 + // content 1.1945 + nsIAtom* fType; 1.1946 + if (NS_AUTOHEIGHT == aContainingBlockHeight) { 1.1947 + // See if the containing block is a cell frame which needs 1.1948 + // to use the mComputedHeight of the cell instead of what the cell block passed in. 1.1949 + // XXX It seems like this could lead to bugs with min-height and friends 1.1950 + if (cbrs->parentReflowState) { 1.1951 + fType = cbrs->frame->GetType(); 1.1952 + if (IS_TABLE_CELL(fType)) { 1.1953 + // use the cell's computed height 1.1954 + aContainingBlockHeight = cbrs->ComputedHeight(); 1.1955 + } 1.1956 + } 1.1957 + } 1.1958 + 1.1959 + // XXX Might need to also pass the CB height (not width) for page boxes, 1.1960 + // too, if we implement them. 1.1961 + InitOffsets(aContainingBlockWidth, 1.1962 + VerticalOffsetPercentBasis(frame, aContainingBlockWidth, 1.1963 + aContainingBlockHeight), 1.1964 + aFrameType, aBorder, aPadding); 1.1965 + 1.1966 + const nsStyleCoord &height = mStylePosition->mHeight; 1.1967 + nsStyleUnit heightUnit = height.GetUnit(); 1.1968 + 1.1969 + // Check for a percentage based height and a containing block height 1.1970 + // that depends on the content height 1.1971 + // XXX twiddling heightUnit doesn't help anymore 1.1972 + // FIXME Shouldn't we fix that? 1.1973 + if (height.HasPercent()) { 1.1974 + if (NS_AUTOHEIGHT == aContainingBlockHeight) { 1.1975 + // this if clause enables %-height on replaced inline frames, 1.1976 + // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;" 1.1977 + // used to be called exclusively. 1.1978 + if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType || 1.1979 + NS_FRAME_REPLACED_CONTAINS_BLOCK( 1.1980 + NS_CSS_FRAME_TYPE_INLINE) == mFrameType) { 1.1981 + // Get the containing block reflow state 1.1982 + NS_ASSERTION(nullptr != cbrs, "no containing block"); 1.1983 + // in quirks mode, get the cb height using the special quirk method 1.1984 + if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { 1.1985 + if (!IS_TABLE_CELL(fType)) { 1.1986 + aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs); 1.1987 + if (aContainingBlockHeight == NS_AUTOHEIGHT) { 1.1988 + heightUnit = eStyleUnit_Auto; 1.1989 + } 1.1990 + } 1.1991 + else { 1.1992 + heightUnit = eStyleUnit_Auto; 1.1993 + } 1.1994 + } 1.1995 + // in standard mode, use the cb height. if it's "auto", as will be the case 1.1996 + // by default in BODY, use auto height as per CSS2 spec. 1.1997 + else 1.1998 + { 1.1999 + if (NS_AUTOHEIGHT != cbrs->ComputedHeight()) 1.2000 + aContainingBlockHeight = cbrs->ComputedHeight(); 1.2001 + else 1.2002 + heightUnit = eStyleUnit_Auto; 1.2003 + } 1.2004 + } 1.2005 + else { 1.2006 + // default to interpreting the height like 'auto' 1.2007 + heightUnit = eStyleUnit_Auto; 1.2008 + } 1.2009 + } 1.2010 + } 1.2011 + 1.2012 + // Compute our offsets if the element is relatively positioned. We need 1.2013 + // the correct containing block width and height here, which is why we need 1.2014 + // to do it after all the quirks-n-such above. (If the element is sticky 1.2015 + // positioned, we need to wait until the scroll container knows its size, 1.2016 + // so we compute offsets from StickyScrollContainer::UpdatePositions.) 1.2017 + if (mStyleDisplay->IsRelativelyPositioned(frame) && 1.2018 + NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) { 1.2019 + uint8_t direction = NS_STYLE_DIRECTION_LTR; 1.2020 + if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { 1.2021 + direction = NS_STYLE_DIRECTION_RTL; 1.2022 + } 1.2023 + ComputeRelativeOffsets(direction, frame, aContainingBlockWidth, 1.2024 + aContainingBlockHeight, ComputedPhysicalOffsets()); 1.2025 + } else { 1.2026 + // Initialize offsets to 0 1.2027 + ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); 1.2028 + } 1.2029 + 1.2030 + // Calculate the computed values for min and max properties. Note that 1.2031 + // this MUST come after we've computed our border and padding. 1.2032 + ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs); 1.2033 + 1.2034 + // Calculate the computed width and height. This varies by frame type 1.2035 + 1.2036 + if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { 1.2037 + // Internal table elements. The rules vary depending on the type. 1.2038 + // Calculate the computed width 1.2039 + bool rowOrRowGroup = false; 1.2040 + const nsStyleCoord &width = mStylePosition->mWidth; 1.2041 + nsStyleUnit widthUnit = width.GetUnit(); 1.2042 + if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) || 1.2043 + (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) { 1.2044 + // 'width' property doesn't apply to table rows and row groups 1.2045 + widthUnit = eStyleUnit_Auto; 1.2046 + rowOrRowGroup = true; 1.2047 + } 1.2048 + 1.2049 + // calc() with percentages acts like auto on internal table elements 1.2050 + if (eStyleUnit_Auto == widthUnit || 1.2051 + (width.IsCalcUnit() && width.CalcHasPercent())) { 1.2052 + ComputedWidth() = AvailableWidth(); 1.2053 + 1.2054 + if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ 1.2055 + // Internal table elements don't have margins. Only tables and 1.2056 + // cells have border and padding 1.2057 + ComputedWidth() -= ComputedPhysicalBorderPadding().left + 1.2058 + ComputedPhysicalBorderPadding().right; 1.2059 + if (ComputedWidth() < 0) 1.2060 + ComputedWidth() = 0; 1.2061 + } 1.2062 + NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width"); 1.2063 + 1.2064 + } else { 1.2065 + NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(), 1.2066 + "unexpected width unit change"); 1.2067 + ComputedWidth() = ComputeWidthValue(aContainingBlockWidth, 1.2068 + mStylePosition->mBoxSizing, 1.2069 + mStylePosition->mWidth); 1.2070 + } 1.2071 + 1.2072 + // Calculate the computed height 1.2073 + if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) || 1.2074 + (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) { 1.2075 + // 'height' property doesn't apply to table columns and column groups 1.2076 + heightUnit = eStyleUnit_Auto; 1.2077 + } 1.2078 + // calc() with percentages acts like 'auto' on internal table elements 1.2079 + if (eStyleUnit_Auto == heightUnit || 1.2080 + (height.IsCalcUnit() && height.CalcHasPercent())) { 1.2081 + ComputedHeight() = NS_AUTOHEIGHT; 1.2082 + } else { 1.2083 + NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(), 1.2084 + "unexpected height unit change"); 1.2085 + ComputedHeight() = ComputeHeightValue(aContainingBlockHeight, 1.2086 + mStylePosition->mBoxSizing, 1.2087 + mStylePosition->mHeight); 1.2088 + } 1.2089 + 1.2090 + // Doesn't apply to table elements 1.2091 + ComputedMinWidth() = ComputedMinHeight() = 0; 1.2092 + ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; 1.2093 + 1.2094 + } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) { 1.2095 + // XXX not sure if this belongs here or somewhere else - cwk 1.2096 + InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth, 1.2097 + aContainingBlockHeight, aFrameType); 1.2098 + } else { 1.2099 + AutoMaybeDisableFontInflation an(frame); 1.2100 + 1.2101 + bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType); 1.2102 + uint32_t computeSizeFlags = isBlock ? 0 : nsIFrame::eShrinkWrap; 1.2103 + 1.2104 + // Make sure legend frames with display:block and width:auto still 1.2105 + // shrink-wrap. 1.2106 + if (isBlock && 1.2107 + ((aFrameType == nsGkAtoms::legendFrame && 1.2108 + frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || 1.2109 + (aFrameType == nsGkAtoms::scrollFrame && 1.2110 + frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) { 1.2111 + computeSizeFlags |= nsIFrame::eShrinkWrap; 1.2112 + } 1.2113 + 1.2114 + const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame); 1.2115 + if (flexContainerFrame) { 1.2116 + computeSizeFlags |= nsIFrame::eShrinkWrap; 1.2117 + 1.2118 + // If we're inside of a flex container that needs to measure our 1.2119 + // auto height, pass that information along to ComputeSize(). 1.2120 + if (mFlags.mIsFlexContainerMeasuringHeight) { 1.2121 + computeSizeFlags |= nsIFrame::eUseAutoHeight; 1.2122 + } 1.2123 + } else { 1.2124 + MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, 1.2125 + "We're not in a flex container, so the flag " 1.2126 + "'mIsFlexContainerMeasuringHeight' shouldn't be set"); 1.2127 + } 1.2128 + 1.2129 + nsSize size = 1.2130 + frame->ComputeSize(rendContext, 1.2131 + nsSize(aContainingBlockWidth, 1.2132 + aContainingBlockHeight), 1.2133 + AvailableWidth(), 1.2134 + nsSize(ComputedPhysicalMargin().LeftRight(), 1.2135 + ComputedPhysicalMargin().TopBottom()), 1.2136 + nsSize(ComputedPhysicalBorderPadding().LeftRight() - 1.2137 + ComputedPhysicalPadding().LeftRight(), 1.2138 + ComputedPhysicalBorderPadding().TopBottom() - 1.2139 + ComputedPhysicalPadding().TopBottom()), 1.2140 + nsSize(ComputedPhysicalPadding().LeftRight(), 1.2141 + ComputedPhysicalPadding().TopBottom()), 1.2142 + computeSizeFlags); 1.2143 + 1.2144 + ComputedWidth() = size.width; 1.2145 + ComputedHeight() = size.height; 1.2146 + NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); 1.2147 + NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || 1.2148 + ComputedHeight() >= 0, "Bogus height"); 1.2149 + 1.2150 + // Exclude inline tables and flex items from the block margin calculations 1.2151 + if (isBlock && 1.2152 + !IsSideCaption(frame, mStyleDisplay) && 1.2153 + mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE && 1.2154 + !flexContainerFrame) { 1.2155 + CalculateBlockSideMargins(AvailableWidth(), ComputedWidth(), aFrameType); 1.2156 + } 1.2157 + } 1.2158 + } 1.2159 +} 1.2160 + 1.2161 +static void 1.2162 +UpdateProp(FrameProperties& aProps, 1.2163 + const FramePropertyDescriptor* aProperty, 1.2164 + bool aNeeded, 1.2165 + nsMargin& aNewValue) 1.2166 +{ 1.2167 + if (aNeeded) { 1.2168 + nsMargin* propValue = static_cast<nsMargin*>(aProps.Get(aProperty)); 1.2169 + if (propValue) { 1.2170 + *propValue = aNewValue; 1.2171 + } else { 1.2172 + aProps.Set(aProperty, new nsMargin(aNewValue)); 1.2173 + } 1.2174 + } else { 1.2175 + aProps.Delete(aProperty); 1.2176 + } 1.2177 +} 1.2178 + 1.2179 +void 1.2180 +nsCSSOffsetState::InitOffsets(nscoord aHorizontalPercentBasis, 1.2181 + nscoord aVerticalPercentBasis, 1.2182 + nsIAtom* aFrameType, 1.2183 + const nsMargin *aBorder, 1.2184 + const nsMargin *aPadding) 1.2185 +{ 1.2186 + DISPLAY_INIT_OFFSETS(frame, this, 1.2187 + aHorizontalPercentBasis, 1.2188 + aVerticalPercentBasis, 1.2189 + aBorder, aPadding); 1.2190 + 1.2191 + // Since we are in reflow, we don't need to store these properties anymore 1.2192 + // unless they are dependent on width, in which case we store the new value. 1.2193 + nsPresContext *presContext = frame->PresContext(); 1.2194 + FrameProperties props(presContext->PropertyTable(), frame); 1.2195 + props.Delete(nsIFrame::UsedBorderProperty()); 1.2196 + 1.2197 + // Compute margins from the specified margin style information. These 1.2198 + // become the default computed values, and may be adjusted below 1.2199 + // XXX fix to provide 0,0 for the top&bottom margins for 1.2200 + // inline-non-replaced elements 1.2201 + bool needMarginProp = ComputeMargin(aHorizontalPercentBasis, 1.2202 + aVerticalPercentBasis); 1.2203 + // XXX We need to include 'auto' horizontal margins in this too! 1.2204 + // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin 1.2205 + // to use it even when the margins are all zero (since sometimes 1.2206 + // they get treated as auto) 1.2207 + ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp, 1.2208 + ComputedPhysicalMargin()); 1.2209 + 1.2210 + 1.2211 + const nsStyleDisplay *disp = frame->StyleDisplay(); 1.2212 + bool isThemed = frame->IsThemed(disp); 1.2213 + bool needPaddingProp; 1.2214 + nsIntMargin widget; 1.2215 + if (isThemed && 1.2216 + presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), 1.2217 + frame, disp->mAppearance, 1.2218 + &widget)) { 1.2219 + ComputedPhysicalPadding().top = presContext->DevPixelsToAppUnits(widget.top); 1.2220 + ComputedPhysicalPadding().right = presContext->DevPixelsToAppUnits(widget.right); 1.2221 + ComputedPhysicalPadding().bottom = presContext->DevPixelsToAppUnits(widget.bottom); 1.2222 + ComputedPhysicalPadding().left = presContext->DevPixelsToAppUnits(widget.left); 1.2223 + needPaddingProp = false; 1.2224 + } 1.2225 + else if (frame->IsSVGText()) { 1.2226 + ComputedPhysicalPadding().SizeTo(0, 0, 0, 0); 1.2227 + needPaddingProp = false; 1.2228 + } 1.2229 + else if (aPadding) { // padding is an input arg 1.2230 + ComputedPhysicalPadding() = *aPadding; 1.2231 + needPaddingProp = frame->StylePadding()->IsWidthDependent() || 1.2232 + (frame->GetStateBits() & NS_FRAME_REFLOW_ROOT); 1.2233 + } 1.2234 + else { 1.2235 + needPaddingProp = ComputePadding(aHorizontalPercentBasis, 1.2236 + aVerticalPercentBasis, aFrameType); 1.2237 + } 1.2238 + 1.2239 + if (isThemed) { 1.2240 + nsIntMargin widget; 1.2241 + presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), 1.2242 + frame, disp->mAppearance, 1.2243 + &widget); 1.2244 + ComputedPhysicalBorderPadding().top = 1.2245 + presContext->DevPixelsToAppUnits(widget.top); 1.2246 + ComputedPhysicalBorderPadding().right = 1.2247 + presContext->DevPixelsToAppUnits(widget.right); 1.2248 + ComputedPhysicalBorderPadding().bottom = 1.2249 + presContext->DevPixelsToAppUnits(widget.bottom); 1.2250 + ComputedPhysicalBorderPadding().left = 1.2251 + presContext->DevPixelsToAppUnits(widget.left); 1.2252 + } 1.2253 + else if (frame->IsSVGText()) { 1.2254 + ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0); 1.2255 + } 1.2256 + else if (aBorder) { // border is an input arg 1.2257 + ComputedPhysicalBorderPadding() = *aBorder; 1.2258 + } 1.2259 + else { 1.2260 + ComputedPhysicalBorderPadding() = frame->StyleBorder()->GetComputedBorder(); 1.2261 + } 1.2262 + ComputedPhysicalBorderPadding() += ComputedPhysicalPadding(); 1.2263 + 1.2264 + if (aFrameType == nsGkAtoms::tableFrame) { 1.2265 + nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame); 1.2266 + 1.2267 + if (tableFrame->IsBorderCollapse()) { 1.2268 + // border-collapsed tables don't use any of their padding, and 1.2269 + // only part of their border. We need to do this here before we 1.2270 + // try to do anything like handling 'auto' widths, 1.2271 + // 'box-sizing', or 'auto' margins. 1.2272 + ComputedPhysicalPadding().SizeTo(0,0,0,0); 1.2273 + ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder(); 1.2274 + } 1.2275 + 1.2276 + // The margin is inherited to the outer table frame via 1.2277 + // the ::-moz-table-outer rule in ua.css. 1.2278 + ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); 1.2279 + } else if (aFrameType == nsGkAtoms::scrollbarFrame) { 1.2280 + // scrollbars may have had their width or height smashed to zero 1.2281 + // by the associated scrollframe, in which case we must not report 1.2282 + // any padding or border. 1.2283 + nsSize size(frame->GetSize()); 1.2284 + if (size.width == 0 || size.height == 0) { 1.2285 + ComputedPhysicalPadding().SizeTo(0,0,0,0); 1.2286 + ComputedPhysicalBorderPadding().SizeTo(0,0,0,0); 1.2287 + } 1.2288 + } 1.2289 + ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp, 1.2290 + ComputedPhysicalPadding()); 1.2291 +} 1.2292 + 1.2293 +// This code enforces section 10.3.3 of the CSS2 spec for this formula: 1.2294 +// 1.2295 +// 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 1.2296 +// 'padding-right' + 'border-right-width' + 'margin-right' 1.2297 +// = width of containing block 1.2298 +// 1.2299 +// Note: the width unit is not auto when this is called 1.2300 +void 1.2301 +nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth, 1.2302 + nscoord aComputedWidth, 1.2303 + nsIAtom* aFrameType) 1.2304 +{ 1.2305 + NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aComputedWidth && 1.2306 + NS_UNCONSTRAINEDSIZE != aAvailWidth, 1.2307 + "have unconstrained width; this should only result from " 1.2308 + "very large sizes, not attempts at intrinsic width " 1.2309 + "calculation"); 1.2310 + 1.2311 + nscoord sum = ComputedPhysicalMargin().left + ComputedPhysicalBorderPadding().left + 1.2312 + aComputedWidth + ComputedPhysicalBorderPadding().right + ComputedPhysicalMargin().right; 1.2313 + if (sum == aAvailWidth) 1.2314 + // The sum is already correct 1.2315 + return; 1.2316 + 1.2317 + // Determine the left and right margin values. The width value 1.2318 + // remains constant while we do this. 1.2319 + 1.2320 + // Calculate how much space is available for margins 1.2321 + nscoord availMarginSpace = aAvailWidth - sum; 1.2322 + 1.2323 + // If the available margin space is negative, then don't follow the 1.2324 + // usual overconstraint rules. 1.2325 + if (availMarginSpace < 0) { 1.2326 + if (mCBReflowState && 1.2327 + mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) { 1.2328 + ComputedPhysicalMargin().left += availMarginSpace; 1.2329 + } else { 1.2330 + ComputedPhysicalMargin().right += availMarginSpace; 1.2331 + } 1.2332 + return; 1.2333 + } 1.2334 + 1.2335 + // The css2 spec clearly defines how block elements should behave 1.2336 + // in section 10.3.3. 1.2337 + bool isAutoLeftMargin = 1.2338 + eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); 1.2339 + bool isAutoRightMargin = 1.2340 + eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); 1.2341 + if (!isAutoLeftMargin && !isAutoRightMargin) { 1.2342 + // Neither margin is 'auto' so we're over constrained. Use the 1.2343 + // 'direction' property of the parent to tell which margin to 1.2344 + // ignore 1.2345 + // First check if there is an HTML alignment that we should honor 1.2346 + const nsHTMLReflowState* prs = parentReflowState; 1.2347 + if (aFrameType == nsGkAtoms::tableFrame) { 1.2348 + NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame, 1.2349 + "table not inside outer table"); 1.2350 + // Center the table within the outer table based on the alignment 1.2351 + // of the outer table's parent. 1.2352 + prs = prs->parentReflowState; 1.2353 + } 1.2354 + if (prs && 1.2355 + (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || 1.2356 + prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || 1.2357 + prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) { 1.2358 + isAutoLeftMargin = 1.2359 + prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT; 1.2360 + isAutoRightMargin = 1.2361 + prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT; 1.2362 + } 1.2363 + // Otherwise apply the CSS rules, and ignore one margin by forcing 1.2364 + // it to 'auto', depending on 'direction'. 1.2365 + else if (mCBReflowState && 1.2366 + NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) { 1.2367 + isAutoLeftMargin = true; 1.2368 + } 1.2369 + else { 1.2370 + isAutoRightMargin = true; 1.2371 + } 1.2372 + } 1.2373 + 1.2374 + // Logic which is common to blocks and tables 1.2375 + // The computed margins need not be zero because the 'auto' could come from 1.2376 + // overconstraint or from HTML alignment so values need to be accumulated 1.2377 + 1.2378 + if (isAutoLeftMargin) { 1.2379 + if (isAutoRightMargin) { 1.2380 + // Both margins are 'auto' so the computed addition should be equal 1.2381 + nscoord forLeft = availMarginSpace / 2; 1.2382 + ComputedPhysicalMargin().left += forLeft; 1.2383 + ComputedPhysicalMargin().right += availMarginSpace - forLeft; 1.2384 + } else { 1.2385 + ComputedPhysicalMargin().left += availMarginSpace; 1.2386 + } 1.2387 + } else if (isAutoRightMargin) { 1.2388 + ComputedPhysicalMargin().right += availMarginSpace; 1.2389 + } 1.2390 +} 1.2391 + 1.2392 +#define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight 1.2393 +// For "normal" we use the font's normal line height (em height + leading). 1.2394 +// If both internal leading and external leading specified by font itself 1.2395 +// are zeros, we should compensate this by creating extra (external) leading 1.2396 +// in eCompensateLeading mode. This is necessary because without this 1.2397 +// compensation, normal line height might looks too tight. 1.2398 + 1.2399 +// For risk management, we use preference to control the behavior, and 1.2400 +// eNoExternalLeading is the old behavior. 1.2401 +static nscoord 1.2402 +GetNormalLineHeight(nsFontMetrics* aFontMetrics) 1.2403 +{ 1.2404 + NS_PRECONDITION(nullptr != aFontMetrics, "no font metrics"); 1.2405 + 1.2406 + nscoord normalLineHeight; 1.2407 + 1.2408 + nscoord externalLeading = aFontMetrics->ExternalLeading(); 1.2409 + nscoord internalLeading = aFontMetrics->InternalLeading(); 1.2410 + nscoord emHeight = aFontMetrics->EmHeight(); 1.2411 + switch (GetNormalLineHeightCalcControl()) { 1.2412 + case eIncludeExternalLeading: 1.2413 + normalLineHeight = emHeight+ internalLeading + externalLeading; 1.2414 + break; 1.2415 + case eCompensateLeading: 1.2416 + if (!internalLeading && !externalLeading) 1.2417 + normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR); 1.2418 + else 1.2419 + normalLineHeight = emHeight+ internalLeading + externalLeading; 1.2420 + break; 1.2421 + default: 1.2422 + //case eNoExternalLeading: 1.2423 + normalLineHeight = emHeight + internalLeading; 1.2424 + } 1.2425 + return normalLineHeight; 1.2426 +} 1.2427 + 1.2428 +static inline nscoord 1.2429 +ComputeLineHeight(nsStyleContext* aStyleContext, 1.2430 + nscoord aBlockHeight, 1.2431 + float aFontSizeInflation) 1.2432 +{ 1.2433 + const nsStyleCoord& lhCoord = aStyleContext->StyleText()->mLineHeight; 1.2434 + 1.2435 + if (lhCoord.GetUnit() == eStyleUnit_Coord) { 1.2436 + nscoord result = lhCoord.GetCoordValue(); 1.2437 + if (aFontSizeInflation != 1.0f) { 1.2438 + result = NSToCoordRound(result * aFontSizeInflation); 1.2439 + } 1.2440 + return result; 1.2441 + } 1.2442 + 1.2443 + if (lhCoord.GetUnit() == eStyleUnit_Factor) 1.2444 + // For factor units the computed value of the line-height property 1.2445 + // is found by multiplying the factor by the font's computed size 1.2446 + // (adjusted for min-size prefs and text zoom). 1.2447 + return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation * 1.2448 + aStyleContext->StyleFont()->mFont.size); 1.2449 + 1.2450 + NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal || 1.2451 + lhCoord.GetUnit() == eStyleUnit_Enumerated, 1.2452 + "bad line-height unit"); 1.2453 + 1.2454 + if (lhCoord.GetUnit() == eStyleUnit_Enumerated) { 1.2455 + NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, 1.2456 + "bad line-height value"); 1.2457 + if (aBlockHeight != NS_AUTOHEIGHT) { 1.2458 + return aBlockHeight; 1.2459 + } 1.2460 + } 1.2461 + 1.2462 + nsRefPtr<nsFontMetrics> fm; 1.2463 + nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, 1.2464 + getter_AddRefs(fm), 1.2465 + aFontSizeInflation); 1.2466 + return GetNormalLineHeight(fm); 1.2467 +} 1.2468 + 1.2469 +nscoord 1.2470 +nsHTMLReflowState::CalcLineHeight() const 1.2471 +{ 1.2472 + nscoord blockHeight = 1.2473 + nsLayoutUtils::IsNonWrapperBlock(frame) ? ComputedHeight() : 1.2474 + (mCBReflowState ? mCBReflowState->ComputedHeight() : NS_AUTOHEIGHT); 1.2475 + 1.2476 + return CalcLineHeight(frame->GetContent(), frame->StyleContext(), blockHeight, 1.2477 + nsLayoutUtils::FontSizeInflationFor(frame)); 1.2478 +} 1.2479 + 1.2480 +/* static */ nscoord 1.2481 +nsHTMLReflowState::CalcLineHeight(nsIContent* aContent, 1.2482 + nsStyleContext* aStyleContext, 1.2483 + nscoord aBlockHeight, 1.2484 + float aFontSizeInflation) 1.2485 +{ 1.2486 + NS_PRECONDITION(aStyleContext, "Must have a style context"); 1.2487 + 1.2488 + nscoord lineHeight = 1.2489 + ComputeLineHeight(aStyleContext, aBlockHeight, aFontSizeInflation); 1.2490 + 1.2491 + NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); 1.2492 + 1.2493 + HTMLInputElement* input = HTMLInputElement::FromContentOrNull(aContent); 1.2494 + if (input && input->IsSingleLineTextControl()) { 1.2495 + // For Web-compatibility, single-line text input elements cannot 1.2496 + // have a line-height smaller than one. 1.2497 + nscoord lineHeightOne = 1.2498 + aFontSizeInflation * aStyleContext->StyleFont()->mFont.size; 1.2499 + if (lineHeight < lineHeightOne) { 1.2500 + lineHeight = lineHeightOne; 1.2501 + } 1.2502 + } 1.2503 + 1.2504 + return lineHeight; 1.2505 +} 1.2506 + 1.2507 +bool 1.2508 +nsCSSOffsetState::ComputeMargin(nscoord aHorizontalPercentBasis, 1.2509 + nscoord aVerticalPercentBasis) 1.2510 +{ 1.2511 + // SVG text frames have no margin. 1.2512 + if (frame->IsSVGText()) { 1.2513 + return false; 1.2514 + } 1.2515 + 1.2516 + // If style style can provide us the margin directly, then use it. 1.2517 + const nsStyleMargin *styleMargin = frame->StyleMargin(); 1.2518 + bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin()); 1.2519 + if (isCBDependent) { 1.2520 + // We have to compute the value 1.2521 + ComputedPhysicalMargin().left = nsLayoutUtils:: 1.2522 + ComputeCBDependentValue(aHorizontalPercentBasis, 1.2523 + styleMargin->mMargin.GetLeft()); 1.2524 + ComputedPhysicalMargin().right = nsLayoutUtils:: 1.2525 + ComputeCBDependentValue(aHorizontalPercentBasis, 1.2526 + styleMargin->mMargin.GetRight()); 1.2527 + 1.2528 + ComputedPhysicalMargin().top = nsLayoutUtils:: 1.2529 + ComputeCBDependentValue(aVerticalPercentBasis, 1.2530 + styleMargin->mMargin.GetTop()); 1.2531 + ComputedPhysicalMargin().bottom = nsLayoutUtils:: 1.2532 + ComputeCBDependentValue(aVerticalPercentBasis, 1.2533 + styleMargin->mMargin.GetBottom()); 1.2534 + } 1.2535 + 1.2536 + nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(frame); 1.2537 + 1.2538 + if (marginAdjustment > 0) { 1.2539 + const nsStyleVisibility* visibility = frame->StyleVisibility(); 1.2540 + if (visibility->mDirection == NS_STYLE_DIRECTION_RTL) { 1.2541 + ComputedPhysicalMargin().right = ComputedPhysicalMargin().right + marginAdjustment; 1.2542 + } else { 1.2543 + ComputedPhysicalMargin().left = ComputedPhysicalMargin().left + marginAdjustment; 1.2544 + } 1.2545 + } 1.2546 + 1.2547 + return isCBDependent; 1.2548 +} 1.2549 + 1.2550 +bool 1.2551 +nsCSSOffsetState::ComputePadding(nscoord aHorizontalPercentBasis, 1.2552 + nscoord aVerticalPercentBasis, 1.2553 + nsIAtom* aFrameType) 1.2554 +{ 1.2555 + // If style can provide us the padding directly, then use it. 1.2556 + const nsStylePadding *stylePadding = frame->StylePadding(); 1.2557 + bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding()); 1.2558 + // a table row/col group, row/col doesn't have padding 1.2559 + // XXXldb Neither do border-collapse tables. 1.2560 + if (nsGkAtoms::tableRowGroupFrame == aFrameType || 1.2561 + nsGkAtoms::tableColGroupFrame == aFrameType || 1.2562 + nsGkAtoms::tableRowFrame == aFrameType || 1.2563 + nsGkAtoms::tableColFrame == aFrameType) { 1.2564 + ComputedPhysicalPadding().SizeTo(0,0,0,0); 1.2565 + } 1.2566 + else if (isCBDependent) { 1.2567 + // We have to compute the value 1.2568 + // clamp negative calc() results to 0 1.2569 + ComputedPhysicalPadding().left = std::max(0, nsLayoutUtils:: 1.2570 + ComputeCBDependentValue(aHorizontalPercentBasis, 1.2571 + stylePadding->mPadding.GetLeft())); 1.2572 + ComputedPhysicalPadding().right = std::max(0, nsLayoutUtils:: 1.2573 + ComputeCBDependentValue(aHorizontalPercentBasis, 1.2574 + stylePadding->mPadding.GetRight())); 1.2575 + 1.2576 + ComputedPhysicalPadding().top = std::max(0, nsLayoutUtils:: 1.2577 + ComputeCBDependentValue(aVerticalPercentBasis, 1.2578 + stylePadding->mPadding.GetTop())); 1.2579 + ComputedPhysicalPadding().bottom = std::max(0, nsLayoutUtils:: 1.2580 + ComputeCBDependentValue(aVerticalPercentBasis, 1.2581 + stylePadding->mPadding.GetBottom())); 1.2582 + } 1.2583 + return isCBDependent; 1.2584 +} 1.2585 + 1.2586 +void 1.2587 +nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, 1.2588 + nscoord aContainingBlockHeight, 1.2589 + const nsHTMLReflowState* aContainingBlockRS) 1.2590 +{ 1.2591 + ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth, 1.2592 + mStylePosition->mBoxSizing, 1.2593 + mStylePosition->mMinWidth); 1.2594 + 1.2595 + if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) { 1.2596 + // Specified value of 'none' 1.2597 + ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE; // no limit 1.2598 + } else { 1.2599 + ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth, 1.2600 + mStylePosition->mBoxSizing, 1.2601 + mStylePosition->mMaxWidth); 1.2602 + } 1.2603 + 1.2604 + // If the computed value of 'min-width' is greater than the value of 1.2605 + // 'max-width', 'max-width' is set to the value of 'min-width' 1.2606 + if (ComputedMinWidth() > ComputedMaxWidth()) { 1.2607 + ComputedMaxWidth() = ComputedMinWidth(); 1.2608 + } 1.2609 + 1.2610 + // Check for percentage based values and a containing block height that 1.2611 + // depends on the content height. Treat them like 'auto' 1.2612 + // Likewise, check for calc() with percentages on internal table elements; 1.2613 + // that's treated as 'auto' too. 1.2614 + // Likewise, if we're a child of a flex container who's measuring our 1.2615 + // intrinsic height, then we want to disregard our min-height. 1.2616 + 1.2617 + const nsStyleCoord &minHeight = mStylePosition->mMinHeight; 1.2618 + if ((NS_AUTOHEIGHT == aContainingBlockHeight && 1.2619 + minHeight.HasPercent()) || 1.2620 + (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && 1.2621 + minHeight.IsCalcUnit() && minHeight.CalcHasPercent()) || 1.2622 + mFlags.mIsFlexContainerMeasuringHeight) { 1.2623 + ComputedMinHeight() = 0; 1.2624 + } else { 1.2625 + ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight, 1.2626 + mStylePosition->mBoxSizing, 1.2627 + minHeight); 1.2628 + } 1.2629 + const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight; 1.2630 + nsStyleUnit maxHeightUnit = maxHeight.GetUnit(); 1.2631 + if (eStyleUnit_None == maxHeightUnit) { 1.2632 + // Specified value of 'none' 1.2633 + ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; // no limit 1.2634 + } else { 1.2635 + // Check for percentage based values and a containing block height that 1.2636 + // depends on the content height. Treat them like 'none' 1.2637 + // Likewise, check for calc() with percentages on internal table elements; 1.2638 + // that's treated as 'auto' too. 1.2639 + // Likewise, if we're a child of a flex container who's measuring our 1.2640 + // intrinsic height, then we want to disregard our max-height. 1.2641 + if ((NS_AUTOHEIGHT == aContainingBlockHeight && 1.2642 + maxHeight.HasPercent()) || 1.2643 + (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && 1.2644 + maxHeight.IsCalcUnit() && maxHeight.CalcHasPercent()) || 1.2645 + mFlags.mIsFlexContainerMeasuringHeight) { 1.2646 + ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; 1.2647 + } else { 1.2648 + ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight, 1.2649 + mStylePosition->mBoxSizing, 1.2650 + maxHeight); 1.2651 + } 1.2652 + } 1.2653 + 1.2654 + // If the computed value of 'min-height' is greater than the value of 1.2655 + // 'max-height', 'max-height' is set to the value of 'min-height' 1.2656 + if (ComputedMinHeight() > ComputedMaxHeight()) { 1.2657 + ComputedMaxHeight() = ComputedMinHeight(); 1.2658 + } 1.2659 +} 1.2660 + 1.2661 +void 1.2662 +nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics, 1.2663 + nsReflowStatus* aStatus) const 1.2664 +{ 1.2665 + if (AvailableHeight() != NS_UNCONSTRAINEDSIZE && 1.2666 + AvailableHeight() < aMetrics.Height() && 1.2667 + !mFlags.mIsTopOfPage) { 1.2668 + *aStatus |= NS_FRAME_TRUNCATED; 1.2669 + } else { 1.2670 + *aStatus &= ~NS_FRAME_TRUNCATED; 1.2671 + } 1.2672 +} 1.2673 + 1.2674 +bool 1.2675 +nsHTMLReflowState::IsFloating() const 1.2676 +{ 1.2677 + return mStyleDisplay->IsFloating(frame); 1.2678 +} 1.2679 + 1.2680 +uint8_t 1.2681 +nsHTMLReflowState::GetDisplay() const 1.2682 +{ 1.2683 + return mStyleDisplay->GetDisplay(frame); 1.2684 +}