michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* struct containing the input to nsIFrame::Reflow */ michael@0: michael@0: #include "nsHTMLReflowState.h" michael@0: michael@0: #include "nsStyleConsts.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsFrame.h" michael@0: #include "nsIContent.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsFontMetrics.h" michael@0: #include "nsBlockFrame.h" michael@0: #include "nsLineBox.h" michael@0: #include "nsFlexContainerFrame.h" michael@0: #include "nsImageFrame.h" michael@0: #include "nsTableFrame.h" michael@0: #include "nsTableCellFrame.h" michael@0: #include "nsIPercentHeightObserver.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsFontInflationData.h" michael@0: #include "StickyScrollContainer.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include michael@0: #include "mozilla/dom/HTMLInputElement.h" michael@0: michael@0: #ifdef DEBUG michael@0: #undef NOISY_VERTICAL_ALIGN michael@0: #else michael@0: #undef NOISY_VERTICAL_ALIGN michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::css; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::layout; michael@0: michael@0: enum eNormalLineHeightControl { michael@0: eUninitialized = -1, michael@0: eNoExternalLeading = 0, // does not include external leading michael@0: eIncludeExternalLeading, // use whatever value font vendor provides michael@0: eCompensateLeading // compensate leading if leading provided by font vendor is not enough michael@0: }; michael@0: michael@0: static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized; michael@0: michael@0: // Initialize a root reflow state with a rendering context to michael@0: // use for measuring things. michael@0: nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsRenderingContext* aRenderingContext, michael@0: const nsSize& aAvailableSpace, michael@0: uint32_t aFlags) michael@0: : nsCSSOffsetState(aFrame, aRenderingContext) michael@0: , mBlockDelta(0) michael@0: , mReflowDepth(0) michael@0: { michael@0: NS_PRECONDITION(aRenderingContext, "no rendering context"); michael@0: MOZ_ASSERT(aPresContext, "no pres context"); michael@0: MOZ_ASSERT(aFrame, "no frame"); michael@0: MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); michael@0: parentReflowState = nullptr; michael@0: AvailableWidth() = aAvailableSpace.width; michael@0: AvailableHeight() = aAvailableSpace.height; michael@0: mFloatManager = nullptr; michael@0: mLineLayout = nullptr; michael@0: memset(&mFlags, 0, sizeof(mFlags)); michael@0: mDiscoveredClearance = nullptr; michael@0: mPercentHeightObserver = nullptr; michael@0: michael@0: if (aFlags & DUMMY_PARENT_REFLOW_STATE) { michael@0: mFlags.mDummyParentReflowState = true; michael@0: } michael@0: michael@0: if (!(aFlags & CALLER_WILL_INIT)) { michael@0: Init(aPresContext); michael@0: } michael@0: } michael@0: michael@0: static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) michael@0: { michael@0: nsIFrame* frameNext = aFrame->GetNextInFlow(); michael@0: nsIFrame* parentNext = aParent->GetNextInFlow(); michael@0: return frameNext && parentNext && frameNext->GetParent() == parentNext; michael@0: } michael@0: michael@0: /** michael@0: * Adjusts the margin for a list (ol, ul), if necessary, depending on michael@0: * font inflation settings. Unfortunately, because bullets from a list are michael@0: * placed in the margin area, we only have ~40px in which to place the michael@0: * bullets. When they are inflated, however, this causes problems, since michael@0: * the text takes up more space than is available in the margin. michael@0: * michael@0: * This method will return a small amount (in app units) by which the michael@0: * margin can be adjusted, so that the space is available for list michael@0: * bullets to be rendered with font inflation enabled. michael@0: */ michael@0: static nscoord michael@0: FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) michael@0: { michael@0: float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); michael@0: if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) { michael@0: const nsBlockFrame* blockFrame = static_cast(aFrame); michael@0: const nsStyleList* styleList = aFrame->StyleList(); michael@0: michael@0: // We only want to adjust the margins if we're dealing with an ordered michael@0: // list. michael@0: if (inflation > 1.0f && michael@0: blockFrame->HasBullet() && michael@0: styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE && michael@0: styleList->mListStyleType != NS_STYLE_LIST_STYLE_DISC && michael@0: styleList->mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE && michael@0: styleList->mListStyleType != NS_STYLE_LIST_STYLE_SQUARE && michael@0: inflation > 1.0f) { michael@0: michael@0: // The HTML spec states that the default padding for ordered lists begins michael@0: // at 40px, indicating that we have 40px of space to place a bullet. When michael@0: // performing font inflation calculations, we add space equivalent to this, michael@0: // but simply inflated at the same amount as the text, in app units. michael@0: return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: // NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid michael@0: // item, we need to make it take the containing-block height as well as the michael@0: // width, since flex items and grid items resolve vertical percent margins michael@0: // and padding against the containing-block height, rather than its width. michael@0: nsCSSOffsetState::nsCSSOffsetState(nsIFrame *aFrame, michael@0: nsRenderingContext *aRenderingContext, michael@0: nscoord aContainingBlockWidth) michael@0: : frame(aFrame) michael@0: , rendContext(aRenderingContext) michael@0: , mWritingMode(aFrame->GetWritingMode()) michael@0: { michael@0: MOZ_ASSERT(!aFrame->IsFlexItem(), michael@0: "We're about to resolve vertical percent margin & padding " michael@0: "values against CB width, which is incorrect for flex items"); michael@0: InitOffsets(aContainingBlockWidth, aContainingBlockWidth, frame->GetType()); michael@0: } michael@0: michael@0: // Initialize a reflow state for a child frame's reflow. Some state michael@0: // is copied from the parent reflow state; the remaining state is michael@0: // computed. michael@0: nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aParentReflowState, michael@0: nsIFrame* aFrame, michael@0: const nsSize& aAvailableSpace, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: uint32_t aFlags) michael@0: : nsCSSOffsetState(aFrame, aParentReflowState.rendContext) michael@0: , mBlockDelta(0) michael@0: , mReflowDepth(aParentReflowState.mReflowDepth + 1) michael@0: , mFlags(aParentReflowState.mFlags) michael@0: { michael@0: MOZ_ASSERT(aPresContext, "no pres context"); michael@0: MOZ_ASSERT(aFrame, "no frame"); michael@0: MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); michael@0: NS_PRECONDITION((aContainingBlockWidth == -1) == michael@0: (aContainingBlockHeight == -1), michael@0: "cb width and height should only be non-default together"); michael@0: NS_PRECONDITION(!mFlags.mSpecialHeightReflow || michael@0: !NS_SUBTREE_DIRTY(aFrame), michael@0: "frame should be clean when getting special height reflow"); michael@0: michael@0: parentReflowState = &aParentReflowState; michael@0: michael@0: // If the parent is dirty, then the child is as well. michael@0: // XXX Are the other cases where the parent reflows a child a second michael@0: // time, as a resize? michael@0: if (!mFlags.mSpecialHeightReflow) michael@0: frame->AddStateBits(parentReflowState->frame->GetStateBits() & michael@0: NS_FRAME_IS_DIRTY); michael@0: michael@0: AvailableWidth() = aAvailableSpace.width; michael@0: AvailableHeight() = aAvailableSpace.height; michael@0: michael@0: mFloatManager = aParentReflowState.mFloatManager; michael@0: if (frame->IsFrameOfType(nsIFrame::eLineParticipant)) michael@0: mLineLayout = aParentReflowState.mLineLayout; michael@0: else michael@0: mLineLayout = nullptr; michael@0: michael@0: // Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in michael@0: // this constructor's init list, so the only flags that we need to explicitly michael@0: // initialize here are those that may need a value other than our parent's. michael@0: mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched && michael@0: CheckNextInFlowParenthood(aFrame, aParentReflowState.frame); michael@0: mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; michael@0: mFlags.mHasClearance = false; michael@0: mFlags.mIsColumnBalancing = false; michael@0: mFlags.mIsFlexContainerMeasuringHeight = false; michael@0: mFlags.mDummyParentReflowState = false; michael@0: michael@0: mDiscoveredClearance = nullptr; michael@0: mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && michael@0: aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) michael@0: ? aParentReflowState.mPercentHeightObserver : nullptr; michael@0: michael@0: if (aFlags & DUMMY_PARENT_REFLOW_STATE) { michael@0: mFlags.mDummyParentReflowState = true; michael@0: } michael@0: michael@0: if (!(aFlags & CALLER_WILL_INIT)) { michael@0: Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight); michael@0: } michael@0: } michael@0: michael@0: inline nscoord michael@0: nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, michael@0: nscoord aContentEdgeToBoxSizing, michael@0: nscoord aBoxSizingToMarginEdge, michael@0: const nsStyleCoord& aCoord) michael@0: { michael@0: return nsLayoutUtils::ComputeWidthValue(rendContext, frame, michael@0: aContainingBlockWidth, michael@0: aContentEdgeToBoxSizing, michael@0: aBoxSizingToMarginEdge, michael@0: aCoord); michael@0: } michael@0: michael@0: nscoord michael@0: nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, michael@0: uint8_t aBoxSizing, michael@0: const nsStyleCoord& aCoord) michael@0: { michael@0: nscoord inside = 0, outside = ComputedPhysicalBorderPadding().LeftRight() + michael@0: ComputedPhysicalMargin().LeftRight(); michael@0: switch (aBoxSizing) { michael@0: case NS_STYLE_BOX_SIZING_BORDER: michael@0: inside = ComputedPhysicalBorderPadding().LeftRight(); michael@0: break; michael@0: case NS_STYLE_BOX_SIZING_PADDING: michael@0: inside = ComputedPhysicalPadding().LeftRight(); michael@0: break; michael@0: } michael@0: outside -= inside; michael@0: michael@0: return ComputeWidthValue(aContainingBlockWidth, inside, michael@0: outside, aCoord); michael@0: } michael@0: michael@0: nscoord michael@0: nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight, michael@0: uint8_t aBoxSizing, michael@0: const nsStyleCoord& aCoord) michael@0: { michael@0: nscoord inside = 0; michael@0: switch (aBoxSizing) { michael@0: case NS_STYLE_BOX_SIZING_BORDER: michael@0: inside = ComputedPhysicalBorderPadding().TopBottom(); michael@0: break; michael@0: case NS_STYLE_BOX_SIZING_PADDING: michael@0: inside = ComputedPhysicalPadding().TopBottom(); michael@0: break; michael@0: } michael@0: return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight, michael@0: inside, aCoord); michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth) michael@0: { michael@0: NS_ASSERTION(frame, "Must have a frame!"); michael@0: // It'd be nice to assert that |frame| is not in reflow, but this fails for michael@0: // two reasons: michael@0: // michael@0: // 1) Viewport frames reset the computed width on a copy of their reflow michael@0: // state when reflowing fixed-pos kids. In that case we actually don't michael@0: // want to mess with the resize flags, because comparing the frame's rect michael@0: // to the munged computed width is pointless. michael@0: // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow michael@0: // state is not used to reflow the parent, but just as a parent for the michael@0: // frame's own reflow state. So given a nsBoxFrame inside some non-XUL michael@0: // (like a text control, for example), we'll end up creating a reflow michael@0: // state for the parent while the parent is reflowing. michael@0: michael@0: NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width"); michael@0: if (ComputedWidth() != aComputedWidth) { michael@0: ComputedWidth() = aComputedWidth; michael@0: nsIAtom* frameType = frame->GetType(); michael@0: if (frameType != nsGkAtoms::viewportFrame) { // Or check GetParent()? michael@0: InitResizeFlags(frame->PresContext(), frameType); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight) michael@0: { michael@0: NS_ASSERTION(frame, "Must have a frame!"); michael@0: // It'd be nice to assert that |frame| is not in reflow, but this fails michael@0: // because: michael@0: // michael@0: // nsFrame::BoxReflow creates a reflow state for its parent. This reflow michael@0: // state is not used to reflow the parent, but just as a parent for the michael@0: // frame's own reflow state. So given a nsBoxFrame inside some non-XUL michael@0: // (like a text control, for example), we'll end up creating a reflow michael@0: // state for the parent while the parent is reflowing. michael@0: michael@0: NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height"); michael@0: if (ComputedHeight() != aComputedHeight) { michael@0: ComputedHeight() = aComputedHeight; michael@0: InitResizeFlags(frame->PresContext(), frame->GetType()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::Init(nsPresContext* aPresContext, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding) michael@0: { michael@0: NS_WARN_IF_FALSE(AvailableWidth() != NS_UNCONSTRAINEDSIZE, michael@0: "have unconstrained width; this should only result from " michael@0: "very large sizes, not attempts at intrinsic width " michael@0: "calculation"); michael@0: michael@0: mStylePosition = frame->StylePosition(); michael@0: mStyleDisplay = frame->StyleDisplay(); michael@0: mStyleVisibility = frame->StyleVisibility(); michael@0: mStyleBorder = frame->StyleBorder(); michael@0: mStyleMargin = frame->StyleMargin(); michael@0: mStylePadding = frame->StylePadding(); michael@0: mStyleText = frame->StyleText(); michael@0: michael@0: nsIAtom* type = frame->GetType(); michael@0: michael@0: InitFrameType(type); michael@0: InitCBReflowState(); michael@0: michael@0: InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, michael@0: aBorder, aPadding, type); michael@0: michael@0: InitResizeFlags(aPresContext, type); michael@0: michael@0: nsIFrame *parent = frame->GetParent(); michael@0: if (parent && michael@0: (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) && michael@0: !(parent->GetType() == nsGkAtoms::scrollFrame && michael@0: parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) { michael@0: frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: } else if (type == nsGkAtoms::svgForeignObjectFrame) { michael@0: // An SVG foreignObject frame is inherently constrained height. michael@0: frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: } else if ((mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto || michael@0: mStylePosition->mMaxHeight.GetUnit() != eStyleUnit_None) && michael@0: // Don't set NS_FRAME_IN_CONSTRAINED_HEIGHT on body or html michael@0: // elements. michael@0: (frame->GetContent() && michael@0: !(frame->GetContent()->IsHTML(nsGkAtoms::body) || michael@0: frame->GetContent()->IsHTML(nsGkAtoms::html)))) { michael@0: michael@0: // If our height was specified as a percentage, then this could michael@0: // actually resolve to 'auto', based on: michael@0: // http://www.w3.org/TR/CSS21/visudet.html#the-height-property michael@0: nsIFrame* containingBlk = frame; michael@0: while (containingBlk) { michael@0: const nsStylePosition* stylePos = containingBlk->StylePosition(); michael@0: if ((stylePos->mHeight.IsCoordPercentCalcUnit() && michael@0: !stylePos->mHeight.HasPercent()) || michael@0: (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && michael@0: !stylePos->mMaxHeight.HasPercent())) { michael@0: frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: break; michael@0: } else if ((stylePos->mHeight.IsCoordPercentCalcUnit() && michael@0: stylePos->mHeight.HasPercent()) || michael@0: (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && michael@0: stylePos->mMaxHeight.HasPercent())) { michael@0: if (!(containingBlk = containingBlk->GetContainingBlock())) { michael@0: // If we've reached the top of the tree, then we don't have michael@0: // a constrained height. michael@0: frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: break; michael@0: } michael@0: michael@0: continue; michael@0: } else { michael@0: frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: break; michael@0: } michael@0: } michael@0: } else { michael@0: frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); michael@0: } michael@0: michael@0: NS_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE && michael@0: !frame->IsFrameOfType(nsIFrame::eReplaced)) || michael@0: type == nsGkAtoms::textFrame || michael@0: ComputedWidth() != NS_UNCONSTRAINEDSIZE, michael@0: "have unconstrained width; this should only result from " michael@0: "very large sizes, not attempts at intrinsic width " michael@0: "calculation"); michael@0: } michael@0: michael@0: void nsHTMLReflowState::InitCBReflowState() michael@0: { michael@0: if (!parentReflowState) { michael@0: mCBReflowState = nullptr; michael@0: return; michael@0: } michael@0: michael@0: if (parentReflowState->frame == frame->GetContainingBlock()) { michael@0: // Inner table frames need to use the containing block of the outer michael@0: // table frame. michael@0: if (frame->GetType() == nsGkAtoms::tableFrame) { michael@0: mCBReflowState = parentReflowState->mCBReflowState; michael@0: } else { michael@0: mCBReflowState = parentReflowState; michael@0: } michael@0: } else { michael@0: mCBReflowState = parentReflowState->mCBReflowState; michael@0: } michael@0: } michael@0: michael@0: /* Check whether CalcQuirkContainingBlockHeight would stop on the michael@0: * given reflow state, using its block as a height. (essentially michael@0: * returns false for any case in which CalcQuirkContainingBlockHeight michael@0: * has a "continue" in its main loop.) michael@0: * michael@0: * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses michael@0: * this function as well michael@0: */ michael@0: static bool michael@0: IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType) michael@0: { michael@0: if (nsGkAtoms::blockFrame == aFrameType || michael@0: #ifdef MOZ_XUL michael@0: nsGkAtoms::XULLabelFrame == aFrameType || michael@0: #endif michael@0: nsGkAtoms::scrollFrame == aFrameType) { michael@0: // Note: This next condition could change due to a style change, michael@0: // but that would cause a style reflow anyway, which means we're ok. michael@0: if (NS_AUTOHEIGHT == rs->ComputedHeight()) { michael@0: if (!rs->frame->IsAbsolutelyPositioned()) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType) michael@0: { michael@0: bool isHResize = (frame->GetSize().width != michael@0: ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) || michael@0: aPresContext->PresShell()->IsReflowOnZoomPending(); michael@0: michael@0: if ((frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) && michael@0: nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { michael@0: // Create our font inflation data if we don't have it already, and michael@0: // give it our current width information. michael@0: bool dirty = nsFontInflationData::UpdateFontInflationDataWidthFor(*this) && michael@0: // Avoid running this at the box-to-block interface michael@0: // (where we shouldn't be inflating anyway, and where michael@0: // reflow state construction is probably to construct a michael@0: // dummy parent reflow state anyway). michael@0: !mFlags.mDummyParentReflowState; michael@0: michael@0: if (dirty || (!frame->GetParent() && isHResize)) { michael@0: // When font size inflation is enabled, a change in either: michael@0: // * the effective width of a font inflation flow root michael@0: // * the width of the frame michael@0: // needs to cause a dirty reflow since they change the font size michael@0: // inflation calculations, which in turn change the size of text, michael@0: // line-heights, etc. This is relatively similar to a classic michael@0: // case of style change reflow, except that because inflation michael@0: // doesn't affect the intrinsic sizing codepath, there's no need michael@0: // to invalidate intrinsic sizes. michael@0: // michael@0: // Note that this makes horizontal resizing a good bit more michael@0: // expensive. However, font size inflation is targeted at a set of michael@0: // devices (zoom-and-pan devices) where the main use case for michael@0: // horizontal resizing needing to be efficient (window resizing) is michael@0: // not present. It does still increase the cost of dynamic changes michael@0: // caused by script where a style or content change in one place michael@0: // causes a resize in another (e.g., rebalancing a table). michael@0: michael@0: // FIXME: This isn't so great for the cases where michael@0: // nsHTMLReflowState::SetComputedWidth is called, if the first time michael@0: // we go through InitResizeFlags we set mHResize to true, and then michael@0: // the second time we'd set it to false even without the michael@0: // NS_FRAME_IS_DIRTY bit already set. michael@0: if (frame->GetType() == nsGkAtoms::svgForeignObjectFrame) { michael@0: // Foreign object frames use dirty bits in a special way. michael@0: frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: nsIFrame *kid = frame->GetFirstPrincipalChild(); michael@0: if (kid) { michael@0: kid->AddStateBits(NS_FRAME_IS_DIRTY); michael@0: } michael@0: } else { michael@0: frame->AddStateBits(NS_FRAME_IS_DIRTY); michael@0: } michael@0: michael@0: // Mark intrinsic widths on all descendants dirty. We need to do michael@0: // this (1) since we're changing the size of text and need to michael@0: // clear text runs on text frames and (2) since we actually are michael@0: // changing some intrinsic widths, but only those that live inside michael@0: // of containers. michael@0: michael@0: // It makes sense to do this for descendants but not ancestors michael@0: // (which is unusual) because we're only changing the unusual michael@0: // inflation-dependent intrinsic widths (i.e., ones computed with michael@0: // nsPresContext::mInflationDisabledForShrinkWrap set to false), michael@0: // which should never affect anything outside of their inflation michael@0: // flow root (or, for that matter, even their inflation michael@0: // container). michael@0: michael@0: // This is also different from what PresShell::FrameNeedsReflow michael@0: // does because it doesn't go through placeholders. It doesn't michael@0: // need to because we're actually doing something that cares about michael@0: // frame tree geometry (the width on an ancestor) rather than michael@0: // style. michael@0: michael@0: nsAutoTArray stack; michael@0: stack.AppendElement(frame); michael@0: michael@0: do { michael@0: nsIFrame *f = stack.ElementAt(stack.Length() - 1); michael@0: stack.RemoveElementAt(stack.Length() - 1); michael@0: michael@0: nsIFrame::ChildListIterator lists(f); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* kid = childFrames.get(); michael@0: kid->MarkIntrinsicWidthsDirty(); michael@0: stack.AppendElement(kid); michael@0: } michael@0: } michael@0: } while (stack.Length() != 0); michael@0: } michael@0: } michael@0: michael@0: mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) && michael@0: isHResize; michael@0: michael@0: // XXX Should we really need to null check mCBReflowState? (We do for michael@0: // at least nsBoxFrame). michael@0: if (IS_TABLE_CELL(aFrameType) && michael@0: (mFlags.mSpecialHeightReflow || michael@0: (frame->FirstInFlow()->GetStateBits() & michael@0: NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && michael@0: (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { michael@0: // Need to set the bit on the cell so that michael@0: // mCBReflowState->mFlags.mVResize is set correctly below when michael@0: // reflowing descendant. michael@0: mFlags.mVResize = true; michael@0: } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) { michael@0: // XXX Is this problematic for relatively positioned inlines acting michael@0: // as containing block for absolutely positioned elements? michael@0: // Possibly; in that case we should at least be checking michael@0: // NS_SUBTREE_DIRTY, I'd think. michael@0: mFlags.mVResize = mCBReflowState->mFlags.mVResize; michael@0: } else if (ComputedHeight() == NS_AUTOHEIGHT) { michael@0: if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && michael@0: mCBReflowState) { michael@0: mFlags.mVResize = mCBReflowState->mFlags.mVResize; michael@0: } else { michael@0: mFlags.mVResize = mFlags.mHResize; michael@0: } michael@0: mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame); michael@0: } else { michael@0: // not 'auto' height michael@0: mFlags.mVResize = frame->GetSize().height != michael@0: ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom(); michael@0: } michael@0: michael@0: bool dependsOnCBHeight = michael@0: (mStylePosition->HeightDependsOnContainer() && michael@0: // FIXME: condition this on not-abspos? michael@0: mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) || michael@0: mStylePosition->MinHeightDependsOnContainer() || michael@0: mStylePosition->MaxHeightDependsOnContainer() || michael@0: mStylePosition->OffsetHasPercent(NS_SIDE_TOP) || michael@0: mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto || michael@0: frame->IsBoxFrame(); michael@0: michael@0: if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { michael@0: NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() == michael@0: NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, michael@0: "bad line-height value"); michael@0: michael@0: // line-height depends on block height michael@0: frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); michael@0: // but only on containing blocks if this frame is not a suitable block michael@0: dependsOnCBHeight |= !nsLayoutUtils::IsNonWrapperBlock(frame); michael@0: } michael@0: michael@0: // If we're the descendant of a table cell that performs special height michael@0: // reflows and we could be the child that requires them, always set michael@0: // the vertical resize in case this is the first pass before the michael@0: // special height reflow. However, don't do this if it actually is michael@0: // the special height reflow, since in that case it will already be michael@0: // set correctly above if we need it set. michael@0: if (!mFlags.mVResize && mCBReflowState && michael@0: (IS_TABLE_CELL(mCBReflowState->frame->GetType()) || michael@0: mCBReflowState->mFlags.mHeightDependsOnAncestorCell) && michael@0: !mCBReflowState->mFlags.mSpecialHeightReflow && michael@0: dependsOnCBHeight) { michael@0: mFlags.mVResize = true; michael@0: mFlags.mHeightDependsOnAncestorCell = true; michael@0: } michael@0: michael@0: // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed. michael@0: michael@0: // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT| michael@0: // &&ed with the percentage height check. However, this doesn't get michael@0: // along with table special height reflows, since a special height michael@0: // reflow (a quirk that makes such percentage heights work on children michael@0: // of table cells) can cause not just a single percentage height to michael@0: // become fixed, but an entire descendant chain of percentage heights michael@0: // to become fixed. michael@0: if (dependsOnCBHeight && mCBReflowState) { michael@0: const nsHTMLReflowState *rs = this; michael@0: bool hitCBReflowState = false; michael@0: do { michael@0: rs = rs->parentReflowState; michael@0: if (!rs) { michael@0: break; michael@0: } michael@0: michael@0: if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) michael@0: break; // no need to go further michael@0: rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); michael@0: michael@0: // Keep track of whether we've hit the containing block, because michael@0: // we need to go at least that far. michael@0: if (rs == mCBReflowState) { michael@0: hitCBReflowState = true; michael@0: } michael@0: michael@0: } while (!hitCBReflowState || michael@0: (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && michael@0: !IsQuirkContainingBlockHeight(rs, rs->frame->GetType()))); michael@0: // Note: We actually don't need to set the michael@0: // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases michael@0: // where we hit the early break statements in michael@0: // CalcQuirkContainingBlockHeight. But it doesn't hurt michael@0: // us to set the bit in these cases. michael@0: michael@0: } michael@0: if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) { michael@0: // If we're reflowing everything, then we'll find out if we need michael@0: // to re-set this. michael@0: frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: nscoord michael@0: nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState) michael@0: { michael@0: const nsHTMLReflowState* rs = aReflowState->mCBReflowState; michael@0: if (!rs) michael@0: return 0; michael@0: return rs->ComputedWidth(); michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) michael@0: { michael@0: const nsStyleDisplay *disp = mStyleDisplay; michael@0: nsCSSFrameType frameType; michael@0: michael@0: // Section 9.7 of the CSS2 spec indicates that absolute position michael@0: // takes precedence over float which takes precedence over display. michael@0: // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right? michael@0: // Make sure the frame was actually moved out of the flow, and don't michael@0: // just assume what the style says, because we might not have had a michael@0: // useful float/absolute containing block michael@0: michael@0: DISPLAY_INIT_TYPE(frame, this); michael@0: michael@0: if (aFrameType == nsGkAtoms::tableFrame) { michael@0: mFrameType = NS_CSS_FRAME_TYPE_BLOCK; michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(frame->StyleDisplay()->IsAbsolutelyPositionedStyle() == michael@0: disp->IsAbsolutelyPositionedStyle(), michael@0: "Unexpected position style"); michael@0: NS_ASSERTION(frame->StyleDisplay()->IsFloatingStyle() == michael@0: disp->IsFloatingStyle(), "Unexpected float style"); michael@0: if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: if (disp->IsAbsolutelyPositioned(frame)) { michael@0: frameType = NS_CSS_FRAME_TYPE_ABSOLUTE; michael@0: //XXXfr hack for making frames behave properly when in overflow container lists michael@0: // see bug 154892; need to revisit later michael@0: if (frame->GetPrevInFlow()) michael@0: frameType = NS_CSS_FRAME_TYPE_BLOCK; michael@0: } michael@0: else if (disp->IsFloating(frame)) { michael@0: frameType = NS_CSS_FRAME_TYPE_FLOATING; michael@0: } else { michael@0: NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP, michael@0: "unknown out of flow frame type"); michael@0: frameType = NS_CSS_FRAME_TYPE_UNKNOWN; michael@0: } michael@0: } michael@0: else { michael@0: switch (GetDisplay()) { michael@0: case NS_STYLE_DISPLAY_BLOCK: michael@0: case NS_STYLE_DISPLAY_LIST_ITEM: michael@0: case NS_STYLE_DISPLAY_TABLE: michael@0: case NS_STYLE_DISPLAY_TABLE_CAPTION: michael@0: case NS_STYLE_DISPLAY_FLEX: michael@0: frameType = NS_CSS_FRAME_TYPE_BLOCK; michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_INLINE: michael@0: case NS_STYLE_DISPLAY_INLINE_BLOCK: michael@0: case NS_STYLE_DISPLAY_INLINE_TABLE: michael@0: case NS_STYLE_DISPLAY_INLINE_BOX: michael@0: case NS_STYLE_DISPLAY_INLINE_XUL_GRID: michael@0: case NS_STYLE_DISPLAY_INLINE_STACK: michael@0: case NS_STYLE_DISPLAY_INLINE_FLEX: michael@0: frameType = NS_CSS_FRAME_TYPE_INLINE; michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_TABLE_CELL: michael@0: case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: michael@0: case NS_STYLE_DISPLAY_TABLE_COLUMN: michael@0: case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP: michael@0: case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: michael@0: case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: michael@0: case NS_STYLE_DISPLAY_TABLE_ROW: michael@0: frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE; michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_NONE: michael@0: default: michael@0: frameType = NS_CSS_FRAME_TYPE_UNKNOWN; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // See if the frame is replaced michael@0: if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) { michael@0: frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType); michael@0: } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) { michael@0: frameType = NS_FRAME_REPLACED(frameType); michael@0: } michael@0: michael@0: mFrameType = frameType; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection, michael@0: nsIFrame* aFrame, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: nsMargin& aComputedOffsets) michael@0: { michael@0: const nsStylePosition* position = aFrame->StylePosition(); michael@0: michael@0: // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right, michael@0: // and 'right' moves the boxes to the left. The computed values are always: michael@0: // left=-right michael@0: bool leftIsAuto = eStyleUnit_Auto == position->mOffset.GetLeftUnit(); michael@0: bool rightIsAuto = eStyleUnit_Auto == position->mOffset.GetRightUnit(); michael@0: michael@0: // If neither 'left' not 'right' are auto, then we're over-constrained and michael@0: // we ignore one of them michael@0: if (!leftIsAuto && !rightIsAuto) { michael@0: if (aCBDirection == NS_STYLE_DIRECTION_RTL) { michael@0: leftIsAuto = true; michael@0: } else { michael@0: rightIsAuto = true; michael@0: } michael@0: } michael@0: michael@0: if (leftIsAuto) { michael@0: if (rightIsAuto) { michael@0: // If both are 'auto' (their initial values), the computed values are 0 michael@0: aComputedOffsets.left = aComputedOffsets.right = 0; michael@0: } else { michael@0: // 'Right' isn't 'auto' so compute its value michael@0: aComputedOffsets.right = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: position->mOffset.GetRight()); michael@0: michael@0: // Computed value for 'left' is minus the value of 'right' michael@0: aComputedOffsets.left = -aComputedOffsets.right; michael@0: } michael@0: michael@0: } else { michael@0: NS_ASSERTION(rightIsAuto, "unexpected specified constraint"); michael@0: michael@0: // 'Left' isn't 'auto' so compute its value michael@0: aComputedOffsets.left = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: position->mOffset.GetLeft()); michael@0: michael@0: // Computed value for 'right' is minus the value of 'left' michael@0: aComputedOffsets.right = -aComputedOffsets.left; michael@0: } michael@0: michael@0: // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties michael@0: // move relatively positioned elements up and down. They also must be each michael@0: // other's negative michael@0: bool topIsAuto = eStyleUnit_Auto == position->mOffset.GetTopUnit(); michael@0: bool bottomIsAuto = eStyleUnit_Auto == position->mOffset.GetBottomUnit(); michael@0: michael@0: // Check for percentage based values and a containing block height that michael@0: // depends on the content height. Treat them like 'auto' michael@0: if (NS_AUTOHEIGHT == aContainingBlockHeight) { michael@0: if (position->OffsetHasPercent(NS_SIDE_TOP)) { michael@0: topIsAuto = true; michael@0: } michael@0: if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) { michael@0: bottomIsAuto = true; michael@0: } michael@0: } michael@0: michael@0: // If neither is 'auto', 'bottom' is ignored michael@0: if (!topIsAuto && !bottomIsAuto) { michael@0: bottomIsAuto = true; michael@0: } michael@0: michael@0: if (topIsAuto) { michael@0: if (bottomIsAuto) { michael@0: // If both are 'auto' (their initial values), the computed values are 0 michael@0: aComputedOffsets.top = aComputedOffsets.bottom = 0; michael@0: } else { michael@0: // 'Bottom' isn't 'auto' so compute its value michael@0: aComputedOffsets.bottom = nsLayoutUtils:: michael@0: ComputeHeightDependentValue(aContainingBlockHeight, michael@0: position->mOffset.GetBottom()); michael@0: michael@0: // Computed value for 'top' is minus the value of 'bottom' michael@0: aComputedOffsets.top = -aComputedOffsets.bottom; michael@0: } michael@0: michael@0: } else { michael@0: NS_ASSERTION(bottomIsAuto, "unexpected specified constraint"); michael@0: michael@0: // 'Top' isn't 'auto' so compute its value michael@0: aComputedOffsets.top = nsLayoutUtils:: michael@0: ComputeHeightDependentValue(aContainingBlockHeight, michael@0: position->mOffset.GetTop()); michael@0: michael@0: // Computed value for 'bottom' is minus the value of 'top' michael@0: aComputedOffsets.bottom = -aComputedOffsets.top; michael@0: } michael@0: michael@0: // Store the offset michael@0: FrameProperties props = aFrame->Properties(); michael@0: nsMargin* offsets = static_cast michael@0: (props.Get(nsIFrame::ComputedOffsetProperty())); michael@0: if (offsets) { michael@0: *offsets = aComputedOffsets; michael@0: } else { michael@0: props.Set(nsIFrame::ComputedOffsetProperty(), michael@0: new nsMargin(aComputedOffsets)); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, michael@0: const nsMargin& aComputedOffsets, michael@0: nsPoint* aPosition) michael@0: { michael@0: if (!aFrame->IsRelativelyPositioned()) { michael@0: NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()), michael@0: "We assume that changing the 'position' property causes " michael@0: "frame reconstruction. If that ever changes, this code " michael@0: "should call " michael@0: "props.Delete(nsIFrame::NormalPositionProperty())"); michael@0: return; michael@0: } michael@0: michael@0: // Store the normal position michael@0: FrameProperties props = aFrame->Properties(); michael@0: nsPoint* normalPosition = static_cast michael@0: (props.Get(nsIFrame::NormalPositionProperty())); michael@0: if (normalPosition) { michael@0: *normalPosition = *aPosition; michael@0: } else { michael@0: props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition)); michael@0: } michael@0: michael@0: const nsStyleDisplay* display = aFrame->StyleDisplay(); michael@0: if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { michael@0: *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); michael@0: } else if (NS_STYLE_POSITION_STICKY == display->mPosition && michael@0: !aFrame->GetNextContinuation() && michael@0: !aFrame->GetPrevContinuation() && michael@0: !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) { michael@0: // Sticky positioning for elements with multiple frames needs to be michael@0: // computed all at once. We can't safely do that here because we might be michael@0: // partway through (re)positioning the frames, so leave it until the scroll michael@0: // container reflows and calls StickyScrollContainer::UpdatePositions. michael@0: // For single-frame sticky positioned elements, though, go ahead and apply michael@0: // it now to avoid unnecessary overflow updates later. michael@0: StickyScrollContainer* ssc = michael@0: StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); michael@0: if (ssc) { michael@0: *aPosition = ssc->ComputePosition(aFrame); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, michael@0: nscoord& aCBLeftEdge, michael@0: nscoord& aCBWidth) michael@0: { michael@0: aFrame = aFrame->GetContainingBlock(); michael@0: NS_ASSERTION(aFrame != frame, "How did that happen?"); michael@0: michael@0: /* Now aFrame is the containing block we want */ michael@0: michael@0: /* Check whether the containing block is currently being reflowed. michael@0: If so, use the info from the reflow state. */ michael@0: const nsHTMLReflowState* state; michael@0: if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { michael@0: for (state = parentReflowState; state && state->frame != aFrame; michael@0: state = state->parentReflowState) { michael@0: /* do nothing */ michael@0: } michael@0: } else { michael@0: state = nullptr; michael@0: } michael@0: michael@0: if (state) { michael@0: aCBLeftEdge = state->ComputedPhysicalBorderPadding().left; michael@0: aCBWidth = state->ComputedWidth(); michael@0: } else { michael@0: /* Didn't find a reflow state for aFrame. Just compute the information we michael@0: want, on the assumption that aFrame already knows its size. This really michael@0: ought to be true by now. */ michael@0: NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), michael@0: "aFrame shouldn't be in reflow; we'll lie if it is"); michael@0: nsMargin borderPadding = aFrame->GetUsedBorderAndPadding(); michael@0: aCBLeftEdge = borderPadding.left; michael@0: aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight(); michael@0: } michael@0: michael@0: return aFrame; michael@0: } michael@0: michael@0: // When determining the hypothetical box that would have been if the element michael@0: // had been in the flow we may not be able to exactly determine both the left michael@0: // and right edges. For example, if the element is a non-replaced inline-level michael@0: // element we would have to reflow it in order to determine it desired width. michael@0: // In that case depending on the progression direction either the left or michael@0: // right edge would be marked as not being exact michael@0: struct nsHypotheticalBox { michael@0: // offsets from left edge of containing block (which is a padding edge) michael@0: nscoord mLeft, mRight; michael@0: // offset from top edge of containing block (which is a padding edge) michael@0: nscoord mTop; michael@0: #ifdef DEBUG michael@0: bool mLeftIsExact, mRightIsExact; michael@0: #endif michael@0: michael@0: nsHypotheticalBox() { michael@0: #ifdef DEBUG michael@0: mLeftIsExact = mRightIsExact = false; michael@0: #endif michael@0: } michael@0: }; michael@0: michael@0: static bool michael@0: GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType) michael@0: { michael@0: // See if it is an image frame michael@0: bool success = false; michael@0: michael@0: // Currently the only type of replaced frame that we can get the intrinsic michael@0: // size for is an image frame michael@0: // XXX We should add back the GetReflowMetrics() function and one of the michael@0: // things should be the intrinsic size... michael@0: if (aFrameType == nsGkAtoms::imageFrame) { michael@0: nsImageFrame* imageFrame = (nsImageFrame*)aFrame; michael@0: michael@0: if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) { michael@0: success = (aIntrinsicSize != nsSize(0, 0)); michael@0: } michael@0: } michael@0: return success; michael@0: } michael@0: michael@0: /** michael@0: * aInsideBoxSizing returns the part of the horizontal padding, border, michael@0: * and margin that goes inside the edge given by box-sizing; michael@0: * aOutsideBoxSizing returns the rest. michael@0: */ michael@0: void michael@0: nsHTMLReflowState::CalculateHorizBorderPaddingMargin( michael@0: nscoord aContainingBlockWidth, michael@0: nscoord* aInsideBoxSizing, michael@0: nscoord* aOutsideBoxSizing) michael@0: { michael@0: const nsMargin& border = mStyleBorder->GetComputedBorder(); michael@0: nsMargin padding, margin; michael@0: michael@0: // See if the style system can provide us the padding directly michael@0: if (!mStylePadding->GetPadding(padding)) { michael@0: // We have to compute the left and right values michael@0: padding.left = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: mStylePadding->mPadding.GetLeft()); michael@0: padding.right = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: mStylePadding->mPadding.GetRight()); michael@0: } michael@0: michael@0: // See if the style system can provide us the margin directly michael@0: if (!mStyleMargin->GetMargin(margin)) { michael@0: // We have to compute the left and right values michael@0: if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) { michael@0: // XXX FIXME (or does CalculateBlockSideMargins do this?) michael@0: margin.left = 0; // just ignore michael@0: } else { michael@0: margin.left = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: mStyleMargin->mMargin.GetLeft()); michael@0: } michael@0: if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) { michael@0: // XXX FIXME (or does CalculateBlockSideMargins do this?) michael@0: margin.right = 0; // just ignore michael@0: } else { michael@0: margin.right = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aContainingBlockWidth, michael@0: mStyleMargin->mMargin.GetRight()); michael@0: } michael@0: } michael@0: michael@0: nscoord outside = michael@0: padding.LeftRight() + border.LeftRight() + margin.LeftRight(); michael@0: nscoord inside = 0; michael@0: switch (mStylePosition->mBoxSizing) { michael@0: case NS_STYLE_BOX_SIZING_BORDER: michael@0: inside += border.LeftRight(); michael@0: // fall through michael@0: case NS_STYLE_BOX_SIZING_PADDING: michael@0: inside += padding.LeftRight(); michael@0: } michael@0: outside -= inside; michael@0: *aInsideBoxSizing = inside; michael@0: *aOutsideBoxSizing = outside; michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * Returns true iff a pre-order traversal of the normal child michael@0: * frames rooted at aFrame finds no non-empty frame before aDescendant. michael@0: */ michael@0: static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, michael@0: nsIFrame* aDescendant, bool* aFound) { michael@0: if (aFrame == aDescendant) { michael@0: *aFound = true; michael@0: return true; michael@0: } michael@0: if (!aFrame->IsSelfEmpty()) { michael@0: *aFound = false; michael@0: return false; michael@0: } michael@0: for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) { michael@0: bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); michael@0: if (*aFound || !allEmpty) { michael@0: return allEmpty; michael@0: } michael@0: } michael@0: *aFound = false; michael@0: return true; michael@0: } michael@0: michael@0: // Calculate the hypothetical box that the element would have if it were in michael@0: // the flow. The values returned are relative to the padding edge of the michael@0: // absolute containing block michael@0: // aContainingBlock is the placeholder's containing block (XXX rename it?) michael@0: // cbrs->frame is the actual containing block michael@0: void michael@0: nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, michael@0: nsIFrame* aPlaceholderFrame, michael@0: nsIFrame* aContainingBlock, michael@0: nscoord aBlockLeftContentEdge, michael@0: nscoord aBlockContentWidth, michael@0: const nsHTMLReflowState* cbrs, michael@0: nsHypotheticalBox& aHypotheticalBox, michael@0: nsIAtom* aFrameType) michael@0: { michael@0: NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, michael@0: "mOriginalDisplay has not been properly initialized"); michael@0: michael@0: // If it's a replaced element and it has a 'auto' value for 'width', see if we michael@0: // can get the intrinsic size. This will allow us to exactly determine both the michael@0: // left and right edges michael@0: bool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto; michael@0: nsSize intrinsicSize; michael@0: bool knowIntrinsicSize = false; michael@0: if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { michael@0: // See if we can get the intrinsic size of the element michael@0: knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType); michael@0: } michael@0: michael@0: // See if we can calculate what the box width would have been if the michael@0: // element had been in the flow michael@0: nscoord boxWidth; michael@0: bool knowBoxWidth = false; michael@0: if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) && michael@0: !NS_FRAME_IS_REPLACED(mFrameType)) { michael@0: // For non-replaced inline-level elements the 'width' property doesn't apply, michael@0: // so we don't know what the width would have been without reflowing it michael@0: michael@0: } else { michael@0: // It's either a replaced inline-level element or a block-level element michael@0: michael@0: // Determine the total amount of horizontal border/padding/margin that michael@0: // the element would have had if it had been in the flow. Note that we michael@0: // ignore any 'auto' and 'inherit' values michael@0: nscoord insideBoxSizing, outsideBoxSizing; michael@0: CalculateHorizBorderPaddingMargin(aBlockContentWidth, michael@0: &insideBoxSizing, &outsideBoxSizing); michael@0: michael@0: if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { michael@0: // It's a replaced element with an 'auto' width so the box width is michael@0: // its intrinsic size plus any border/padding/margin michael@0: if (knowIntrinsicSize) { michael@0: boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing; michael@0: knowBoxWidth = true; michael@0: } michael@0: michael@0: } else if (isAutoWidth) { michael@0: // The box width is the containing block width michael@0: boxWidth = aBlockContentWidth; michael@0: knowBoxWidth = true; michael@0: michael@0: } else { michael@0: // We need to compute it. It's important we do this, because if it's michael@0: // percentage based this computed value may be different from the computed michael@0: // value calculated using the absolute containing block width michael@0: boxWidth = ComputeWidthValue(aBlockContentWidth, michael@0: insideBoxSizing, outsideBoxSizing, michael@0: mStylePosition->mWidth) + michael@0: insideBoxSizing + outsideBoxSizing; michael@0: knowBoxWidth = true; michael@0: } michael@0: } michael@0: michael@0: // Get the 'direction' of the block michael@0: const nsStyleVisibility* blockVis = aContainingBlock->StyleVisibility(); michael@0: michael@0: // Get the placeholder x-offset and y-offset in the coordinate michael@0: // space of its containing block michael@0: // XXXbz the placeholder is not fully reflowed yet if our containing block is michael@0: // relatively positioned... michael@0: nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock); michael@0: michael@0: // First, determine the hypothetical box's mTop. We want to check the michael@0: // content insertion frame of aContainingBlock for block-ness, but make michael@0: // sure to compute all coordinates in the coordinate system of michael@0: // aContainingBlock. michael@0: nsBlockFrame* blockFrame = michael@0: nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame()); michael@0: if (blockFrame) { michael@0: nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y; michael@0: bool isValid; michael@0: nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); michael@0: if (!isValid) { michael@0: // Give up. We're probably dealing with somebody using michael@0: // position:absolute inside native-anonymous content anyway. michael@0: aHypotheticalBox.mTop = placeholderOffset.y; michael@0: } else { michael@0: NS_ASSERTION(iter.GetContainer() == blockFrame, michael@0: "Found placeholder in wrong block!"); michael@0: nsBlockFrame::line_iterator lineBox = iter.GetLine(); michael@0: michael@0: // How we determine the hypothetical box depends on whether the element michael@0: // would have been inline-level or block-level michael@0: if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { michael@0: // Use the top of the inline box which the placeholder lives in michael@0: // as the hypothetical box's top. michael@0: aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; michael@0: } else { michael@0: // The element would have been block-level which means it would michael@0: // be below the line containing the placeholder frame, unless michael@0: // all the frames before it are empty. In that case, it would michael@0: // have been just before this line. michael@0: // XXXbz the line box is not fully reflowed yet if our michael@0: // containing block is relatively positioned... michael@0: if (lineBox != iter.End()) { michael@0: nsIFrame * firstFrame = lineBox->mFirstChild; michael@0: bool found = false; michael@0: bool allEmpty = true; michael@0: while (firstFrame) { // See bug 223064 michael@0: allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, michael@0: aPlaceholderFrame, &found); michael@0: if (found || !allEmpty) michael@0: break; michael@0: firstFrame = firstFrame->GetNextSibling(); michael@0: } michael@0: NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); michael@0: michael@0: if (allEmpty) { michael@0: // The top of the hypothetical box is the top of the line michael@0: // containing the placeholder, since there is nothing in the michael@0: // line before our placeholder except empty frames. michael@0: aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; michael@0: } else { michael@0: // The top of the hypothetical box is just below the line michael@0: // containing the placeholder. michael@0: aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().YMost() + blockYOffset; michael@0: } michael@0: } else { michael@0: // Just use the placeholder's y-offset wrt the containing block michael@0: aHypotheticalBox.mTop = placeholderOffset.y; michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: // The containing block is not a block, so it's probably something michael@0: // like a XUL box, etc. michael@0: // Just use the placeholder's y-offset michael@0: aHypotheticalBox.mTop = placeholderOffset.y; michael@0: } michael@0: michael@0: // Second, determine the hypothetical box's mLeft & mRight michael@0: // To determine the left and right offsets we need to look at the block's 'direction' michael@0: if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) { michael@0: // How we determine the hypothetical box depends on whether the element michael@0: // would have been inline-level or block-level michael@0: if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { michael@0: // The placeholder represents the left edge of the hypothetical box michael@0: aHypotheticalBox.mLeft = placeholderOffset.x; michael@0: } else { michael@0: aHypotheticalBox.mLeft = aBlockLeftContentEdge; michael@0: } michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mLeftIsExact = true; michael@0: #endif michael@0: michael@0: if (knowBoxWidth) { michael@0: aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth; michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mRightIsExact = true; michael@0: #endif michael@0: } else { michael@0: // We can't compute the right edge because we don't know the desired michael@0: // width. So instead use the right content edge of the block parent, michael@0: // but remember it's not exact michael@0: aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mRightIsExact = false; michael@0: #endif michael@0: } michael@0: michael@0: } else { michael@0: // The placeholder represents the right edge of the hypothetical box michael@0: if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { michael@0: aHypotheticalBox.mRight = placeholderOffset.x; michael@0: } else { michael@0: aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; michael@0: } michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mRightIsExact = true; michael@0: #endif michael@0: michael@0: if (knowBoxWidth) { michael@0: aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth; michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mLeftIsExact = true; michael@0: #endif michael@0: } else { michael@0: // We can't compute the left edge because we don't know the desired michael@0: // width. So instead use the left content edge of the block parent, michael@0: // but remember it's not exact michael@0: aHypotheticalBox.mLeft = aBlockLeftContentEdge; michael@0: #ifdef DEBUG michael@0: aHypotheticalBox.mLeftIsExact = false; michael@0: #endif michael@0: } michael@0: michael@0: } michael@0: michael@0: // The current coordinate space is that of the nearest block to the placeholder. michael@0: // Convert to the coordinate space of the absolute containing block michael@0: // One weird thing here is that for fixed-positioned elements we want to do michael@0: // the conversion incorrectly; specifically we want to ignore any scrolling michael@0: // that may have happened; michael@0: nsPoint cbOffset; michael@0: if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED && michael@0: // Exclude cases inside -moz-transform where fixed is like absolute. michael@0: nsLayoutUtils::IsReallyFixedPos(frame)) { michael@0: // In this case, cbrs->frame will always be an ancestor of michael@0: // aContainingBlock, so can just walk our way up the frame tree. michael@0: // Make sure to not add positions of frames whose parent is a michael@0: // scrollFrame, since we're doing fixed positioning, which assumes michael@0: // everything is scrolled to (0,0). michael@0: cbOffset.MoveTo(0, 0); michael@0: do { michael@0: NS_ASSERTION(aContainingBlock, michael@0: "Should hit cbrs->frame before we run off the frame tree!"); michael@0: cbOffset += aContainingBlock->GetPositionIgnoringScrolling(); michael@0: aContainingBlock = aContainingBlock->GetParent(); michael@0: } while (aContainingBlock != cbrs->frame); michael@0: } else { michael@0: // XXXldb We need to either ignore scrolling for the absolute michael@0: // positioning case too (and take the incompatibility) or figure out michael@0: // how to make these positioned elements actually *move* when we michael@0: // scroll, and thus avoid the resulting incremental reflow bugs. michael@0: cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame); michael@0: } michael@0: aHypotheticalBox.mLeft += cbOffset.x; michael@0: aHypotheticalBox.mTop += cbOffset.y; michael@0: aHypotheticalBox.mRight += cbOffset.x; michael@0: michael@0: // The specified offsets are relative to the absolute containing block's michael@0: // padding edge and our current values are relative to the border edge, so michael@0: // translate. michael@0: nsMargin border = cbrs->ComputedPhysicalBorderPadding() - cbrs->ComputedPhysicalPadding(); michael@0: aHypotheticalBox.mLeft -= border.left; michael@0: aHypotheticalBox.mRight -= border.left; michael@0: aHypotheticalBox.mTop -= border.top; michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* cbrs, michael@0: nscoord containingBlockWidth, michael@0: nscoord containingBlockHeight, michael@0: nsIAtom* aFrameType) michael@0: { michael@0: NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT, michael@0: "containing block height must be constrained"); michael@0: michael@0: NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame, michael@0: "InitAbsoluteConstraints should not be called on table frames"); michael@0: NS_ASSERTION(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW, michael@0: "Why are we here?"); michael@0: michael@0: // Get the placeholder frame michael@0: nsIFrame* placeholderFrame; michael@0: michael@0: placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(frame); michael@0: NS_ASSERTION(nullptr != placeholderFrame, "no placeholder frame"); michael@0: michael@0: // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are michael@0: // 'auto', then compute the hypothetical box of where the element would michael@0: // have been if it had been in the flow michael@0: nsHypotheticalBox hypotheticalBox; michael@0: if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) && michael@0: (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) || michael@0: ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) && michael@0: (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) { michael@0: // Find the nearest containing block frame to the placeholder frame, michael@0: // and return its left edge and width. michael@0: nscoord cbLeftEdge, cbWidth; michael@0: nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame, michael@0: cbLeftEdge, michael@0: cbWidth); michael@0: michael@0: CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame, michael@0: cbLeftEdge, cbWidth, cbrs, hypotheticalBox, aFrameType); michael@0: } michael@0: michael@0: // Initialize the 'left' and 'right' computed offsets michael@0: // XXX Handle new 'static-position' value... michael@0: bool leftIsAuto = false, rightIsAuto = false; michael@0: if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) { michael@0: ComputedPhysicalOffsets().left = 0; michael@0: leftIsAuto = true; michael@0: } else { michael@0: ComputedPhysicalOffsets().left = nsLayoutUtils:: michael@0: ComputeCBDependentValue(containingBlockWidth, michael@0: mStylePosition->mOffset.GetLeft()); michael@0: } michael@0: if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) { michael@0: ComputedPhysicalOffsets().right = 0; michael@0: rightIsAuto = true; michael@0: } else { michael@0: ComputedPhysicalOffsets().right = nsLayoutUtils:: michael@0: ComputeCBDependentValue(containingBlockWidth, michael@0: mStylePosition->mOffset.GetRight()); michael@0: } michael@0: michael@0: // Use the horizontal component of the hypothetical box in the cases michael@0: // where it's needed. michael@0: if (leftIsAuto && rightIsAuto) { michael@0: // Use the direction of the original ("static-position") containing block michael@0: // to dictate whether 'left' or 'right' is treated like 'static-position'. michael@0: if (NS_STYLE_DIRECTION_LTR == placeholderFrame->GetContainingBlock() michael@0: ->StyleVisibility()->mDirection) { michael@0: NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have " michael@0: "exact value on containing block's start side"); michael@0: ComputedPhysicalOffsets().left = hypotheticalBox.mLeft; michael@0: leftIsAuto = false; michael@0: } else { michael@0: NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have " michael@0: "exact value on containing block's start side"); michael@0: ComputedPhysicalOffsets().right = containingBlockWidth - hypotheticalBox.mRight; michael@0: rightIsAuto = false; michael@0: } michael@0: } michael@0: michael@0: // Initialize the 'top' and 'bottom' computed offsets michael@0: bool topIsAuto = false, bottomIsAuto = false; michael@0: if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) { michael@0: ComputedPhysicalOffsets().top = 0; michael@0: topIsAuto = true; michael@0: } else { michael@0: ComputedPhysicalOffsets().top = nsLayoutUtils:: michael@0: ComputeHeightDependentValue(containingBlockHeight, michael@0: mStylePosition->mOffset.GetTop()); michael@0: } michael@0: if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) { michael@0: ComputedPhysicalOffsets().bottom = 0; michael@0: bottomIsAuto = true; michael@0: } else { michael@0: ComputedPhysicalOffsets().bottom = nsLayoutUtils:: michael@0: ComputeHeightDependentValue(containingBlockHeight, michael@0: mStylePosition->mOffset.GetBottom()); michael@0: } michael@0: michael@0: if (topIsAuto && bottomIsAuto) { michael@0: // Treat 'top' like 'static-position' michael@0: ComputedPhysicalOffsets().top = hypotheticalBox.mTop; michael@0: topIsAuto = false; michael@0: } michael@0: michael@0: bool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(); michael@0: bool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit(); michael@0: michael@0: uint32_t computeSizeFlags = 0; michael@0: if (leftIsAuto || rightIsAuto) { michael@0: computeSizeFlags |= nsIFrame::eShrinkWrap; michael@0: } michael@0: michael@0: { michael@0: AutoMaybeDisableFontInflation an(frame); michael@0: michael@0: nsSize size = michael@0: frame->ComputeSize(rendContext, michael@0: nsSize(containingBlockWidth, michael@0: containingBlockHeight), michael@0: containingBlockWidth, // XXX or mAvailableWidth? michael@0: nsSize(ComputedPhysicalMargin().LeftRight() + michael@0: ComputedPhysicalOffsets().LeftRight(), michael@0: ComputedPhysicalMargin().TopBottom() + michael@0: ComputedPhysicalOffsets().TopBottom()), michael@0: nsSize(ComputedPhysicalBorderPadding().LeftRight() - michael@0: ComputedPhysicalPadding().LeftRight(), michael@0: ComputedPhysicalBorderPadding().TopBottom() - michael@0: ComputedPhysicalPadding().TopBottom()), michael@0: nsSize(ComputedPhysicalPadding().LeftRight(), michael@0: ComputedPhysicalPadding().TopBottom()), michael@0: computeSizeFlags); michael@0: ComputedWidth() = size.width; michael@0: ComputedHeight() = size.height; michael@0: } michael@0: NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); michael@0: NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || michael@0: ComputedHeight() >= 0, "Bogus height"); michael@0: michael@0: // XXX Now that we have ComputeSize, can we condense many of the michael@0: // branches off of widthIsAuto? michael@0: michael@0: if (leftIsAuto) { michael@0: // We know 'right' is not 'auto' anymore thanks to the hypothetical michael@0: // box code above. michael@0: // Solve for 'left'. michael@0: if (widthIsAuto) { michael@0: // XXXldb This, and the corresponding code in michael@0: // nsAbsoluteContainingBlock.cpp, could probably go away now that michael@0: // we always compute widths. michael@0: ComputedPhysicalOffsets().left = NS_AUTOOFFSET; michael@0: } else { michael@0: ComputedPhysicalOffsets().left = containingBlockWidth - ComputedPhysicalMargin().left - michael@0: ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - michael@0: ComputedPhysicalMargin().right - ComputedPhysicalOffsets().right; michael@0: michael@0: } michael@0: } else if (rightIsAuto) { michael@0: // We know 'left' is not 'auto' anymore thanks to the hypothetical michael@0: // box code above. michael@0: // Solve for 'right'. michael@0: if (widthIsAuto) { michael@0: // XXXldb This, and the corresponding code in michael@0: // nsAbsoluteContainingBlock.cpp, could probably go away now that michael@0: // we always compute widths. michael@0: ComputedPhysicalOffsets().right = NS_AUTOOFFSET; michael@0: } else { michael@0: ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left - michael@0: ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() - michael@0: ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right; michael@0: } michael@0: } else { michael@0: // Neither 'left' nor 'right' is 'auto'. However, the width might michael@0: // still not fill all the available space (even though we didn't michael@0: // shrink-wrap) in case: michael@0: // * width was specified michael@0: // * we're dealing with a replaced element michael@0: // * width was constrained by min-width or max-width. michael@0: michael@0: nscoord availMarginSpace = containingBlockWidth - michael@0: ComputedPhysicalOffsets().LeftRight() - michael@0: ComputedPhysicalMargin().LeftRight() - michael@0: ComputedPhysicalBorderPadding().LeftRight() - michael@0: ComputedWidth(); michael@0: bool marginLeftIsAuto = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); michael@0: bool marginRightIsAuto = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); michael@0: michael@0: if (marginLeftIsAuto) { michael@0: if (marginRightIsAuto) { michael@0: if (availMarginSpace < 0) { michael@0: // Note that this case is different from the neither-'auto' michael@0: // case below, where the spec says to ignore 'left'/'right'. michael@0: if (cbrs && michael@0: NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { michael@0: // Ignore the specified value for 'margin-left'. michael@0: ComputedPhysicalMargin().left = availMarginSpace; michael@0: } else { michael@0: // Ignore the specified value for 'margin-right'. michael@0: ComputedPhysicalMargin().right = availMarginSpace; michael@0: } michael@0: } else { michael@0: // Both 'margin-left' and 'margin-right' are 'auto', so they get michael@0: // equal values michael@0: ComputedPhysicalMargin().left = availMarginSpace / 2; michael@0: ComputedPhysicalMargin().right = availMarginSpace - ComputedPhysicalMargin().left; michael@0: } michael@0: } else { michael@0: // Just 'margin-left' is 'auto' michael@0: ComputedPhysicalMargin().left = availMarginSpace; michael@0: } michael@0: } else { michael@0: if (marginRightIsAuto) { michael@0: // Just 'margin-right' is 'auto' michael@0: ComputedPhysicalMargin().right = availMarginSpace; michael@0: } else { michael@0: // We're over-constrained so use the direction of the containing michael@0: // block to dictate which value to ignore. (And note that the michael@0: // spec says to ignore 'left' or 'right' rather than michael@0: // 'margin-left' or 'margin-right'.) michael@0: // Note that this case is different from the both-'auto' case michael@0: // above, where the spec says to ignore michael@0: // 'margin-left'/'margin-right'. michael@0: if (cbrs && michael@0: NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { michael@0: // Ignore the specified value for 'left'. michael@0: ComputedPhysicalOffsets().left += availMarginSpace; michael@0: } else { michael@0: // Ignore the specified value for 'right'. michael@0: ComputedPhysicalOffsets().right += availMarginSpace; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (topIsAuto) { michael@0: // solve for 'top' michael@0: if (heightIsAuto) { michael@0: ComputedPhysicalOffsets().top = NS_AUTOOFFSET; michael@0: } else { michael@0: ComputedPhysicalOffsets().top = containingBlockHeight - ComputedPhysicalMargin().top - michael@0: ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom - michael@0: ComputedPhysicalMargin().bottom - ComputedPhysicalOffsets().bottom; michael@0: } michael@0: } else if (bottomIsAuto) { michael@0: // solve for 'bottom' michael@0: if (heightIsAuto) { michael@0: ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET; michael@0: } else { michael@0: ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top - michael@0: ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() - michael@0: ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom; michael@0: } michael@0: } else { michael@0: // Neither 'top' nor 'bottom' is 'auto'. michael@0: nscoord autoHeight = containingBlockHeight - michael@0: ComputedPhysicalOffsets().TopBottom() - michael@0: ComputedPhysicalMargin().TopBottom() - michael@0: ComputedPhysicalBorderPadding().TopBottom(); michael@0: if (autoHeight < 0) { michael@0: autoHeight = 0; michael@0: } michael@0: michael@0: if (ComputedHeight() == NS_UNCONSTRAINEDSIZE) { michael@0: // For non-replaced elements with 'height' auto, the 'height' michael@0: // fills the remaining space. michael@0: ComputedHeight() = autoHeight; michael@0: michael@0: // XXX Do these need box-sizing adjustments? michael@0: if (ComputedHeight() > ComputedMaxHeight()) michael@0: ComputedHeight() = ComputedMaxHeight(); michael@0: if (ComputedHeight() < ComputedMinHeight()) michael@0: ComputedHeight() = ComputedMinHeight(); michael@0: } michael@0: michael@0: // The height might still not fill all the available space in case: michael@0: // * height was specified michael@0: // * we're dealing with a replaced element michael@0: // * height was constrained by min-height or max-height. michael@0: nscoord availMarginSpace = autoHeight - ComputedHeight(); michael@0: bool marginTopIsAuto = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit(); michael@0: bool marginBottomIsAuto = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit(); michael@0: michael@0: if (marginTopIsAuto) { michael@0: if (marginBottomIsAuto) { michael@0: if (availMarginSpace < 0) { michael@0: // FIXME: Note that the spec doesn't actually say we should do this! michael@0: ComputedPhysicalMargin().bottom = availMarginSpace; michael@0: } else { michael@0: // Both 'margin-top' and 'margin-bottom' are 'auto', so they get michael@0: // equal values michael@0: ComputedPhysicalMargin().top = availMarginSpace / 2; michael@0: ComputedPhysicalMargin().bottom = availMarginSpace - ComputedPhysicalMargin().top; michael@0: } michael@0: } else { michael@0: // Just 'margin-top' is 'auto' michael@0: ComputedPhysicalMargin().top = availMarginSpace; michael@0: } michael@0: } else { michael@0: if (marginBottomIsAuto) { michael@0: // Just 'margin-bottom' is 'auto' michael@0: ComputedPhysicalMargin().bottom = availMarginSpace; michael@0: } else { michael@0: // We're over-constrained so ignore the specified value for michael@0: // 'bottom'. (And note that the spec says to ignore 'bottom' michael@0: // rather than 'margin-bottom'.) michael@0: ComputedPhysicalOffsets().bottom += availMarginSpace; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nscoord michael@0: GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState) michael@0: { michael@0: nscoord result = 0; michael@0: if (!aReflowState) return result; michael@0: michael@0: // zero auto margins michael@0: nsMargin margin = aReflowState->ComputedPhysicalMargin(); michael@0: if (NS_AUTOMARGIN == margin.top) michael@0: margin.top = 0; michael@0: if (NS_AUTOMARGIN == margin.bottom) michael@0: margin.bottom = 0; michael@0: michael@0: result += margin.top + margin.bottom; michael@0: result += aReflowState->ComputedPhysicalBorderPadding().top + michael@0: aReflowState->ComputedPhysicalBorderPadding().bottom; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /* Get the height based on the viewport of the containing block specified michael@0: * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT michael@0: * This will walk up the chain of containing blocks looking for a computed height michael@0: * until it finds the canvas frame, or it encounters a frame that is not a block, michael@0: * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693) michael@0: * michael@0: * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block. michael@0: * michael@0: * See also IsQuirkContainingBlockHeight. michael@0: */ michael@0: static nscoord michael@0: CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) michael@0: { michael@0: const nsHTMLReflowState* firstAncestorRS = nullptr; // a candidate for html frame michael@0: const nsHTMLReflowState* secondAncestorRS = nullptr; // a candidate for body frame michael@0: michael@0: // initialize the default to NS_AUTOHEIGHT as this is the containings block michael@0: // computed height when this function is called. It is possible that we michael@0: // don't alter this height especially if we are restricted to one level michael@0: nscoord result = NS_AUTOHEIGHT; michael@0: michael@0: const nsHTMLReflowState* rs = aCBReflowState; michael@0: for (; rs; rs = rs->parentReflowState) { michael@0: nsIAtom* frameType = rs->frame->GetType(); michael@0: // if the ancestor is auto height then skip it and continue up if it michael@0: // is the first block frame and possibly the body/html michael@0: if (nsGkAtoms::blockFrame == frameType || michael@0: #ifdef MOZ_XUL michael@0: nsGkAtoms::XULLabelFrame == frameType || michael@0: #endif michael@0: nsGkAtoms::scrollFrame == frameType) { michael@0: michael@0: secondAncestorRS = firstAncestorRS; michael@0: firstAncestorRS = rs; michael@0: michael@0: // If the current frame we're looking at is positioned, we don't want to michael@0: // go any further (see bug 221784). The behavior we want here is: 1) If michael@0: // not auto-height, use this as the percentage base. 2) If auto-height, michael@0: // keep looking, unless the frame is positioned. michael@0: if (NS_AUTOHEIGHT == rs->ComputedHeight()) { michael@0: if (rs->frame->IsAbsolutelyPositioned()) { michael@0: break; michael@0: } else { michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: else if (nsGkAtoms::canvasFrame == frameType) { michael@0: // Always continue on to the height calculation michael@0: } michael@0: else if (nsGkAtoms::pageContentFrame == frameType) { michael@0: nsIFrame* prevInFlow = rs->frame->GetPrevInFlow(); michael@0: // only use the page content frame for a height basis if it is the first in flow michael@0: if (prevInFlow) michael@0: break; michael@0: } michael@0: else { michael@0: break; michael@0: } michael@0: michael@0: // if the ancestor is the page content frame then the percent base is michael@0: // the avail height, otherwise it is the computed height michael@0: result = (nsGkAtoms::pageContentFrame == frameType) michael@0: ? rs->AvailableHeight() : rs->ComputedHeight(); michael@0: // if unconstrained - don't sutract borders - would result in huge height michael@0: if (NS_AUTOHEIGHT == result) return result; michael@0: michael@0: // if we got to the canvas or page content frame, then subtract out michael@0: // margin/border/padding for the BODY and HTML elements michael@0: if ((nsGkAtoms::canvasFrame == frameType) || michael@0: (nsGkAtoms::pageContentFrame == frameType)) { michael@0: michael@0: result -= GetVerticalMarginBorderPadding(firstAncestorRS); michael@0: result -= GetVerticalMarginBorderPadding(secondAncestorRS); michael@0: michael@0: #ifdef DEBUG michael@0: // make sure the first ancestor is the HTML and the second is the BODY michael@0: if (firstAncestorRS) { michael@0: nsIContent* frameContent = firstAncestorRS->frame->GetContent(); michael@0: if (frameContent) { michael@0: nsIAtom *contentTag = frameContent->Tag(); michael@0: NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML"); michael@0: } michael@0: } michael@0: if (secondAncestorRS) { michael@0: nsIContent* frameContent = secondAncestorRS->frame->GetContent(); michael@0: if (frameContent) { michael@0: nsIAtom *contentTag = frameContent->Tag(); michael@0: NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: } michael@0: // if we got to the html frame (a block child of the canvas) ... michael@0: else if (nsGkAtoms::blockFrame == frameType && michael@0: rs->parentReflowState && michael@0: nsGkAtoms::canvasFrame == michael@0: rs->parentReflowState->frame->GetType()) { michael@0: // ... then subtract out margin/border/padding for the BODY element michael@0: result -= GetVerticalMarginBorderPadding(secondAncestorRS); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: // Make sure not to return a negative height here! michael@0: return std::max(result, 0); michael@0: } michael@0: michael@0: // Called by InitConstraints() to compute the containing block rectangle for michael@0: // the element. Handles the special logic for absolutely positioned elements michael@0: void michael@0: nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aContainingBlockRS, michael@0: nscoord& aContainingBlockWidth, michael@0: nscoord& aContainingBlockHeight) michael@0: { michael@0: // Unless the element is absolutely positioned, the containing block is michael@0: // formed by the content edge of the nearest block-level ancestor michael@0: aContainingBlockWidth = aContainingBlockRS->ComputedWidth(); michael@0: aContainingBlockHeight = aContainingBlockRS->ComputedHeight(); michael@0: michael@0: // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to michael@0: // special case them here. michael@0: if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE || michael@0: (frame->GetType() == nsGkAtoms::tableFrame && michael@0: frame->IsAbsolutelyPositioned() && michael@0: (frame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) { michael@0: // See if the ancestor is block-level or inline-level michael@0: if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) { michael@0: // Base our size on the actual size of the frame. In cases when this is michael@0: // completely bogus (eg initial reflow), this code shouldn't even be michael@0: // called, since the code in nsInlineFrame::Reflow will pass in michael@0: // the containing block dimensions to our constructor. michael@0: // XXXbz we should be taking the in-flows into account too, but michael@0: // that's very hard. michael@0: nsMargin computedBorder = aContainingBlockRS->ComputedPhysicalBorderPadding() - michael@0: aContainingBlockRS->ComputedPhysicalPadding(); michael@0: aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width - michael@0: computedBorder.LeftRight(); michael@0: NS_ASSERTION(aContainingBlockWidth >= 0, michael@0: "Negative containing block width!"); michael@0: aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height - michael@0: computedBorder.TopBottom(); michael@0: NS_ASSERTION(aContainingBlockHeight >= 0, michael@0: "Negative containing block height!"); michael@0: } else { michael@0: // If the ancestor is block-level, the containing block is formed by the michael@0: // padding edge of the ancestor michael@0: aContainingBlockWidth += aContainingBlockRS->ComputedPhysicalPadding().LeftRight(); michael@0: aContainingBlockHeight += aContainingBlockRS->ComputedPhysicalPadding().TopBottom(); michael@0: } michael@0: } else { michael@0: // an element in quirks mode gets a containing block based on looking for a michael@0: // parent with a non-auto height if the element has a percent height michael@0: // Note: We don't emulate this quirk for percents in calc(). michael@0: if (NS_AUTOHEIGHT == aContainingBlockHeight) { michael@0: if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && michael@0: mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) { michael@0: aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: static eNormalLineHeightControl GetNormalLineHeightCalcControl(void) michael@0: { michael@0: if (sNormalLineHeightControl == eUninitialized) { michael@0: // browser.display.normal_lineheight_calc_control is not user michael@0: // changeable, so no need to register callback for it. michael@0: int32_t val = michael@0: Preferences::GetInt("browser.display.normal_lineheight_calc_control", michael@0: eNoExternalLeading); michael@0: sNormalLineHeightControl = static_cast(val); michael@0: } michael@0: return sNormalLineHeightControl; michael@0: } michael@0: michael@0: static inline bool michael@0: IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay) michael@0: { michael@0: if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION) michael@0: return false; michael@0: uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide; michael@0: return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; michael@0: } michael@0: michael@0: static nsFlexContainerFrame* michael@0: GetFlexContainer(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* parent = aFrame->GetParent(); michael@0: if (!parent || michael@0: parent->GetType() != nsGkAtoms::flexContainerFrame) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return static_cast(parent); michael@0: } michael@0: michael@0: // Flex items resolve percentage margin & padding against the flex michael@0: // container's height (which is the containing block height). michael@0: // For everything else: the CSS21 spec requires that margin and padding michael@0: // percentage values are calculated with respect to the *width* of the michael@0: // containing block, even for margin & padding in the vertical axis. michael@0: static nscoord michael@0: VerticalOffsetPercentBasis(const nsIFrame* aFrame, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight) michael@0: { michael@0: if (!aFrame->IsFlexItem()) { michael@0: return aContainingBlockWidth; michael@0: } michael@0: michael@0: if (aContainingBlockHeight == NS_AUTOHEIGHT) { michael@0: return 0; michael@0: } michael@0: michael@0: return aContainingBlockHeight; michael@0: } michael@0: michael@0: // XXX refactor this code to have methods for each set of properties michael@0: // we are computing: width,height,line-height; margin; offsets michael@0: michael@0: void michael@0: nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding, michael@0: nsIAtom* aFrameType) michael@0: { michael@0: DISPLAY_INIT_CONSTRAINTS(frame, this, michael@0: aContainingBlockWidth, aContainingBlockHeight, michael@0: aBorder, aPadding); michael@0: michael@0: // If this is a reflow root, then set the computed width and michael@0: // height equal to the available space michael@0: if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) { michael@0: // XXXldb This doesn't mean what it used to! michael@0: InitOffsets(aContainingBlockWidth, michael@0: VerticalOffsetPercentBasis(frame, aContainingBlockWidth, michael@0: aContainingBlockHeight), michael@0: aFrameType, aBorder, aPadding); michael@0: // Override mComputedMargin since reflow roots start from the michael@0: // frame's boundary, which is inside the margin. michael@0: ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); michael@0: ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); michael@0: michael@0: ComputedWidth() = AvailableWidth() - ComputedPhysicalBorderPadding().LeftRight(); michael@0: if (ComputedWidth() < 0) michael@0: ComputedWidth() = 0; michael@0: if (AvailableHeight() != NS_UNCONSTRAINEDSIZE) { michael@0: ComputedHeight() = AvailableHeight() - ComputedPhysicalBorderPadding().TopBottom(); michael@0: if (ComputedHeight() < 0) michael@0: ComputedHeight() = 0; michael@0: } else { michael@0: ComputedHeight() = NS_UNCONSTRAINEDSIZE; michael@0: } michael@0: michael@0: ComputedMinWidth() = ComputedMinHeight() = 0; michael@0: ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; michael@0: } else { michael@0: // Get the containing block reflow state michael@0: const nsHTMLReflowState* cbrs = mCBReflowState; michael@0: NS_ASSERTION(nullptr != cbrs, "no containing block"); michael@0: michael@0: // If we weren't given a containing block width and height, then michael@0: // compute one michael@0: if (aContainingBlockWidth == -1) { michael@0: ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, michael@0: aContainingBlockHeight); michael@0: } michael@0: michael@0: // See if the containing block height is based on the size of its michael@0: // content michael@0: nsIAtom* fType; michael@0: if (NS_AUTOHEIGHT == aContainingBlockHeight) { michael@0: // See if the containing block is a cell frame which needs michael@0: // to use the mComputedHeight of the cell instead of what the cell block passed in. michael@0: // XXX It seems like this could lead to bugs with min-height and friends michael@0: if (cbrs->parentReflowState) { michael@0: fType = cbrs->frame->GetType(); michael@0: if (IS_TABLE_CELL(fType)) { michael@0: // use the cell's computed height michael@0: aContainingBlockHeight = cbrs->ComputedHeight(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // XXX Might need to also pass the CB height (not width) for page boxes, michael@0: // too, if we implement them. michael@0: InitOffsets(aContainingBlockWidth, michael@0: VerticalOffsetPercentBasis(frame, aContainingBlockWidth, michael@0: aContainingBlockHeight), michael@0: aFrameType, aBorder, aPadding); michael@0: michael@0: const nsStyleCoord &height = mStylePosition->mHeight; michael@0: nsStyleUnit heightUnit = height.GetUnit(); michael@0: michael@0: // Check for a percentage based height and a containing block height michael@0: // that depends on the content height michael@0: // XXX twiddling heightUnit doesn't help anymore michael@0: // FIXME Shouldn't we fix that? michael@0: if (height.HasPercent()) { michael@0: if (NS_AUTOHEIGHT == aContainingBlockHeight) { michael@0: // this if clause enables %-height on replaced inline frames, michael@0: // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;" michael@0: // used to be called exclusively. michael@0: if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType || michael@0: NS_FRAME_REPLACED_CONTAINS_BLOCK( michael@0: NS_CSS_FRAME_TYPE_INLINE) == mFrameType) { michael@0: // Get the containing block reflow state michael@0: NS_ASSERTION(nullptr != cbrs, "no containing block"); michael@0: // in quirks mode, get the cb height using the special quirk method michael@0: if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { michael@0: if (!IS_TABLE_CELL(fType)) { michael@0: aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs); michael@0: if (aContainingBlockHeight == NS_AUTOHEIGHT) { michael@0: heightUnit = eStyleUnit_Auto; michael@0: } michael@0: } michael@0: else { michael@0: heightUnit = eStyleUnit_Auto; michael@0: } michael@0: } michael@0: // in standard mode, use the cb height. if it's "auto", as will be the case michael@0: // by default in BODY, use auto height as per CSS2 spec. michael@0: else michael@0: { michael@0: if (NS_AUTOHEIGHT != cbrs->ComputedHeight()) michael@0: aContainingBlockHeight = cbrs->ComputedHeight(); michael@0: else michael@0: heightUnit = eStyleUnit_Auto; michael@0: } michael@0: } michael@0: else { michael@0: // default to interpreting the height like 'auto' michael@0: heightUnit = eStyleUnit_Auto; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Compute our offsets if the element is relatively positioned. We need michael@0: // the correct containing block width and height here, which is why we need michael@0: // to do it after all the quirks-n-such above. (If the element is sticky michael@0: // positioned, we need to wait until the scroll container knows its size, michael@0: // so we compute offsets from StickyScrollContainer::UpdatePositions.) michael@0: if (mStyleDisplay->IsRelativelyPositioned(frame) && michael@0: NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) { michael@0: uint8_t direction = NS_STYLE_DIRECTION_LTR; michael@0: if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { michael@0: direction = NS_STYLE_DIRECTION_RTL; michael@0: } michael@0: ComputeRelativeOffsets(direction, frame, aContainingBlockWidth, michael@0: aContainingBlockHeight, ComputedPhysicalOffsets()); michael@0: } else { michael@0: // Initialize offsets to 0 michael@0: ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); michael@0: } michael@0: michael@0: // Calculate the computed values for min and max properties. Note that michael@0: // this MUST come after we've computed our border and padding. michael@0: ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs); michael@0: michael@0: // Calculate the computed width and height. This varies by frame type michael@0: michael@0: if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { michael@0: // Internal table elements. The rules vary depending on the type. michael@0: // Calculate the computed width michael@0: bool rowOrRowGroup = false; michael@0: const nsStyleCoord &width = mStylePosition->mWidth; michael@0: nsStyleUnit widthUnit = width.GetUnit(); michael@0: if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) || michael@0: (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) { michael@0: // 'width' property doesn't apply to table rows and row groups michael@0: widthUnit = eStyleUnit_Auto; michael@0: rowOrRowGroup = true; michael@0: } michael@0: michael@0: // calc() with percentages acts like auto on internal table elements michael@0: if (eStyleUnit_Auto == widthUnit || michael@0: (width.IsCalcUnit() && width.CalcHasPercent())) { michael@0: ComputedWidth() = AvailableWidth(); michael@0: michael@0: if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ michael@0: // Internal table elements don't have margins. Only tables and michael@0: // cells have border and padding michael@0: ComputedWidth() -= ComputedPhysicalBorderPadding().left + michael@0: ComputedPhysicalBorderPadding().right; michael@0: if (ComputedWidth() < 0) michael@0: ComputedWidth() = 0; michael@0: } michael@0: NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width"); michael@0: michael@0: } else { michael@0: NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(), michael@0: "unexpected width unit change"); michael@0: ComputedWidth() = ComputeWidthValue(aContainingBlockWidth, michael@0: mStylePosition->mBoxSizing, michael@0: mStylePosition->mWidth); michael@0: } michael@0: michael@0: // Calculate the computed height michael@0: if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) || michael@0: (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) { michael@0: // 'height' property doesn't apply to table columns and column groups michael@0: heightUnit = eStyleUnit_Auto; michael@0: } michael@0: // calc() with percentages acts like 'auto' on internal table elements michael@0: if (eStyleUnit_Auto == heightUnit || michael@0: (height.IsCalcUnit() && height.CalcHasPercent())) { michael@0: ComputedHeight() = NS_AUTOHEIGHT; michael@0: } else { michael@0: NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(), michael@0: "unexpected height unit change"); michael@0: ComputedHeight() = ComputeHeightValue(aContainingBlockHeight, michael@0: mStylePosition->mBoxSizing, michael@0: mStylePosition->mHeight); michael@0: } michael@0: michael@0: // Doesn't apply to table elements michael@0: ComputedMinWidth() = ComputedMinHeight() = 0; michael@0: ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; michael@0: michael@0: } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) { michael@0: // XXX not sure if this belongs here or somewhere else - cwk michael@0: InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth, michael@0: aContainingBlockHeight, aFrameType); michael@0: } else { michael@0: AutoMaybeDisableFontInflation an(frame); michael@0: michael@0: bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType); michael@0: uint32_t computeSizeFlags = isBlock ? 0 : nsIFrame::eShrinkWrap; michael@0: michael@0: // Make sure legend frames with display:block and width:auto still michael@0: // shrink-wrap. michael@0: if (isBlock && michael@0: ((aFrameType == nsGkAtoms::legendFrame && michael@0: frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || michael@0: (aFrameType == nsGkAtoms::scrollFrame && michael@0: frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) { michael@0: computeSizeFlags |= nsIFrame::eShrinkWrap; michael@0: } michael@0: michael@0: const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame); michael@0: if (flexContainerFrame) { michael@0: computeSizeFlags |= nsIFrame::eShrinkWrap; michael@0: michael@0: // If we're inside of a flex container that needs to measure our michael@0: // auto height, pass that information along to ComputeSize(). michael@0: if (mFlags.mIsFlexContainerMeasuringHeight) { michael@0: computeSizeFlags |= nsIFrame::eUseAutoHeight; michael@0: } michael@0: } else { michael@0: MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, michael@0: "We're not in a flex container, so the flag " michael@0: "'mIsFlexContainerMeasuringHeight' shouldn't be set"); michael@0: } michael@0: michael@0: nsSize size = michael@0: frame->ComputeSize(rendContext, michael@0: nsSize(aContainingBlockWidth, michael@0: aContainingBlockHeight), michael@0: AvailableWidth(), michael@0: nsSize(ComputedPhysicalMargin().LeftRight(), michael@0: ComputedPhysicalMargin().TopBottom()), michael@0: nsSize(ComputedPhysicalBorderPadding().LeftRight() - michael@0: ComputedPhysicalPadding().LeftRight(), michael@0: ComputedPhysicalBorderPadding().TopBottom() - michael@0: ComputedPhysicalPadding().TopBottom()), michael@0: nsSize(ComputedPhysicalPadding().LeftRight(), michael@0: ComputedPhysicalPadding().TopBottom()), michael@0: computeSizeFlags); michael@0: michael@0: ComputedWidth() = size.width; michael@0: ComputedHeight() = size.height; michael@0: NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); michael@0: NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || michael@0: ComputedHeight() >= 0, "Bogus height"); michael@0: michael@0: // Exclude inline tables and flex items from the block margin calculations michael@0: if (isBlock && michael@0: !IsSideCaption(frame, mStyleDisplay) && michael@0: mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE && michael@0: !flexContainerFrame) { michael@0: CalculateBlockSideMargins(AvailableWidth(), ComputedWidth(), aFrameType); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: UpdateProp(FrameProperties& aProps, michael@0: const FramePropertyDescriptor* aProperty, michael@0: bool aNeeded, michael@0: nsMargin& aNewValue) michael@0: { michael@0: if (aNeeded) { michael@0: nsMargin* propValue = static_cast(aProps.Get(aProperty)); michael@0: if (propValue) { michael@0: *propValue = aNewValue; michael@0: } else { michael@0: aProps.Set(aProperty, new nsMargin(aNewValue)); michael@0: } michael@0: } else { michael@0: aProps.Delete(aProperty); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSOffsetState::InitOffsets(nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis, michael@0: nsIAtom* aFrameType, michael@0: const nsMargin *aBorder, michael@0: const nsMargin *aPadding) michael@0: { michael@0: DISPLAY_INIT_OFFSETS(frame, this, michael@0: aHorizontalPercentBasis, michael@0: aVerticalPercentBasis, michael@0: aBorder, aPadding); michael@0: michael@0: // Since we are in reflow, we don't need to store these properties anymore michael@0: // unless they are dependent on width, in which case we store the new value. michael@0: nsPresContext *presContext = frame->PresContext(); michael@0: FrameProperties props(presContext->PropertyTable(), frame); michael@0: props.Delete(nsIFrame::UsedBorderProperty()); michael@0: michael@0: // Compute margins from the specified margin style information. These michael@0: // become the default computed values, and may be adjusted below michael@0: // XXX fix to provide 0,0 for the top&bottom margins for michael@0: // inline-non-replaced elements michael@0: bool needMarginProp = ComputeMargin(aHorizontalPercentBasis, michael@0: aVerticalPercentBasis); michael@0: // XXX We need to include 'auto' horizontal margins in this too! michael@0: // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin michael@0: // to use it even when the margins are all zero (since sometimes michael@0: // they get treated as auto) michael@0: ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp, michael@0: ComputedPhysicalMargin()); michael@0: michael@0: michael@0: const nsStyleDisplay *disp = frame->StyleDisplay(); michael@0: bool isThemed = frame->IsThemed(disp); michael@0: bool needPaddingProp; michael@0: nsIntMargin widget; michael@0: if (isThemed && michael@0: presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), michael@0: frame, disp->mAppearance, michael@0: &widget)) { michael@0: ComputedPhysicalPadding().top = presContext->DevPixelsToAppUnits(widget.top); michael@0: ComputedPhysicalPadding().right = presContext->DevPixelsToAppUnits(widget.right); michael@0: ComputedPhysicalPadding().bottom = presContext->DevPixelsToAppUnits(widget.bottom); michael@0: ComputedPhysicalPadding().left = presContext->DevPixelsToAppUnits(widget.left); michael@0: needPaddingProp = false; michael@0: } michael@0: else if (frame->IsSVGText()) { michael@0: ComputedPhysicalPadding().SizeTo(0, 0, 0, 0); michael@0: needPaddingProp = false; michael@0: } michael@0: else if (aPadding) { // padding is an input arg michael@0: ComputedPhysicalPadding() = *aPadding; michael@0: needPaddingProp = frame->StylePadding()->IsWidthDependent() || michael@0: (frame->GetStateBits() & NS_FRAME_REFLOW_ROOT); michael@0: } michael@0: else { michael@0: needPaddingProp = ComputePadding(aHorizontalPercentBasis, michael@0: aVerticalPercentBasis, aFrameType); michael@0: } michael@0: michael@0: if (isThemed) { michael@0: nsIntMargin widget; michael@0: presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), michael@0: frame, disp->mAppearance, michael@0: &widget); michael@0: ComputedPhysicalBorderPadding().top = michael@0: presContext->DevPixelsToAppUnits(widget.top); michael@0: ComputedPhysicalBorderPadding().right = michael@0: presContext->DevPixelsToAppUnits(widget.right); michael@0: ComputedPhysicalBorderPadding().bottom = michael@0: presContext->DevPixelsToAppUnits(widget.bottom); michael@0: ComputedPhysicalBorderPadding().left = michael@0: presContext->DevPixelsToAppUnits(widget.left); michael@0: } michael@0: else if (frame->IsSVGText()) { michael@0: ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0); michael@0: } michael@0: else if (aBorder) { // border is an input arg michael@0: ComputedPhysicalBorderPadding() = *aBorder; michael@0: } michael@0: else { michael@0: ComputedPhysicalBorderPadding() = frame->StyleBorder()->GetComputedBorder(); michael@0: } michael@0: ComputedPhysicalBorderPadding() += ComputedPhysicalPadding(); michael@0: michael@0: if (aFrameType == nsGkAtoms::tableFrame) { michael@0: nsTableFrame *tableFrame = static_cast(frame); michael@0: michael@0: if (tableFrame->IsBorderCollapse()) { michael@0: // border-collapsed tables don't use any of their padding, and michael@0: // only part of their border. We need to do this here before we michael@0: // try to do anything like handling 'auto' widths, michael@0: // 'box-sizing', or 'auto' margins. michael@0: ComputedPhysicalPadding().SizeTo(0,0,0,0); michael@0: ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder(); michael@0: } michael@0: michael@0: // The margin is inherited to the outer table frame via michael@0: // the ::-moz-table-outer rule in ua.css. michael@0: ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); michael@0: } else if (aFrameType == nsGkAtoms::scrollbarFrame) { michael@0: // scrollbars may have had their width or height smashed to zero michael@0: // by the associated scrollframe, in which case we must not report michael@0: // any padding or border. michael@0: nsSize size(frame->GetSize()); michael@0: if (size.width == 0 || size.height == 0) { michael@0: ComputedPhysicalPadding().SizeTo(0,0,0,0); michael@0: ComputedPhysicalBorderPadding().SizeTo(0,0,0,0); michael@0: } michael@0: } michael@0: ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp, michael@0: ComputedPhysicalPadding()); michael@0: } michael@0: michael@0: // This code enforces section 10.3.3 of the CSS2 spec for this formula: michael@0: // michael@0: // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + michael@0: // 'padding-right' + 'border-right-width' + 'margin-right' michael@0: // = width of containing block michael@0: // michael@0: // Note: the width unit is not auto when this is called michael@0: void michael@0: nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth, michael@0: nscoord aComputedWidth, michael@0: nsIAtom* aFrameType) michael@0: { michael@0: NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aComputedWidth && michael@0: NS_UNCONSTRAINEDSIZE != aAvailWidth, michael@0: "have unconstrained width; this should only result from " michael@0: "very large sizes, not attempts at intrinsic width " michael@0: "calculation"); michael@0: michael@0: nscoord sum = ComputedPhysicalMargin().left + ComputedPhysicalBorderPadding().left + michael@0: aComputedWidth + ComputedPhysicalBorderPadding().right + ComputedPhysicalMargin().right; michael@0: if (sum == aAvailWidth) michael@0: // The sum is already correct michael@0: return; michael@0: michael@0: // Determine the left and right margin values. The width value michael@0: // remains constant while we do this. michael@0: michael@0: // Calculate how much space is available for margins michael@0: nscoord availMarginSpace = aAvailWidth - sum; michael@0: michael@0: // If the available margin space is negative, then don't follow the michael@0: // usual overconstraint rules. michael@0: if (availMarginSpace < 0) { michael@0: if (mCBReflowState && michael@0: mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) { michael@0: ComputedPhysicalMargin().left += availMarginSpace; michael@0: } else { michael@0: ComputedPhysicalMargin().right += availMarginSpace; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // The css2 spec clearly defines how block elements should behave michael@0: // in section 10.3.3. michael@0: bool isAutoLeftMargin = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); michael@0: bool isAutoRightMargin = michael@0: eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); michael@0: if (!isAutoLeftMargin && !isAutoRightMargin) { michael@0: // Neither margin is 'auto' so we're over constrained. Use the michael@0: // 'direction' property of the parent to tell which margin to michael@0: // ignore michael@0: // First check if there is an HTML alignment that we should honor michael@0: const nsHTMLReflowState* prs = parentReflowState; michael@0: if (aFrameType == nsGkAtoms::tableFrame) { michael@0: NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame, michael@0: "table not inside outer table"); michael@0: // Center the table within the outer table based on the alignment michael@0: // of the outer table's parent. michael@0: prs = prs->parentReflowState; michael@0: } michael@0: if (prs && michael@0: (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || michael@0: prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || michael@0: prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) { michael@0: isAutoLeftMargin = michael@0: prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT; michael@0: isAutoRightMargin = michael@0: prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT; michael@0: } michael@0: // Otherwise apply the CSS rules, and ignore one margin by forcing michael@0: // it to 'auto', depending on 'direction'. michael@0: else if (mCBReflowState && michael@0: NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) { michael@0: isAutoLeftMargin = true; michael@0: } michael@0: else { michael@0: isAutoRightMargin = true; michael@0: } michael@0: } michael@0: michael@0: // Logic which is common to blocks and tables michael@0: // The computed margins need not be zero because the 'auto' could come from michael@0: // overconstraint or from HTML alignment so values need to be accumulated michael@0: michael@0: if (isAutoLeftMargin) { michael@0: if (isAutoRightMargin) { michael@0: // Both margins are 'auto' so the computed addition should be equal michael@0: nscoord forLeft = availMarginSpace / 2; michael@0: ComputedPhysicalMargin().left += forLeft; michael@0: ComputedPhysicalMargin().right += availMarginSpace - forLeft; michael@0: } else { michael@0: ComputedPhysicalMargin().left += availMarginSpace; michael@0: } michael@0: } else if (isAutoRightMargin) { michael@0: ComputedPhysicalMargin().right += availMarginSpace; michael@0: } michael@0: } michael@0: michael@0: #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight michael@0: // For "normal" we use the font's normal line height (em height + leading). michael@0: // If both internal leading and external leading specified by font itself michael@0: // are zeros, we should compensate this by creating extra (external) leading michael@0: // in eCompensateLeading mode. This is necessary because without this michael@0: // compensation, normal line height might looks too tight. michael@0: michael@0: // For risk management, we use preference to control the behavior, and michael@0: // eNoExternalLeading is the old behavior. michael@0: static nscoord michael@0: GetNormalLineHeight(nsFontMetrics* aFontMetrics) michael@0: { michael@0: NS_PRECONDITION(nullptr != aFontMetrics, "no font metrics"); michael@0: michael@0: nscoord normalLineHeight; michael@0: michael@0: nscoord externalLeading = aFontMetrics->ExternalLeading(); michael@0: nscoord internalLeading = aFontMetrics->InternalLeading(); michael@0: nscoord emHeight = aFontMetrics->EmHeight(); michael@0: switch (GetNormalLineHeightCalcControl()) { michael@0: case eIncludeExternalLeading: michael@0: normalLineHeight = emHeight+ internalLeading + externalLeading; michael@0: break; michael@0: case eCompensateLeading: michael@0: if (!internalLeading && !externalLeading) michael@0: normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR); michael@0: else michael@0: normalLineHeight = emHeight+ internalLeading + externalLeading; michael@0: break; michael@0: default: michael@0: //case eNoExternalLeading: michael@0: normalLineHeight = emHeight + internalLeading; michael@0: } michael@0: return normalLineHeight; michael@0: } michael@0: michael@0: static inline nscoord michael@0: ComputeLineHeight(nsStyleContext* aStyleContext, michael@0: nscoord aBlockHeight, michael@0: float aFontSizeInflation) michael@0: { michael@0: const nsStyleCoord& lhCoord = aStyleContext->StyleText()->mLineHeight; michael@0: michael@0: if (lhCoord.GetUnit() == eStyleUnit_Coord) { michael@0: nscoord result = lhCoord.GetCoordValue(); michael@0: if (aFontSizeInflation != 1.0f) { michael@0: result = NSToCoordRound(result * aFontSizeInflation); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: if (lhCoord.GetUnit() == eStyleUnit_Factor) michael@0: // For factor units the computed value of the line-height property michael@0: // is found by multiplying the factor by the font's computed size michael@0: // (adjusted for min-size prefs and text zoom). michael@0: return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation * michael@0: aStyleContext->StyleFont()->mFont.size); michael@0: michael@0: NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal || michael@0: lhCoord.GetUnit() == eStyleUnit_Enumerated, michael@0: "bad line-height unit"); michael@0: michael@0: if (lhCoord.GetUnit() == eStyleUnit_Enumerated) { michael@0: NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, michael@0: "bad line-height value"); michael@0: if (aBlockHeight != NS_AUTOHEIGHT) { michael@0: return aBlockHeight; michael@0: } michael@0: } michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, michael@0: getter_AddRefs(fm), michael@0: aFontSizeInflation); michael@0: return GetNormalLineHeight(fm); michael@0: } michael@0: michael@0: nscoord michael@0: nsHTMLReflowState::CalcLineHeight() const michael@0: { michael@0: nscoord blockHeight = michael@0: nsLayoutUtils::IsNonWrapperBlock(frame) ? ComputedHeight() : michael@0: (mCBReflowState ? mCBReflowState->ComputedHeight() : NS_AUTOHEIGHT); michael@0: michael@0: return CalcLineHeight(frame->GetContent(), frame->StyleContext(), blockHeight, michael@0: nsLayoutUtils::FontSizeInflationFor(frame)); michael@0: } michael@0: michael@0: /* static */ nscoord michael@0: nsHTMLReflowState::CalcLineHeight(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nscoord aBlockHeight, michael@0: float aFontSizeInflation) michael@0: { michael@0: NS_PRECONDITION(aStyleContext, "Must have a style context"); michael@0: michael@0: nscoord lineHeight = michael@0: ComputeLineHeight(aStyleContext, aBlockHeight, aFontSizeInflation); michael@0: michael@0: NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); michael@0: michael@0: HTMLInputElement* input = HTMLInputElement::FromContentOrNull(aContent); michael@0: if (input && input->IsSingleLineTextControl()) { michael@0: // For Web-compatibility, single-line text input elements cannot michael@0: // have a line-height smaller than one. michael@0: nscoord lineHeightOne = michael@0: aFontSizeInflation * aStyleContext->StyleFont()->mFont.size; michael@0: if (lineHeight < lineHeightOne) { michael@0: lineHeight = lineHeightOne; michael@0: } michael@0: } michael@0: michael@0: return lineHeight; michael@0: } michael@0: michael@0: bool michael@0: nsCSSOffsetState::ComputeMargin(nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis) michael@0: { michael@0: // SVG text frames have no margin. michael@0: if (frame->IsSVGText()) { michael@0: return false; michael@0: } michael@0: michael@0: // If style style can provide us the margin directly, then use it. michael@0: const nsStyleMargin *styleMargin = frame->StyleMargin(); michael@0: bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin()); michael@0: if (isCBDependent) { michael@0: // We have to compute the value michael@0: ComputedPhysicalMargin().left = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aHorizontalPercentBasis, michael@0: styleMargin->mMargin.GetLeft()); michael@0: ComputedPhysicalMargin().right = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aHorizontalPercentBasis, michael@0: styleMargin->mMargin.GetRight()); michael@0: michael@0: ComputedPhysicalMargin().top = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aVerticalPercentBasis, michael@0: styleMargin->mMargin.GetTop()); michael@0: ComputedPhysicalMargin().bottom = nsLayoutUtils:: michael@0: ComputeCBDependentValue(aVerticalPercentBasis, michael@0: styleMargin->mMargin.GetBottom()); michael@0: } michael@0: michael@0: nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(frame); michael@0: michael@0: if (marginAdjustment > 0) { michael@0: const nsStyleVisibility* visibility = frame->StyleVisibility(); michael@0: if (visibility->mDirection == NS_STYLE_DIRECTION_RTL) { michael@0: ComputedPhysicalMargin().right = ComputedPhysicalMargin().right + marginAdjustment; michael@0: } else { michael@0: ComputedPhysicalMargin().left = ComputedPhysicalMargin().left + marginAdjustment; michael@0: } michael@0: } michael@0: michael@0: return isCBDependent; michael@0: } michael@0: michael@0: bool michael@0: nsCSSOffsetState::ComputePadding(nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis, michael@0: nsIAtom* aFrameType) michael@0: { michael@0: // If style can provide us the padding directly, then use it. michael@0: const nsStylePadding *stylePadding = frame->StylePadding(); michael@0: bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding()); michael@0: // a table row/col group, row/col doesn't have padding michael@0: // XXXldb Neither do border-collapse tables. michael@0: if (nsGkAtoms::tableRowGroupFrame == aFrameType || michael@0: nsGkAtoms::tableColGroupFrame == aFrameType || michael@0: nsGkAtoms::tableRowFrame == aFrameType || michael@0: nsGkAtoms::tableColFrame == aFrameType) { michael@0: ComputedPhysicalPadding().SizeTo(0,0,0,0); michael@0: } michael@0: else if (isCBDependent) { michael@0: // We have to compute the value michael@0: // clamp negative calc() results to 0 michael@0: ComputedPhysicalPadding().left = std::max(0, nsLayoutUtils:: michael@0: ComputeCBDependentValue(aHorizontalPercentBasis, michael@0: stylePadding->mPadding.GetLeft())); michael@0: ComputedPhysicalPadding().right = std::max(0, nsLayoutUtils:: michael@0: ComputeCBDependentValue(aHorizontalPercentBasis, michael@0: stylePadding->mPadding.GetRight())); michael@0: michael@0: ComputedPhysicalPadding().top = std::max(0, nsLayoutUtils:: michael@0: ComputeCBDependentValue(aVerticalPercentBasis, michael@0: stylePadding->mPadding.GetTop())); michael@0: ComputedPhysicalPadding().bottom = std::max(0, nsLayoutUtils:: michael@0: ComputeCBDependentValue(aVerticalPercentBasis, michael@0: stylePadding->mPadding.GetBottom())); michael@0: } michael@0: return isCBDependent; michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: const nsHTMLReflowState* aContainingBlockRS) michael@0: { michael@0: ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth, michael@0: mStylePosition->mBoxSizing, michael@0: mStylePosition->mMinWidth); michael@0: michael@0: if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) { michael@0: // Specified value of 'none' michael@0: ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE; // no limit michael@0: } else { michael@0: ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth, michael@0: mStylePosition->mBoxSizing, michael@0: mStylePosition->mMaxWidth); michael@0: } michael@0: michael@0: // If the computed value of 'min-width' is greater than the value of michael@0: // 'max-width', 'max-width' is set to the value of 'min-width' michael@0: if (ComputedMinWidth() > ComputedMaxWidth()) { michael@0: ComputedMaxWidth() = ComputedMinWidth(); michael@0: } michael@0: michael@0: // Check for percentage based values and a containing block height that michael@0: // depends on the content height. Treat them like 'auto' michael@0: // Likewise, check for calc() with percentages on internal table elements; michael@0: // that's treated as 'auto' too. michael@0: // Likewise, if we're a child of a flex container who's measuring our michael@0: // intrinsic height, then we want to disregard our min-height. michael@0: michael@0: const nsStyleCoord &minHeight = mStylePosition->mMinHeight; michael@0: if ((NS_AUTOHEIGHT == aContainingBlockHeight && michael@0: minHeight.HasPercent()) || michael@0: (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && michael@0: minHeight.IsCalcUnit() && minHeight.CalcHasPercent()) || michael@0: mFlags.mIsFlexContainerMeasuringHeight) { michael@0: ComputedMinHeight() = 0; michael@0: } else { michael@0: ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight, michael@0: mStylePosition->mBoxSizing, michael@0: minHeight); michael@0: } michael@0: const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight; michael@0: nsStyleUnit maxHeightUnit = maxHeight.GetUnit(); michael@0: if (eStyleUnit_None == maxHeightUnit) { michael@0: // Specified value of 'none' michael@0: ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; // no limit michael@0: } else { michael@0: // Check for percentage based values and a containing block height that michael@0: // depends on the content height. Treat them like 'none' michael@0: // Likewise, check for calc() with percentages on internal table elements; michael@0: // that's treated as 'auto' too. michael@0: // Likewise, if we're a child of a flex container who's measuring our michael@0: // intrinsic height, then we want to disregard our max-height. michael@0: if ((NS_AUTOHEIGHT == aContainingBlockHeight && michael@0: maxHeight.HasPercent()) || michael@0: (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && michael@0: maxHeight.IsCalcUnit() && maxHeight.CalcHasPercent()) || michael@0: mFlags.mIsFlexContainerMeasuringHeight) { michael@0: ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; michael@0: } else { michael@0: ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight, michael@0: mStylePosition->mBoxSizing, michael@0: maxHeight); michael@0: } michael@0: } michael@0: michael@0: // If the computed value of 'min-height' is greater than the value of michael@0: // 'max-height', 'max-height' is set to the value of 'min-height' michael@0: if (ComputedMinHeight() > ComputedMaxHeight()) { michael@0: ComputedMaxHeight() = ComputedMinHeight(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus* aStatus) const michael@0: { michael@0: if (AvailableHeight() != NS_UNCONSTRAINEDSIZE && michael@0: AvailableHeight() < aMetrics.Height() && michael@0: !mFlags.mIsTopOfPage) { michael@0: *aStatus |= NS_FRAME_TRUNCATED; michael@0: } else { michael@0: *aStatus &= ~NS_FRAME_TRUNCATED; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsHTMLReflowState::IsFloating() const michael@0: { michael@0: return mStyleDisplay->IsFloating(frame); michael@0: } michael@0: michael@0: uint8_t michael@0: nsHTMLReflowState::GetDisplay() const michael@0: { michael@0: return mStyleDisplay->GetDisplay(frame); michael@0: }