/* virtual */ LogicalSize nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { LogicalSize result = nsContainerFrame::ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags); // XXX The code below doesn't make sense if the caller's writing mode // is orthogonal to this frame's. Not sure yet what should happen then; // for now, just bail out. if (aWM.IsVertical() != GetWritingMode().IsVertical()) { return result; } // Fieldsets never shrink below their min width. // If we're a container for font size inflation, then shrink // wrapping inside of us should not apply font size inflation. AutoMaybeDisableFontInflation an(this); nscoord minISize = GetMinISize(aRenderingContext); if (minISize > result.ISize(aWM)) { result.ISize(aWM) = minISize; } return result; }
/* virtual */ LogicalSize nsMathMLSelectedFrame::ComputeSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { nsIFrame* childFrame = GetSelectedFrame(); if (childFrame) { // Delegate size computation to the child frame. // Try to account for border/padding/margin on this frame and the child, // though we don't really support them during reflow anyway... nscoord availableISize = aAvailableISize - aBorder.ISize(aWM) - aPadding.ISize(aWM) - aMargin.ISize(aWM); LogicalSize cbSize = aCBSize - aBorder - aPadding - aMargin; SizeComputationInput offsetState(childFrame, aRenderingContext, aWM, availableISize); LogicalSize size = childFrame->ComputeSize(aRenderingContext, aWM, cbSize, availableISize, offsetState.ComputedLogicalMargin().Size(aWM), offsetState.ComputedLogicalBorderPadding().Size(aWM) - offsetState.ComputedLogicalPadding().Size(aWM), offsetState.ComputedLogicalPadding().Size(aWM), aFlags); return size + offsetState.ComputedLogicalBorderPadding().Size(aWM); } return LogicalSize(aWM); }
// Compute the margin-box inline size of aChildFrame given the inputs. // If aMarginResult is non-null, fill it with the part of the // margin-isize that was contributed by the margin. static nscoord ChildShrinkWrapISize(nsRenderingContext *aRenderingContext, nsIFrame *aChildFrame, WritingMode aWM, LogicalSize aCBSize, nscoord aAvailableISize, nscoord *aMarginResult = nullptr) { AutoMaybeDisableFontInflation an(aChildFrame); // For the caption frame, child's WM may differ from the table's main WM. WritingMode childWM = aChildFrame->GetWritingMode(); SizeComputationInput offsets(aChildFrame, aRenderingContext, aWM, aCBSize.ISize(aWM)); LogicalSize marginSize = offsets.ComputedLogicalMargin().Size(childWM).ConvertTo(aWM, childWM); LogicalSize paddingSize = offsets.ComputedLogicalPadding().Size(childWM).ConvertTo(aWM, childWM); LogicalSize bpSize = offsets.ComputedLogicalBorderPadding().Size(childWM).ConvertTo(aWM, childWM); LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, marginSize, bpSize - paddingSize, paddingSize, nsIFrame::ComputeSizeFlags::eShrinkWrap); if (aMarginResult) { *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(aWM); } return size.ISize(aWM) + marginSize.ISize(aWM) + bpSize.ISize(aWM); }
nscoord nsTableWrapperFrame::ChildShrinkWrapISize(nsRenderingContext* aRenderingContext, nsIFrame* aChildFrame, WritingMode aWM, LogicalSize aCBSize, nscoord aAvailableISize, nscoord* aMarginResult) const { AutoMaybeDisableFontInflation an(aChildFrame); // For the caption frame, child's WM may differ from the table's main WM. WritingMode childWM = aChildFrame->GetWritingMode(); SizeComputationInput offsets(aChildFrame, aRenderingContext, aWM, aCBSize.ISize(aWM)); LogicalSize marginSize = offsets.ComputedLogicalMargin().Size(childWM).ConvertTo(aWM, childWM); LogicalSize paddingSize = offsets.ComputedLogicalPadding().Size(childWM).ConvertTo(aWM, childWM); LogicalSize bpSize = offsets.ComputedLogicalBorderPadding().Size(childWM).ConvertTo(aWM, childWM); // Shrink-wrap aChildFrame by default, except if we're a stretched grid item. auto flags = ComputeSizeFlags::eShrinkWrap; auto parent = GetParent(); nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr; bool isGridItem = (parentFrameType == nsGkAtoms::gridContainerFrame && !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); if (MOZ_UNLIKELY(isGridItem) && !StyleMargin()->HasInlineAxisAuto(aWM)) { auto inlineAxisAlignment = aWM.IsOrthogonalTo(parent->GetWritingMode()) ? StylePosition()->UsedAlignSelf(parent->StyleContext()) : StylePosition()->UsedJustifySelf(parent->StyleContext()); if (inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL || inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) { flags = nsIFrame::ComputeSizeFlags::eDefault; } } LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, marginSize, bpSize - paddingSize, paddingSize, flags); if (aMarginResult) { *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(aWM); } return size.ISize(aWM) + marginSize.ISize(aWM) + bpSize.ISize(aWM); }
LogicalSize nsTextControlFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { float inflation = nsLayoutUtils::FontSizeInflationFor(this); LogicalSize autoSize = CalcIntrinsicSize(aRenderingContext, aWM, inflation); // Note: nsContainerFrame::ComputeAutoSize only computes the inline-size (and // only for 'auto'), the block-size it returns is always NS_UNCONSTRAINEDSIZE. const nsStyleCoord& iSizeCoord = StylePosition()->ISize(aWM); if (iSizeCoord.GetUnit() == eStyleUnit_Auto) { if (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) { // CalcIntrinsicSize isn't aware of grid-item margin-box clamping, so we // fall back to nsContainerFrame's ComputeAutoSize to handle that. // XXX maybe a font-inflation issue here? (per the assertion below). autoSize.ISize(aWM) = nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags).ISize(aWM); } #ifdef DEBUG else { LogicalSize ancestorAutoSize = nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags); // Disabled when there's inflation; see comment in GetXULPrefSize. MOZ_ASSERT(inflation != 1.0f || ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM), "Incorrect size computed by ComputeAutoSize?"); } #endif } return autoSize; }
/* virtual */ LogicalSize nsBackdropFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, bool aShrinkWrap) { // Note that this frame is a child of the viewport frame. LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE); if (aShrinkWrap) { result.ISize(aWM) = 0; } else { result.ISize(aWM) = aAvailableISize - aMargin.ISize(aWM) - aBorder.ISize(aWM) - aPadding.ISize(aWM); } return result; }
/* virtual */ LogicalSize nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, bool aShrinkWrap) { LogicalSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aShrinkWrap); // If we're a container for font size inflation, then shrink // wrapping inside of us should not apply font size inflation. AutoMaybeDisableFontInflation an(this); // XXX todo: make this aware of vertical writing modes uint8_t captionSide = StyleTableBorder()->mCaptionSide; if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { result.ISize(aWM) = GetMinISize(aRenderingContext); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // The outer frame constrains our available width to the width of // the table. Grow if our min-width is bigger than that, but not // larger than the containing block width. (It would really be nice // to transmit that information another way, so we could grow up to // the table's available width, but that's harder.) nscoord min = GetMinISize(aRenderingContext); if (min > aCBSize.ISize(aWM)) { min = aCBSize.ISize(aWM); } if (min > result.ISize(aWM)) { result.ISize(aWM) = min; } } return result; }
void nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, nsIFrame* aChildFrame, const nsHTMLReflowState& aOuterRS, void* aChildRSSpace, nscoord aAvailISize) { // work around pixel rounding errors, round down to ensure we don't exceed the avail height in WritingMode wm = aChildFrame->GetWritingMode(); LogicalSize outerSize = aOuterRS.AvailableSize(wm); nscoord availBSize = outerSize.BSize(wm); if (NS_UNCONSTRAINEDSIZE != availBSize) { if (mCaptionFrames.FirstChild() == aChildFrame) { availBSize = NS_UNCONSTRAINEDSIZE; } else { LogicalMargin margin(wm); GetChildMargin(aPresContext, aOuterRS, aChildFrame, outerSize.ISize(wm), margin); NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BStart(wm), "No unconstrainedsize arithmetic, please"); availBSize -= margin.BStart(wm); NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BEnd(wm), "No unconstrainedsize arithmetic, please"); availBSize -= margin.BEnd(wm); } } LogicalSize availSize(wm, aAvailISize, availBSize); // create and init the child reflow state, using placement new on // stack space allocated by the caller, so that the caller can destroy // it nsHTMLReflowState &childRS = * new (aChildRSSpace) nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, childRS); // see if we need to reset top-of-page due to a caption if (childRS.mFlags.mIsTopOfPage && mCaptionFrames.FirstChild() == aChildFrame) { uint8_t captionSide = GetCaptionSide(); if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { childRS.mFlags.mIsTopOfPage = false; } } }
void nsTableWrapperFrame::OuterBeginReflowChild(nsPresContext* aPresContext, nsIFrame* aChildFrame, const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI, nscoord aAvailISize) { // work around pixel rounding errors, round down to ensure we don't exceed the avail height in WritingMode wm = aChildFrame->GetWritingMode(); LogicalSize outerSize = aOuterRI.AvailableSize(wm); nscoord availBSize = outerSize.BSize(wm); if (NS_UNCONSTRAINEDSIZE != availBSize) { if (mCaptionFrames.FirstChild() == aChildFrame) { availBSize = NS_UNCONSTRAINEDSIZE; } else { LogicalMargin margin(wm); GetChildMargin(aPresContext, aOuterRI, aChildFrame, outerSize.ISize(wm), margin); NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BStart(wm), "No unconstrainedsize arithmetic, please"); availBSize -= margin.BStart(wm); NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BEnd(wm), "No unconstrainedsize arithmetic, please"); availBSize -= margin.BEnd(wm); } } LogicalSize availSize(wm, aAvailISize, availBSize); // create and init the child reflow state, using passed-in Maybe<>, // so that caller can use it after we return. aChildRI.emplace(aPresContext, aOuterRI, aChildFrame, availSize, nullptr, ReflowInput::CALLER_WILL_INIT); InitChildReflowInput(*aPresContext, *aChildRI); // see if we need to reset top-of-page due to a caption if (aChildRI->mFlags.mIsTopOfPage && mCaptionFrames.FirstChild() == aChildFrame) { uint8_t captionSide = GetCaptionSide(); if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { aChildRI->mFlags.mIsTopOfPage = false; } } }
void nsGridContainerFrame::CalculateTrackSizes(const LogicalSize& aPercentageBasis, const nsStylePosition* aStyle, nsTArray<TrackSize>& aColSizes, nsTArray<TrackSize>& aRowSizes) { aColSizes.SetLength(mGridColEnd - 1); aRowSizes.SetLength(mGridRowEnd - 1); WritingMode wm = GetWritingMode(); InitializeTrackSizes(aPercentageBasis.ISize(wm), aStyle->mGridTemplateColumns.mMinTrackSizingFunctions, aStyle->mGridTemplateColumns.mMaxTrackSizingFunctions, aStyle->mGridAutoColumnsMin, aStyle->mGridAutoColumnsMax, aColSizes); InitializeTrackSizes(aPercentageBasis.BSize(wm), aStyle->mGridTemplateRows.mMinTrackSizingFunctions, aStyle->mGridTemplateRows.mMaxTrackSizingFunctions, aStyle->mGridAutoRowsMin, aStyle->mGridAutoRowsMax, aRowSizes); }
LogicalSize nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, bool aShrinkWrap) { float inflation = nsLayoutUtils::FontSizeInflationFor(this); LogicalSize autoSize(aWM); nsresult rv = CalcIntrinsicSize(aRenderingContext, aWM, autoSize, inflation); if (NS_FAILED(rv)) { // What now? autoSize.SizeTo(aWM, 0, 0); } #ifdef DEBUG // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width else { const nsStyleCoord& inlineStyleCoord = aWM.IsVertical() ? StylePosition()->mHeight : StylePosition()->mWidth; if (inlineStyleCoord.GetUnit() == eStyleUnit_Auto) { LogicalSize ancestorAutoSize = nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aShrinkWrap); // Disabled when there's inflation; see comment in GetPrefSize. MOZ_ASSERT(inflation != 1.0f || ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM), "Incorrect size computed by ComputeAutoSize?"); } } #endif return autoSize; }
// Compute the margin-box width of aChildFrame given the inputs. If // aMarginResult is non-null, fill it with the part of the margin-width // that was contributed by the margin. static nscoord ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext, nsIFrame *aChildFrame, nsSize aCBSize, nscoord aAvailableWidth, nscoord *aMarginResult = nullptr) { AutoMaybeDisableFontInflation an(aChildFrame); nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width); WritingMode wm = offsets.GetWritingMode(); LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, wm, LogicalSize(wm, aCBSize), aAvailableWidth, offsets.ComputedLogicalMargin().Size(wm), offsets.ComputedLogicalBorderPadding().Size(wm) - offsets.ComputedLogicalPadding().Size(wm), offsets.ComputedLogicalPadding().Size(wm), nsIFrame::ComputeSizeFlags::eShrinkWrap); if (aMarginResult) *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(wm); return size.ISize(wm) + offsets.ComputedLogicalMargin().IStartEnd(wm) + offsets.ComputedLogicalBorderPadding().IStartEnd(wm); }
void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE, "Should have a precomputed inline-size!"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- bool reflowInner; bool reflowLegend; nsIFrame* legend = GetLegend(); nsIFrame* inner = GetInner(); if (aReflowState.ShouldReflowAllKids()) { reflowInner = inner != nullptr; reflowLegend = legend != nullptr; } else { reflowInner = inner && NS_SUBTREE_DIRTY(inner); reflowLegend = legend && NS_SUBTREE_DIRTY(legend); } // We don't allow fieldsets to break vertically. If we did, we'd // need logic here to push and pull overflow frames. // Since we're not applying our padding in this frame, we need to add it here // to compute the available width for our children. WritingMode wm = GetWritingMode(); WritingMode innerWM = inner ? inner->GetWritingMode() : wm; WritingMode legendWM = legend ? legend->GetWritingMode() : wm; LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM); LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM); innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) = NS_UNCONSTRAINEDSIZE; NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, inner, nsLayoutUtils::MIN_ISIZE) <= innerAvailSize.ISize(innerWM), "Bogus availSize.ISize; should be bigger"); NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, legend, nsLayoutUtils::MIN_ISIZE) <= legendAvailSize.ISize(legendWM), "Bogus availSize.ISize; should be bigger"); // get our border and padding LogicalMargin border = aReflowState.ComputedLogicalBorderPadding() - aReflowState.ComputedLogicalPadding(); // Figure out how big the legend is if there is one. // get the legend's margin LogicalMargin legendMargin(wm); // reflow the legend only if needed Maybe<nsHTMLReflowState> legendReflowState; if (legend) { legendReflowState.emplace(aPresContext, aReflowState, legend, legendAvailSize); } if (reflowLegend) { nsHTMLReflowMetrics legendDesiredSize(aReflowState); // We'll move the legend to its proper place later, so the position // and containerSize passed here are unimportant. const nsSize dummyContainerSize; ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.Width(), legendDesiredSize.Height()); #endif // figure out the legend's rectangle legendMargin = legend->GetLogicalUsedMargin(wm); mLegendRect = LogicalRect(wm, 0, 0, legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm), legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm)); nscoord oldSpace = mLegendSpace; mLegendSpace = 0; if (mLegendRect.BSize(wm) > border.BStart(wm)) { // center the border on the legend mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm); } else { mLegendRect.BStart(wm) = (border.BStart(wm) - mLegendRect.BSize(wm)) / 2; } // if the legend space changes then we need to reflow the // content area as well. if (mLegendSpace != oldSpace && inner) { reflowInner = true; } FinishReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState.ptr(), wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME); } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. legendMargin = legend->GetLogicalUsedMargin(wm); } // This containerSize is incomplete as yet: it does not include the size // of the |inner| frame itself. nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) + border.Size(wm)).GetPhysicalSize(wm); // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our // height is unconstrained (in which case the child's will be too). if (aReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.SetComputedBSize( std::max(0, aReflowState.ComputedBSize() - mLegendSpace)); } if (aReflowState.ComputedMinBSize() > 0) { kidReflowState.ComputedMinBSize() = std::max(0, aReflowState.ComputedMinBSize() - mLegendSpace); } if (aReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.ComputedMaxBSize() = std::max(0, aReflowState.ComputedMaxBSize() - mLegendSpace); } nsHTMLReflowMetrics kidDesiredSize(kidReflowState, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace); // We don't know the correct containerSize until we have reflowed |inner|, // so we use a dummy value for now; FinishReflowChild will fix the position // if necessary. const nsSize dummyContainerSize; ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, wm, pt, dummyContainerSize, 0, aStatus); // Update containerSize to account for size of the inner frame, so that // FinishReflowChild can position it correctly. containerSize += kidDesiredSize.PhysicalSize(); FinishReflowChild(inner, aPresContext, kidDesiredSize, &kidReflowState, wm, pt, containerSize, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } else if (inner) { // |inner| didn't need to be reflowed but we do need to include its size // in containerSize. containerSize += inner->GetSize(); } LogicalRect contentRect(wm); if (inner) { // We don't support margins on inner, so our content rect is just the // inner's border-box. (We don't really care about container size at this // point, as we'll figure out the actual positioning later.) contentRect = inner->GetLogicalRect(wm, containerSize); } // Our content rect must fill up the available width LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm); if (availSize.ISize(wm) > contentRect.ISize(wm)) { contentRect.ISize(wm) = innerAvailSize.ISize(wm); } if (legend) { // The legend is positioned inline-wards within the inner's content rect // (so that padding on the fieldset affects the legend position). LogicalRect innerContentRect = contentRect; innerContentRect.Deflate(wm, aReflowState.ComputedLogicalPadding()); // If the inner content rect is larger than the legend, we can align the // legend. if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) { int32_t align = static_cast<nsLegendFrame*> (legend->GetContentInsertionFrame())->GetAlign(); if (!wm.IsBidiLTR()) { if (align == NS_STYLE_TEXT_ALIGN_LEFT || align == NS_STYLE_TEXT_ALIGN_MOZ_LEFT) { align = NS_STYLE_TEXT_ALIGN_END; } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT || align == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) { align = NS_STYLE_TEXT_ALIGN_DEFAULT; } } switch (align) { case NS_STYLE_TEXT_ALIGN_END: mLegendRect.IStart(wm) = innerContentRect.IEnd(wm) - mLegendRect.ISize(wm); break; case NS_STYLE_TEXT_ALIGN_CENTER: case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: // Note: rounding removed; there doesn't seem to be any need mLegendRect.IStart(wm) = innerContentRect.IStart(wm) + (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2; break; default: mLegendRect.IStart(wm) = innerContentRect.IStart(wm); break; } } else { // otherwise make place for the legend mLegendRect.IStart(wm) = innerContentRect.IStart(wm); innerContentRect.ISize(wm) = mLegendRect.ISize(wm); contentRect.ISize(wm) = mLegendRect.ISize(wm) + aReflowState.ComputedLogicalPadding().IStartEnd(wm); } // place the legend LogicalRect actualLegendRect = mLegendRect; actualLegendRect.Deflate(wm, legendMargin); LogicalPoint actualLegendPos(actualLegendRect.Origin(wm)); // Note that legend's writing mode may be different from the fieldset's, // so we need to convert offsets before applying them to it (bug 1134534). LogicalMargin offsets = legendReflowState->ComputedLogicalOffsets(). ConvertTo(wm, legendReflowState->GetWritingMode()); nsHTMLReflowState::ApplyRelativePositioning(legend, wm, offsets, &actualLegendPos, containerSize); legend->SetPosition(wm, actualLegendPos, containerSize); nsContainerFrame::PositionFrameView(legend); nsContainerFrame::PositionChildViews(legend); } // Return our size and our result. LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm), mLegendSpace + border.BStartEnd(wm) + (inner ? inner->BSize(wm) : 0)); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (legend) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend); } if (inner) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner); } // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); InvalidateFrame(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: const nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding(); WritingMode wm = aFirstKid->GetWritingMode(); LogicalSize availSize = aButtonReflowState.ComputedSize(GetWritingMode()); availSize.BSize(wm) = NS_INTRINSICSIZE; // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= LogicalMargin(wm, focusPadding).IStartEnd(wm); // See whether out availSize's width is big enough. If it's smaller than our // intrinsic min width, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available width and our left // offset to allow the kid to spill left into our padding. nscoord xoffset = focusPadding.left + aButtonReflowState.ComputedPhysicalBorderPadding().left; nscoord extrawidth = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedWidth(); if (extrawidth > 0) { nscoord extraleft = extrawidth / 2; nscoord extraright = extrawidth - extraleft; NS_ASSERTION(extraright >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding extraleft = std::min(extraleft, aButtonReflowState.ComputedPhysicalPadding().left); extraright = std::min(extraright, aButtonReflowState.ComputedPhysicalPadding().right); xoffset -= extraleft; availSize.Width(wm) = availSize.Width(wm) + extraleft + extraright; } availSize.Width(wm) = std::max(availSize.Width(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, xoffset, focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box height: nscoord buttonContentBoxHeight = 0; if (aButtonReflowState.ComputedHeight() != NS_INTRINSICSIZE) { // Button has a fixed height -- that's its content-box height. buttonContentBoxHeight = aButtonReflowState.ComputedHeight(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' height, plus any focus-padding space: buttonContentBoxHeight = contentsDesiredSize.Height() + focusPadding.TopBottom(); // Make sure we obey min/max-height in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedHeight()). Note that we do this before // adjusting for borderpadding, since mComputedMaxHeight and // mComputedMinHeight are content heights. buttonContentBoxHeight = NS_CSS_MINMAX(buttonContentBoxHeight, aButtonReflowState.ComputedMinHeight(), aButtonReflowState.ComputedMaxHeight()); } // Center child vertically in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBoxHeight - focusPadding.TopBottom() - contentsDesiredSize.Height(); nscoord yoffset = std::max(0, extraSpace / 2); // Adjust yoffset to be in terms of the button's frame-rect, instead of // its focus-padding rect: yoffset += focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top; // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, xoffset, yoffset, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.Width() = aButtonReflowState.ComputedWidth() + aButtonReflowState.ComputedPhysicalBorderPadding().LeftRight(); aButtonDesiredSize.Height() = buttonContentBoxHeight + aButtonReflowState.ComputedPhysicalBorderPadding().TopBottom(); // * Button's ascent is its child's ascent, plus the child's y-offset // within our frame: aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + yoffset); aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
/* virtual */ LogicalSize nsTableWrapperFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { nscoord kidAvailableISize = aAvailableISize - aMargin.ISize(aWM); NS_ASSERTION(aBorder.IsAllZero() && aPadding.IsAllZero(), "Table wrapper frames cannot have borders or paddings"); // When we're shrink-wrapping, our auto size needs to wrap around the // actual size of the table, which (if it is specified as a percent) // could be something that is not reflected in our GetMinISize and // GetPrefISize. See bug 349457 for an example. // Match the availableISize logic in Reflow. uint8_t captionSide = GetCaptionSide(); nscoord inlineSize; if (captionSide == NO_SIDE) { inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize); } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { nscoord capISize = ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(), aWM, aCBSize, kidAvailableISize); inlineSize = capISize + ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize - capISize); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { nscoord margin; inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize, &margin); nscoord capISize = ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(), aWM, aCBSize, inlineSize - margin); if (capISize > inlineSize) { inlineSize = capISize; } } else { NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize); nscoord capISize = ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(), aWM, aCBSize, kidAvailableISize); if (capISize > inlineSize) { inlineSize = capISize; } } return LogicalSize(aWM, inlineSize, NS_UNCONSTRAINEDSIZE); }
// Returns true if this function managed to successfully move a frame, and // false if it could not process the position change, and a reflow should // be performed instead. bool RecomputePosition(nsIFrame* aFrame) { // Don't process position changes on table frames, since we already handle // the dynamic position change on the table wrapper frame, and the // reflow-based fallback code path also ignores positions on inner table // frames. if (aFrame->GetType() == nsGkAtoms::tableFrame) { return true; } const nsStyleDisplay* display = aFrame->StyleDisplay(); // Changes to the offsets of a non-positioned element can safely be ignored. if (display->mPosition == NS_STYLE_POSITION_STATIC) { return true; } // Don't process position changes on frames which have views or the ones which // have a view somewhere in their descendants, because the corresponding view // needs to be repositioned properly as well. if (aFrame->HasView() || (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) { StyleChangeReflow(aFrame, nsChangeHint_NeedReflow); return false; } aFrame->SchedulePaint(); // For relative positioning, we can simply update the frame rect if (display->IsRelativelyPositionedStyle()) { // Move the frame if (display->mPosition == NS_STYLE_POSITION_STICKY) { if (display->IsInnerTableStyle()) { // We don't currently support sticky positioning of inner table // elements (bug 975644). Bail. // // When this is fixed, remove the null-check for the computed // offsets in nsTableRowFrame::ReflowChildren. return true; } // Update sticky positioning for an entire element at once, starting with // the first continuation or ib-split sibling. // It's rare that the frame we already have isn't already the first // continuation or ib-split sibling, but it can happen when styles differ // across continuations such as ::first-line or ::first-letter, and in // those cases we will generally (but maybe not always) do the work twice. nsIFrame* firstContinuation = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); StickyScrollContainer::ComputeStickyOffsets(firstContinuation); StickyScrollContainer* ssc = StickyScrollContainer::GetStickyScrollContainerForFrame( firstContinuation); if (ssc) { ssc->PositionContinuations(firstContinuation); } } else { MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition, "Unexpected type of positioning"); for (nsIFrame* cont = aFrame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { nsIFrame* cb = cont->GetContainingBlock(); nsMargin newOffsets; WritingMode wm = cb->GetWritingMode(); const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size()); ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets); NS_ASSERTION(newOffsets.left == -newOffsets.right && newOffsets.top == -newOffsets.bottom, "ComputeRelativeOffsets should return valid results"); // ReflowInput::ApplyRelativePositioning would work here, but // since we've already checked mPosition and aren't changing the frame's // normal position, go ahead and add the offsets directly. cont->SetPosition(cont->GetNormalPosition() + nsPoint(newOffsets.left, newOffsets.top)); } } return true; } // For the absolute positioning case, set up a fake HTML reflow state for // the frame, and then get the offsets and size from it. If the frame's size // doesn't need to change, we can simply update the frame position. Otherwise // we fall back to a reflow. nsRenderingContext rc( aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext()); // Construct a bogus parent reflow state so that there's a usable // containing block reflow state. nsIFrame* parentFrame = aFrame->GetParent(); WritingMode parentWM = parentFrame->GetWritingMode(); WritingMode frameWM = aFrame->GetWritingMode(); LogicalSize parentSize = parentFrame->GetLogicalSize(); nsFrameState savedState = parentFrame->GetStateBits(); ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, &rc, parentSize); parentFrame->RemoveStateBits(~nsFrameState(0)); parentFrame->AddStateBits(savedState); // The bogus parent state here was created with no parent state of its own, // and therefore it won't have an mCBReflowInput set up. // But we may need one (for InitCBReflowInput in a child state), so let's // try to create one here for the cases where it will be needed. Maybe<ReflowInput> cbReflowInput; nsIFrame* cbFrame = parentFrame->GetContainingBlock(); if (cbFrame && (aFrame->GetContainingBlock() != parentFrame || parentFrame->GetType() == nsGkAtoms::tableFrame)) { LogicalSize cbSize = cbFrame->GetLogicalSize(); cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize); cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin(); cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding(); cbReflowInput->ComputedPhysicalBorderPadding() = cbFrame->GetUsedBorderAndPadding(); parentReflowInput.mCBReflowInput = cbReflowInput.ptr(); } NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE && parentSize.BSize(parentWM) != NS_INTRINSICSIZE, "parentSize should be valid"); parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0)); parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0)); parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding(); parentReflowInput.ComputedPhysicalBorderPadding() = parentFrame->GetUsedBorderAndPadding(); LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM); availSize.BSize(frameWM) = NS_INTRINSICSIZE; ViewportFrame* viewport = do_QueryFrame(parentFrame); nsSize cbSize = viewport ? viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size() : aFrame->GetContainingBlock()->GetSize(); const nsMargin& parentBorder = parentReflowInput.mStyleBorder->GetComputedBorder(); cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom()); LogicalSize lcbSize(frameWM, cbSize); ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame, availSize, &lcbSize); nsSize computedSize(reflowInput.ComputedWidth(), reflowInput.ComputedHeight()); computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight(); if (computedSize.height != NS_INTRINSICSIZE) { computedSize.height += reflowInput.ComputedPhysicalBorderPadding().TopBottom(); } nsSize size = aFrame->GetSize(); // The RecomputePosition hint is not used if any offset changed between auto // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new // element height will be its intrinsic height, and since 'top' and 'bottom''s // auto-ness hasn't changed, the old height must also be its intrinsic // height, which we can assume hasn't changed (or reflow would have // been triggered). if (computedSize.width == size.width && (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) { // If we're solving for 'left' or 'top', then compute it here, in order to // match the reflow code path. if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) { reflowInput.ComputedPhysicalOffsets().left = cbSize.width - reflowInput.ComputedPhysicalOffsets().right - reflowInput.ComputedPhysicalMargin().right - size.width - reflowInput.ComputedPhysicalMargin().left; } if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) { reflowInput.ComputedPhysicalOffsets().top = cbSize.height - reflowInput.ComputedPhysicalOffsets().bottom - reflowInput.ComputedPhysicalMargin().bottom - size.height - reflowInput.ComputedPhysicalMargin().top; } // Move the frame nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left + reflowInput.ComputedPhysicalMargin().left, parentBorder.top + reflowInput.ComputedPhysicalOffsets().top + reflowInput.ComputedPhysicalMargin().top); aFrame->SetPosition(pos); return true; } // Fall back to a reflow StyleChangeReflow(aFrame, nsChangeHint_NeedReflow); return false; }
void nsTableWrapperFrame::SetDesiredSize(uint8_t aCaptionSide, const LogicalSize& aInnerSize, const LogicalSize& aCaptionSize, const LogicalMargin& aInnerMargin, const LogicalMargin& aCaptionMargin, nscoord& aISize, nscoord& aBSize, WritingMode aWM) { aISize = aBSize = 0; // compute the overall inline-size switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_LEFT: aISize = std::max(aInnerMargin.LineLeft(aWM), aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) + aInnerSize.ISize(aWM) + aInnerMargin.LineRight(aWM); break; case NS_STYLE_CAPTION_SIDE_RIGHT: aISize = std::max(aInnerMargin.LineRight(aWM), aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) + aInnerSize.ISize(aWM) + aInnerMargin.LineLeft(aWM); break; default: aISize = std::max(aInnerMargin.IStartEnd(aWM) + aInnerSize.ISize(aWM), aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)); break; } // compute the overall block-size switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_TOP: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM); aBSize += std::max(aInnerMargin.BStart(aWM), aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM)); break; case NS_STYLE_CAPTION_SIDE_BOTTOM: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BStart(aWM); aBSize += std::max(aInnerMargin.BEnd(aWM), aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM)); break; case NS_STYLE_CAPTION_SIDE_LEFT: case NS_STYLE_CAPTION_SIDE_RIGHT: aBSize = aInnerMargin.BStart(aWM); aBSize += std::max(aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM), aCaptionSize.BSize(aWM) + aCaptionMargin.BEnd(aWM)); break; default: NS_ASSERTION(aCaptionSide == NO_SIDE, "unexpected caption side"); aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BStartEnd(aWM); break; } // negative sizes can upset overflow-area code aISize = std::max(aISize, 0); aBSize = std::max(aBSize, 0); }
nsresult nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext, WritingMode aWM, LogicalSize& aIntrinsicSize, float aFontSizeInflation) { // Get leading and the Average/MaxAdvance char width nscoord lineHeight = 0; nscoord charWidth = 0; nscoord charMaxAdvance = 0; RefPtr<nsFontMetrics> fontMet; nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), aFontSizeInflation); NS_ENSURE_SUCCESS(rv, rv); lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), NS_AUTOHEIGHT, aFontSizeInflation); charWidth = fontMet->AveCharWidth(); charMaxAdvance = fontMet->MaxAdvance(); // Set the width equal to the width in characters int32_t cols = GetCols(); aIntrinsicSize.ISize(aWM) = cols * charWidth; // To better match IE, take the maximum character width(in twips) and remove // 4 pixels add this on as additional padding(internalPadding). But only do // this if we think we have a fixed-width font. if (mozilla::Abs(charWidth - charMaxAdvance) > (unsigned)nsPresContext::CSSPixelsToAppUnits(1)) { nscoord internalPadding = std::max(0, charMaxAdvance - nsPresContext::CSSPixelsToAppUnits(4)); nscoord t = nsPresContext::CSSPixelsToAppUnits(1); // Round to a multiple of t nscoord rest = internalPadding % t; if (rest < t - rest) { internalPadding -= rest; } else { internalPadding += t - rest; } // Now add the extra padding on (so that small input sizes work well) aIntrinsicSize.ISize(aWM) += internalPadding; } else { // This is to account for the anonymous <br> having a 1 twip width // in Full Standards mode, see BRFrame::Reflow and bug 228752. if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) { aIntrinsicSize.ISize(aWM) += 1; } } // Increment width with cols * letter-spacing. { const nsStyleCoord& lsCoord = StyleText()->mLetterSpacing; if (eStyleUnit_Coord == lsCoord.GetUnit()) { nscoord letterSpacing = lsCoord.GetCoordValue(); if (letterSpacing != 0) { aIntrinsicSize.ISize(aWM) += cols * letterSpacing; } } } // Set the height equal to total number of rows (times the height of each // line, of course) aIntrinsicSize.BSize(aWM) = lineHeight * GetRows(); // Add in the size of the scrollbars for textarea if (IsTextArea()) { nsIFrame* first = PrincipalChildList().FirstChild(); nsIScrollableFrame *scrollableFrame = do_QueryFrame(first); NS_ASSERTION(scrollableFrame, "Child must be scrollable"); if (scrollableFrame) { LogicalMargin scrollbarSizes(aWM, scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext)); aIntrinsicSize.ISize(aWM) += scrollbarSizes.IStartEnd(aWM); aIntrinsicSize.BSize(aWM) += scrollbarSizes.BStartEnd(aWM); } } return NS_OK; }
nsresult nsTableWrapperFrame::GetCaptionOrigin(uint32_t aCaptionSide, const LogicalSize& aContainBlockSize, const LogicalSize& aInnerSize, const LogicalMargin& aInnerMargin, const LogicalSize& aCaptionSize, LogicalMargin& aCaptionMargin, LogicalPoint& aOrigin, WritingMode aWM) { aOrigin.I(aWM) = aOrigin.B(aWM) = 0; if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) || (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) { return NS_OK; } if (mCaptionFrames.IsEmpty()) { return NS_OK; } NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) && NS_AUTOMARGIN != aCaptionMargin.BStart(aWM) && NS_AUTOMARGIN != aCaptionMargin.BEnd(aWM), "The computed caption margin is auto?"); // inline-dir computation switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_BOTTOM: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // We placed the caption using only the table's isize as available // isize, and we should position it this way as well. aOrigin.I(aWM) += aInnerMargin.IStart(aWM); } break; case NS_STYLE_CAPTION_SIDE_LEFT: case NS_STYLE_CAPTION_SIDE_RIGHT: aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT)) { aOrigin.I(aWM) += aInnerMargin.IStart(aWM) + aInnerSize.ISize(aWM); } break; default: // block-start NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE, "unexpected caption side"); aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) { // We placed the caption using only the table's isize as available // isize, and we should position it this way as well. aOrigin.I(aWM) += aInnerMargin.IStart(aWM); } break; } // block-dir computation switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_RIGHT: case NS_STYLE_CAPTION_SIDE_LEFT: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); switch (GetCaptionVerticalAlign()) { case NS_STYLE_VERTICAL_ALIGN_MIDDLE: aOrigin.B(aWM) = std::max(0, aInnerMargin.BStart(aWM) + ((aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)) / 2)); break; case NS_STYLE_VERTICAL_ALIGN_BOTTOM: aOrigin.B(aWM) = std::max(0, aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)); break; default: break; } break; case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: case NS_STYLE_CAPTION_SIDE_BOTTOM: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) + aCaptionMargin.BStart(aWM); break; case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: case NS_STYLE_CAPTION_SIDE_TOP: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionMargin.BStart(aWM); break; default: NS_NOTREACHED("Unknown caption alignment type"); break; } return NS_OK; }
nsresult nsTableWrapperFrame::GetInnerOrigin(uint32_t aCaptionSide, const LogicalSize& aContainBlockSize, const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin, const LogicalSize& aInnerSize, LogicalMargin& aInnerMargin, LogicalPoint& aOrigin, WritingMode aWM) { NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) && NS_AUTOMARGIN != aCaptionMargin.IEnd(aWM), "The computed caption margin is auto?"); NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.IStart(aWM) && NS_AUTOMARGIN != aInnerMargin.IEnd(aWM) && NS_AUTOMARGIN != aInnerMargin.BStart(aWM) && NS_AUTOMARGIN != aInnerMargin.BEnd(aWM), "The computed inner margin is auto?"); aOrigin.I(aWM) = aOrigin.B(aWM) = 0; if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) || (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) { return NS_OK; } nscoord minCapISize = aCaptionSize.ISize(aWM) + aCaptionMargin.IStartEnd(aWM); // inline-dir computation switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_LEFT: case NS_STYLE_CAPTION_SIDE_RIGHT: if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_LEFT)) { if (aInnerMargin.IStart(aWM) < minCapISize) { // shift the inner table to get some place for the caption aInnerMargin.IEnd(aWM) += aInnerMargin.IStart(aWM) - minCapISize; aInnerMargin.IEnd(aWM) = std::max(0, aInnerMargin.IEnd(aWM)); aInnerMargin.IStart(aWM) = minCapISize; } } aOrigin.I(aWM) = aInnerMargin.IStart(aWM); break; default: NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE || aCaptionSide == NO_SIDE, "unexpected caption side"); aOrigin.I(aWM) = aInnerMargin.IStart(aWM); break; } // block-dir computation switch (aCaptionSide) { case NS_STYLE_CAPTION_SIDE_BOTTOM: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); break; case NS_STYLE_CAPTION_SIDE_LEFT: case NS_STYLE_CAPTION_SIDE_RIGHT: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); switch (GetCaptionVerticalAlign()) { case NS_STYLE_VERTICAL_ALIGN_MIDDLE: aOrigin.B(aWM) = std::max(aInnerMargin.BStart(aWM), (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2); break; case NS_STYLE_VERTICAL_ALIGN_BOTTOM: aOrigin.B(aWM) = std::max(aInnerMargin.BStart(aWM), aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)); break; default: break; } break; case NO_SIDE: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: case NS_STYLE_CAPTION_SIDE_TOP: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM); break; default: NS_NOTREACHED("Unknown caption alignment type"); break; } return NS_OK; }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); LogicalSize availSize = aButtonReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_INTRINSICSIZE; // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: LogicalMargin focusPadding = LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding()); // See whether out availSize's inline-size is big enough. If it's // smaller than our intrinsic min iSize, that means that the kid // wouldn't really fit. In that case, we overflow into our internal // focuspadding (which other browsers don't have) so that there's a // little more space for it. // Note that GetMinISize includes the focusPadding. nscoord IOverflow = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedISize(); nscoord IFocusPadding = focusPadding.IStartEnd(wm); nscoord focusPaddingReduction = std::min(IFocusPadding, std::max(IOverflow, 0)); if (focusPaddingReduction > 0) { nscoord startReduction = focusPadding.IStart(wm); if (focusPaddingReduction != IFocusPadding) { startReduction = NSToCoordRound(startReduction * (float(focusPaddingReduction) / float(IFocusPadding))); } focusPadding.IStart(wm) -= startReduction; focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction; } // shorthand for a value we need to use in a bunch of places const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding(); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= focusPadding.IStartEnd(wm); LogicalPoint childPos(wm); childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm); availSize.ISize(wm) = std::max(availSize.ISize(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding.GetPhysicalMargin(wm)); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); childPos.B(wm) = 0; // This will be set properly later, after reflowing the // child to determine its size. // We just pass a dummy containerSize here, as the child will be // repositioned later by FinishReflowChild. nsSize dummyContainerSize; ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, wm, childPos, dummyContainerSize, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box size: LogicalSize buttonContentBox(wm); if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBox.BSize(wm) = aButtonReflowState.ComputedBSize(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' bSize, plus any focus-padding space: buttonContentBox.BSize(wm) = contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm); // Make sure we obey min/max-bSize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since mComputedMaxBSize and // mComputedMinBSize are content bSizes. buttonContentBox.BSize(wm) = NS_CSS_MINMAX(buttonContentBox.BSize(wm), aButtonReflowState.ComputedMinBSize(), aButtonReflowState.ComputedMaxBSize()); } if (aButtonReflowState.ComputedISize() != NS_INTRINSICSIZE) { buttonContentBox.ISize(wm) = aButtonReflowState.ComputedISize(); } else { buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm); buttonContentBox.ISize(wm) = NS_CSS_MINMAX(buttonContentBox.ISize(wm), aButtonReflowState.ComputedMinISize(), aButtonReflowState.ComputedMaxISize()); } // Center child in the block-direction in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) - contentsDesiredSize.BSize(wm); childPos.B(wm) = std::max(0, extraSpace / 2); // Adjust childPos.B() to be in terms of the button's frame-rect, instead of // its focus-padding rect: childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm); nsSize containerSize = (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, wm, childPos, containerSize, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.SetSize(wm, LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm), buttonContentBox.BSize(wm) + clbp.BStartEnd(wm))); // * Button's ascent is its child's ascent, plus the child's block-offset // within our frame... unless it's orthogonal, in which case we'll use the // contents inline-size as an approximation for now. // XXX is there a better strategy? should we include border-padding? if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm)); } else { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + childPos.B(wm)); } aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus); // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize availSize = aReflowInput.AvailableSize(); const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding(); NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE, "should no longer use unconstrained inline size"); availSize.ISize(wm) -= bp.IStartEnd(wm); if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { availSize.BSize(wm) -= bp.BStartEnd(wm); } WritingMode lineWM = aMetrics.GetWritingMode(); ReflowOutput kidMetrics(lineWM); // Reflow the child if (!aReflowInput.mLineLayout) { // When there is no lineLayout provided, we provide our own. The // only time that the first-letter-frame is not reflowing in a // line context is when its floating. WritingMode kidWritingMode = WritingModeForLine(wm, kid); LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm); ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize); nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr); ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm), availSize.ISize(wm), NS_UNCONSTRAINEDSIZE, false, true, kidWritingMode, nsSize(aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); rs.mLineLayout = ≪ ll.SetInFirstLetter(true); ll.SetFirstLetterStyleOK(true); kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus); ll.EndLineReflow(); ll.SetInFirstLetter(false); // In the floating first-letter case, we need to set this ourselves; // nsLineLayout::BeginSpan will set it in the other case mBaseline = kidMetrics.BlockStartAscent(); // Place and size the child and update the output metrics LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM); kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm), convertedSize.ISize(wm), convertedSize.BSize(wm))); kid->FinishAndStoreOverflow(&kidMetrics); kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); convertedSize.ISize(wm) += bp.IStartEnd(wm); convertedSize.BSize(wm) += bp.BStartEnd(wm); aMetrics.SetSize(wm, convertedSize); aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); // Ensure that the overflow rect contains the child textframe's // overflow rect. // Note that if this is floating, the overline/underline drawable // area is in the overflow rect of the child textframe. aMetrics.UnionOverflowAreasWithDesiredBounds(); ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); FinishAndStoreOverflow(&aMetrics); } else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowInput.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowInput, bp.IStart(wm), availSize.ISize(wm), &mBaseline); ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame); NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(), "we're assuming we can mix sizes between lineWM and wm " "since we shouldn't have orthogonal writing modes within " "a line."); aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm); ll->SetInFirstLetter(false); if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) { aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm); } else { nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm); } } if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { // Create a continuation or remove existing continuations based on // the reflow completion status. if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { if (aReflowInput.mLineLayout) { aReflowInput.mLineLayout->SetFirstLetterStyleOK(false); } nsIFrame* kidNextInFlow = kid->GetNextInFlow(); if (kidNextInFlow) { // Remove all of the childs next-in-flows kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already // have one. if (!IsFloating()) { CreateNextInFlow(kid); // And then push it to our overflow list const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid); if (overflow.NotEmpty()) { SetOverflowFrames(overflow); } } else if (!kid->GetNextInFlow()) { // For floating first letter frames (if a continuation wasn't already // created for us) we need to put the continuation with the rest of the // text that the first letter frame was made out of. nsIFrame* continuation; CreateContinuationForFloatingParent(aPresContext, kid, &continuation, true); } } } NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowInput, aMetrics); }
void nsTableWrapperFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aOuterRI, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableWrapperFrame"); DISPLAY_REFLOW(aPresContext, this, aOuterRI, aDesiredSize, aStatus); // Initialize out parameters aDesiredSize.ClearSize(); aStatus = NS_FRAME_COMPLETE; if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { // Set up our kids. They're already present, on an overflow list, // or there are none so we'll create them now MoveOverflowToChildList(); } Maybe<ReflowInput> captionRI; Maybe<ReflowInput> innerRI; nsRect origInnerRect = InnerTableFrame()->GetRect(); nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect(); bool innerFirstReflow = InnerTableFrame()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); nsRect origCaptionRect; nsRect origCaptionVisualOverflow; bool captionFirstReflow = false; if (mCaptionFrames.NotEmpty()) { origCaptionRect = mCaptionFrames.FirstChild()->GetRect(); origCaptionVisualOverflow = mCaptionFrames.FirstChild()->GetVisualOverflowRect(); captionFirstReflow = mCaptionFrames.FirstChild()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); } // ComputeAutoSize has to match this logic. WritingMode wm = aOuterRI.GetWritingMode(); uint8_t captionSide = GetCaptionSide(); WritingMode captionWM = wm; // will be changed below if necessary if (captionSide == NO_SIDE) { // We don't have a caption. OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI, aOuterRI.ComputedSize(wm).ISize(wm)); } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { // ComputeAutoSize takes care of making side captions small. Compute // the caption's size first, and tell the table to fit in what's left. OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI, captionRI, aOuterRI.ComputedSize(wm).ISize(wm)); captionWM = captionRI->GetWritingMode(); nscoord innerAvailISize = aOuterRI.ComputedSize(wm).ISize(wm) - captionRI->ComputedSizeWithMarginBorderPadding(wm).ISize(wm); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI, innerAvailISize); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // Compute the table's size first, and then prevent the caption from // being larger in the inline dir unless it has to be. // // Note that CSS 2.1 (but not 2.0) says: // The width of the anonymous box is the border-edge width of the // table box inside it // We don't actually make our anonymous box that isize (if we did, // it would break 'auto' margins), but this effectively does that. OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI, aOuterRI.ComputedSize(wm).ISize(wm)); // It's good that CSS 2.1 says not to include margins, since we // can't, since they already been converted so they exactly // fill the available isize (ignoring the margin on one side if // neither are auto). (We take advantage of that later when we call // GetCaptionOrigin, though.) nscoord innerBorderISize = innerRI->ComputedSizeWithBorderPadding(wm).ISize(wm); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI, captionRI, innerBorderISize); captionWM = captionRI->GetWritingMode(); } else { NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); // Size the table and the caption independently. captionWM = mCaptionFrames.FirstChild()->GetWritingMode(); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI, captionRI, aOuterRI.ComputedSize(captionWM).ISize(captionWM)); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI, aOuterRI.ComputedSize(wm).ISize(wm)); } // First reflow the caption. Maybe<ReflowOutput> captionMet; LogicalSize captionSize(wm); LogicalMargin captionMargin(wm); if (mCaptionFrames.NotEmpty()) { captionMet.emplace(wm); nsReflowStatus capStatus; // don't let the caption cause incomplete OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(), *captionRI, *captionMet, capStatus); captionSize.ISize(wm) = captionMet->ISize(wm); captionSize.BSize(wm) = captionMet->BSize(wm); captionMargin = captionRI->ComputedLogicalMargin().ConvertTo(wm, captionWM); // Now that we know the bsize of the caption, reduce the available bsize // for the table frame if we are bsize constrained and the caption is above // or below the inner table. Also reduce the CB size that we store for // our children in case we're a grid item, by the same amount. LogicalSize* cbSize = Properties().Get(GridItemCBSizeProperty()); if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize() || cbSize) { nscoord captionBSize = 0; nscoord captionISize = 0; switch (captionSide) { case NS_STYLE_CAPTION_SIDE_TOP: case NS_STYLE_CAPTION_SIDE_BOTTOM: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: captionBSize = captionSize.BSize(wm) + captionMargin.BStartEnd(wm); break; case NS_STYLE_CAPTION_SIDE_LEFT: case NS_STYLE_CAPTION_SIDE_RIGHT: captionISize = captionSize.ISize(wm) + captionMargin.IStartEnd(wm); break; } if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize()) { innerRI->AvailableBSize() = std::max(0, innerRI->AvailableBSize() - captionBSize); } if (cbSize) { // Shrink the CB size by the size reserved for the caption. LogicalSize oldCBSize = *cbSize; cbSize->ISize(wm) = std::max(0, cbSize->ISize(wm) - captionISize); cbSize->BSize(wm) = std::max(0, cbSize->BSize(wm) - captionBSize); if (oldCBSize != *cbSize) { // Reset the inner table's ReflowInput to stretch it to the new size. innerRI.reset(); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI, aOuterRI.ComputedSize(wm).ISize(wm)); } } } } // Then, now that we know how much to reduce the isize of the inner // table to account for side captions, reflow the inner table. ReflowOutput innerMet(innerRI->GetWritingMode()); OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRI, innerMet, aStatus); LogicalSize innerSize(wm, innerMet.ISize(wm), innerMet.BSize(wm)); LogicalMargin innerMargin = innerRI->ComputedLogicalMargin(); LogicalSize containSize(wm, GetContainingBlockSize(aOuterRI)); // Now that we've reflowed both we can place them. // XXXldb Most of the input variables here are now uninitialized! // XXX Need to recompute inner table's auto margins for the case of side // captions. (Caption's are broken too, but that should be fixed earlier.) // Compute the desiredSize so that we can use it as the containerSize // for the FinishReflowChild calls below. LogicalSize desiredSize(wm); SetDesiredSize(captionSide, innerSize, captionSize, innerMargin, captionMargin, desiredSize.ISize(wm), desiredSize.BSize(wm), wm); aDesiredSize.SetSize(wm, desiredSize); nsSize containerSize = aDesiredSize.PhysicalSize(); // XXX It's possible for this to be NS_UNCONSTRAINEDSIZE, which will result // in assertions from FinishReflowChild. if (mCaptionFrames.NotEmpty()) { LogicalPoint captionOrigin(wm); GetCaptionOrigin(captionSide, containSize, innerSize, innerMargin, captionSize, captionMargin, captionOrigin, wm); FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, *captionMet, captionRI.ptr(), wm, captionOrigin, containerSize, 0); captionRI.reset(); } // XXX If the bsize is constrained then we need to check whether // everything still fits... LogicalPoint innerOrigin(wm); GetInnerOrigin(captionSide, containSize, captionSize, captionMargin, innerSize, innerMargin, innerOrigin, wm); FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRI.ptr(), wm, innerOrigin, containerSize, 0); innerRI.reset(); nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect, origInnerVisualOverflow, innerFirstReflow); if (mCaptionFrames.NotEmpty()) { nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect, origCaptionVisualOverflow, captionFirstReflow); } UpdateOverflowAreas(aDesiredSize); if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aOuterRI, aDesiredSize.mOverflowAreas, 0, aStatus); } FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRI, aStatus); // Return our desired rect NS_FRAME_SET_TRUNCATION(aStatus, aOuterRI, aDesiredSize); }
/* virtual */ LogicalSize nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, uint32_t aFlags) { if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) { // The embedding element has sized itself using the CSS replaced element // sizing rules, using our intrinsic dimensions as necessary. The SVG spec // says that the width and height of embedded SVG is overridden by the // width and height of the embedding element, so we just need to size to // the viewport that the embedding element has established for us. return aCBSize; } LogicalSize cbSize = aCBSize; IntrinsicSize intrinsicSize = GetIntrinsicSize(); if (!mContent->GetParent()) { // We're the root of the outermost browsing context, so we need to scale // cbSize by the full-zoom so that SVGs with percentage width/height zoom: NS_ASSERTION(aCBSize.ISize(aWM) != NS_AUTOHEIGHT && aCBSize.BSize(aWM) != NS_AUTOHEIGHT, "root should not have auto-width/height containing block"); cbSize.ISize(aWM) *= PresContext()->GetFullZoom(); cbSize.BSize(aWM) *= PresContext()->GetFullZoom(); // We also need to honour the width and height attributes' default values // of 100% when we're the root of a browsing context. (GetIntrinsicSize() // doesn't report these since there's no such thing as a percentage // intrinsic size. Also note that explicit percentage values are mapped // into style, so the following isn't for them.) SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent); nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; if (width.IsPercentage()) { NS_ABORT_IF_FALSE(intrinsicSize.width.GetUnit() == eStyleUnit_None, "GetIntrinsicSize should have reported no " "intrinsic width"); float val = width.GetAnimValInSpecifiedUnits() / 100.0f; if (val < 0.0f) val = 0.0f; intrinsicSize.width.SetCoordValue(val * cbSize.Width(aWM)); } nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; NS_ASSERTION(aCBSize.BSize(aWM) != NS_AUTOHEIGHT, "root should not have auto-height containing block"); if (height.IsPercentage()) { NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_None, "GetIntrinsicSize should have reported no " "intrinsic height"); float val = height.GetAnimValInSpecifiedUnits() / 100.0f; if (val < 0.0f) val = 0.0f; intrinsicSize.height.SetCoordValue(val * cbSize.Height(aWM)); } NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_Coord && intrinsicSize.width.GetUnit() == eStyleUnit_Coord, "We should have just handled the only situation where" "we lack an intrinsic height or width."); } return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM, aRenderingContext, this, intrinsicSize, GetIntrinsicRatio(), cbSize, aMargin, aBorder, aPadding); }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); LogicalSize availSize = aButtonReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_INTRINSICSIZE; // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: const LogicalMargin focusPadding = LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding()); // shorthand for a value we need to use in a bunch of places const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding(); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= focusPadding.IStartEnd(wm); // See whether out availSize's inline-size is big enough. If it's smaller than // our intrinsic min iSize, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available iSize and our inline-start // offset to allow the kid to spill start-wards into our padding. nscoord ioffset = focusPadding.IStart(wm) + clbp.IStart(wm); nscoord extraISize = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedISize(); if (extraISize > 0) { nscoord extraIStart = extraISize / 2; nscoord extraIEnd = extraISize - extraIStart; NS_ASSERTION(extraIEnd >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding const LogicalMargin& padding = aButtonReflowState.ComputedLogicalPadding(); extraIStart = std::min(extraIStart, padding.IStart(wm)); extraIEnd = std::min(extraIEnd, padding.IEnd(wm)); ioffset -= extraIStart; availSize.ISize(wm) = availSize.ISize(wm) + extraIStart + extraIEnd; } availSize.ISize(wm) = std::max(availSize.ISize(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding.GetPhysicalMargin(wm)); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); nscoord boffset = focusPadding.BStart(wm) + clbp.BStart(wm); ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, isVertical ? boffset : ioffset, isVertical ? ioffset : boffset, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box height: nscoord buttonContentBoxBSize = 0; if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBoxBSize = aButtonReflowState.ComputedBSize(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' bSize, plus any focus-padding space: buttonContentBoxBSize = contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm); // Make sure we obey min/max-bSize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since mComputedMaxBSize and // mComputedMinBSize are content bSizes. buttonContentBoxBSize = NS_CSS_MINMAX(buttonContentBoxBSize, aButtonReflowState.ComputedMinBSize(), aButtonReflowState.ComputedMaxBSize()); } // Center child in the block-direction in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBoxBSize - focusPadding.BStartEnd(wm) - contentsDesiredSize.BSize(wm); boffset = std::max(0, extraSpace / 2); // Adjust boffset to be in terms of the button's frame-rect, instead of // its focus-padding rect: boffset += focusPadding.BStart(wm) + clbp.BStart(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, isVertical ? boffset : ioffset, isVertical ? ioffset : boffset, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.SetSize(wm, LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm), buttonContentBoxBSize + clbp.BStartEnd(wm))); // * Button's ascent is its child's ascent, plus the child's block-offset // within our frame... unless it's orthogonal, in which case we'll use the // contents inline-size as an approximation for now. // XXX is there a better strategy? should we include border-padding? if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm)); } else { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + boffset); } aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
void nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( nsPresContext* aPresContext, ReflowInput& aKidReflowInput, const LogicalSize& aKidSize, const LogicalMargin& aMargin, LogicalMargin* aOffsets, LogicalSize* aLogicalCBSize) { WritingMode wm = aKidReflowInput.GetWritingMode(); WritingMode outerWM = aKidReflowInput.mParentReflowInput->GetWritingMode(); // Now that we know the child's size, we resolve any sentinel values in its // IStart/BStart offset coordinates that depend on that size. // * NS_AUTOOFFSET indicates that the child's position in the given axis // is determined by its end-wards offset property, combined with its size and // available space. e.g.: "top: auto; height: auto; bottom: 50px" // * m{I,B}OffsetsResolvedAfterSize indicate that the child is using its // static position in that axis, *and* its static position is determined by // the axis-appropriate css-align property (which may require the child's // size, e.g. to center it within the parent). if ((NS_AUTOOFFSET == aOffsets->IStart(outerWM)) || (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) || aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign || aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { if (-1 == aLogicalCBSize->ISize(wm)) { // Get the containing block width/height const ReflowInput* parentRI = aKidReflowInput.mParentReflowInput; *aLogicalCBSize = aKidReflowInput.ComputeContainingBlockRectangle(aPresContext, parentRI); } const LogicalSize logicalCBSizeOuterWM = aLogicalCBSize->ConvertTo(outerWM, wm); // placeholderContainer is used in each of the m{I,B}OffsetsNeedCSSAlign // clauses. We declare it at this scope so we can avoid having to look // it up twice (and only look it up if it's needed). nsContainerFrame* placeholderContainer = nullptr; if (NS_AUTOOFFSET == aOffsets->IStart(outerWM)) { NS_ASSERTION(NS_AUTOOFFSET != aOffsets->IEnd(outerWM), "Can't solve for both start and end"); aOffsets->IStart(outerWM) = logicalCBSizeOuterWM.ISize(outerWM) - aOffsets->IEnd(outerWM) - aMargin.IStartEnd(outerWM) - aKidSize.ISize(outerWM); } else if (aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign) { placeholderContainer = GetPlaceholderContainer(aPresContext, aKidReflowInput.mFrame); nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, placeholderContainer, outerWM, eLogicalAxisInline); // Shift IStart from its current position (at start corner of the // alignment container) by the returned offset. And set IEnd to the // distance between the kid's end edge to containing block's end edge. aOffsets->IStart(outerWM) += offset; aOffsets->IEnd(outerWM) = logicalCBSizeOuterWM.ISize(outerWM) - (aOffsets->IStart(outerWM) + aKidSize.ISize(outerWM)); } if (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) { aOffsets->BStart(outerWM) = logicalCBSizeOuterWM.BSize(outerWM) - aOffsets->BEnd(outerWM) - aMargin.BStartEnd(outerWM) - aKidSize.BSize(outerWM); } else if (aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { if (!placeholderContainer) { placeholderContainer = GetPlaceholderContainer(aPresContext, aKidReflowInput.mFrame); } nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, placeholderContainer, outerWM, eLogicalAxisBlock); // Shift BStart from its current position (at start corner of the // alignment container) by the returned offset. And set BEnd to the // distance between the kid's end edge to containing block's end edge. aOffsets->BStart(outerWM) += offset; aOffsets->BEnd(outerWM) = logicalCBSizeOuterWM.BSize(outerWM) - (aOffsets->BStart(outerWM) + aKidSize.BSize(outerWM)); } aKidReflowInput.SetComputedLogicalOffsets(aOffsets->ConvertTo(wm, outerWM)); } }
void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsRubyBaseContainerFrame* aBaseContainer, nsReflowStatus& aStatus) { WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowInput.AvailableISize(), aReflowInput.AvailableBSize()); WritingMode rubyWM = GetWritingMode(); NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM), "Ruby frame writing-mode shouldn't be orthogonal to its line"); AutoRubyTextContainerArray textContainers(aBaseContainer); const uint32_t rtcCount = textContainers.Length(); ReflowOutput baseMetrics(aReflowInput); bool pushedFrame; aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus, &baseMetrics, pushedFrame); if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) { if (aBaseContainer != mFrames.FirstChild()) { // Some segments may have been reflowed before, hence it is not // a break-before for the ruby container. aStatus = NS_INLINE_LINE_BREAK_AFTER(NS_FRAME_NOT_COMPLETE); PushChildren(aBaseContainer, aBaseContainer->GetPrevSibling()); aReflowInput.mLineLayout->SetDirtyNextLine(); } // This base container is not placed at all, we can skip all // text containers paired with it. return; } if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // It always promise that if the status is incomplete, there is a // break occurs. Break before has been processed above. However, // it is possible that break after happens with the frame reflow // completed. It happens if there is a force break at the end. MOZ_ASSERT(NS_INLINE_IS_BREAK_AFTER(aStatus)); // Find the previous sibling which we will // insert new continuations after. nsIFrame* lastChild; if (rtcCount > 0) { lastChild = textContainers.LastElement(); } else { lastChild = aBaseContainer; } // Create continuations for the base container nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer); // newBaseContainer is null if there are existing next-in-flows. // We only need to move and push if there were not. if (newBaseContainer) { // Move the new frame after all the text containers mFrames.RemoveFrame(newBaseContainer); mFrames.InsertFrame(nullptr, lastChild, newBaseContainer); // Create continuations for text containers nsIFrame* newLastChild = newBaseContainer; for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]); MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist " "if the corresponding rbc does not"); mFrames.RemoveFrame(newTextContainer); mFrames.InsertFrame(nullptr, newLastChild, newTextContainer); newLastChild = newTextContainer; } } if (lastChild != mFrames.LastChild()) { // Always push the next frame after the last child in this segment. // It is possible that we pulled it back before our next-in-flow // drain our overflow. PushChildren(lastChild->GetNextSibling(), lastChild); aReflowInput.mLineLayout->SetDirtyNextLine(); } } else { // If the ruby base container is reflowed completely, the line // layout will remove the next-in-flows of that frame. But the // line layout is not aware of the ruby text containers, hence // it is necessary to remove them here. for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* nextRTC = textContainers[i]->GetNextInFlow(); if (nextRTC) { nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true); } } } nscoord segmentISize = baseMetrics.ISize(lineWM); const nsSize dummyContainerSize; LogicalRect baseRect = aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize); // We need to position our rtc frames on one side or the other of the // base container's rect, using a coordinate space that's relative to // the ruby frame. Right now, the base container's rect's block-axis // position is relative to the block container frame containing the // lines, so we use 0 instead. (i.e. we assume that the base container // is adjacent to the ruby frame's block-start edge.) // XXX We may need to add border/padding here. See bug 1055667. baseRect.BStart(lineWM) = 0; // The rect for offsets of text containers. LogicalRect offsetRect = baseRect; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; WritingMode rtcWM = textContainer->GetWritingMode(); nsReflowStatus textReflowStatus; ReflowOutput textMetrics(aReflowInput); ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer, availSize.ConvertTo(rtcWM, lineWM)); // FIXME We probably shouldn't be using the same nsLineLayout for // the text containers. But it should be fine now as we are // not actually using this line layout to reflow something, // but just read the writing mode from it. textReflowInput.mLineLayout = aReflowInput.mLineLayout; textContainer->Reflow(aPresContext, textMetrics, textReflowInput, textReflowStatus); // Ruby text containers always return NS_FRAME_COMPLETE even when // they have continuations, because the breaking has already been // handled when reflowing the base containers. NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE, "Ruby text container must not break itself inside"); // The metrics is initialized with reflow state of this ruby frame, // hence the writing-mode is tied to rubyWM instead of rtcWM. LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM); textContainer->SetSize(lineWM, size); nscoord reservedISize = RubyUtils::GetReservedISize(textContainer); segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize); uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition; MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER || rubyPosition == NS_STYLE_RUBY_POSITION_UNDER); Maybe<LogicalSide> side; if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver)); } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder)); } else { // XXX inter-character support in bug 1055672 MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position"); } LogicalPoint position(lineWM); if (side.isSome()) { if (side.value() == eLogicalSideBStart) { offsetRect.BStart(lineWM) -= size.BSize(lineWM); offsetRect.BSize(lineWM) += size.BSize(lineWM); position = offsetRect.Origin(lineWM); } else if (side.value() == eLogicalSideBEnd) { position = offsetRect.Origin(lineWM) + LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM)); offsetRect.BSize(lineWM) += size.BSize(lineWM); } else { MOZ_ASSERT_UNREACHABLE("???"); } } // Using a dummy container-size here, so child positioning may not be // correct. We will fix it in nsLineLayout after the whole line is // reflowed. FinishReflowChild(textContainer, aPresContext, textMetrics, &textReflowInput, lineWM, position, dummyContainerSize, 0); } MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM), "Annotations should only be placed on the block directions"); nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM); if (deltaISize <= 0) { RubyUtils::ClearReservedISize(aBaseContainer); } else { RubyUtils::SetReservedISize(aBaseContainer, deltaISize); aReflowInput.mLineLayout->AdvanceICoord(deltaISize); } // Set block leadings of the base container nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM); nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM); // XXX When bug 765861 gets fixed, this warning should be upgraded. NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0, "Leadings should be non-negative (because adding " "ruby annotation can only increase the size)"); mBStartLeading = std::max(mBStartLeading, startLeading); mBEndLeading = std::max(mBEndLeading, endLeading); }
bool nsColumnSetFrame::ReflowChildren(ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aCarriedOutBEndMargin, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); bool isRTL = !wm.IsBidiLTR(); bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceBSize > aConfig.mColMaxBSize; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n" " mBalanceColCount=%d, mColISize=%d, mColGap=%d\n", mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount, aConfig.mColISize, aConfig.mColGap); #endif DrainOverflowColumns(); const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize; if (colBSizeChanged) { mLastBalanceBSize = aConfig.mColMaxBSize; // XXX Seems like this could fire if incremental reflow pushed the column set // down so we reflow incrementally with a different available height. // We need a way to do an incremental reflow and be sure availableHeight // changes are taken account of! Right now I think block frames with absolute // children might exit early. //NS_ASSERTION(aKidReason != eReflowReason_Incremental, // "incremental reflow should not have changed the balance height"); } // get our border and padding LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding(); borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowInput)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); LogicalPoint childOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); // In vertical-rl mode, columns will not be correctly placed if the // reflowInput's ComputedWidth() is UNCONSTRAINED (in which case we'll get // a containerSize.width of zero here). In that case, the column positions // will be adjusted later, after our correct contentSize is known. nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained(); // For RTL, since the columns might not fill the frame exactly, we // need to account for the slop. Otherwise we'll waste time moving the // columns by some tiny amount // XXX when all of layout is converted to logical coordinates, we // probably won't need to do this hack any more. For now, we // confine it to the legacy horizontal-rl case if (!isVertical && isRTL) { nscoord availISize = aReflowInput.AvailableISize(); if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowInput.ComputedISize(); } if (availISize != NS_INTRINSICSIZE) { childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) - availISize; #ifdef DEBUG_roc printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm)); #endif } } int columnCount = 0; int contentBEnd = 0; bool reflowNext = false; while (child) { // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't // skip if the next column is dirty, because the next column's first line(s) // might be pullable back to this column. We can't skip if it's the last child // because we need to obtain the bottom margin. We can't skip // if this is the last column and we're supposed to assign unbounded // height to it, because that could change the available height from // the last time we reflowed it and we should try to pull all the // content from its next sibling. (Note that it might be the last // column, but not be the last child because the desired number of columns // has changed.) bool skipIncremental = !aReflowInput.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(child) && child->GetNextSibling() && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) && !NS_SUBTREE_DIRTY(child->GetNextSibling()); // If we need to pull up content from the prev-in-flow then this is not just // a height shrink. The prev in flow will have set the dirty bit. // Check the overflow rect YMost instead of just the child's content height. The child // may have overflowing content that cares about the available height boundary. // (It may also have overflowing content that doesn't care about the available height // boundary, but if so, too bad, this optimization is defeated.) // We want scrollable overflow here since this is a calculation that // affects layout. bool skipResizeBSizeShrink = false; if (shrinkingBSizeOnly) { switch (wm.GetBlockDir()) { case WritingMode::eBlockTB: if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockLR: if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockRL: // XXX not sure how to handle this, so for now just don't attempt // the optimization break; default: NS_NOTREACHED("unknown block direction"); break; } } nscoord childContentBEnd = 0; if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(child, childOrigin, wm, containerSize); // If this is the last frame then make sure we get the right status nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { aStatus = mLastFrameStatus; } childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); #ifdef DEBUG_roc printf("*** Skipping child #%d %p (incremental %d, resize block-size shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus); #endif } else { LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.BSize(wm) = GetAvailableContentBSize(aReflowInput); } LogicalSize computedSize = aReflowInput.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); ReflowInput kidReflowInput(PresContext(), aReflowInput, child, availSize, &kidCBSize); kidReflowInput.mFlags.mIsTopOfPage = true; kidReflowInput.mFlags.mTableIsSplittable = false; kidReflowInput.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; // We need to reflow any float placeholders, even if our column height // hasn't changed. kidReflowInput.mFlags.mMustReflowPlaceholders = !colBSizeChanged; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,availSize.BSize(wm)); #endif // Note if the column's next in flow is not being changed by this incremental reflow. // This may allow the current column to avoid trying to pull lines from the next column. if (child->GetNextSibling() && !(GetStateBits() & NS_FRAME_IS_DIRTY) && !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { kidReflowInput.mFlags.mNextInFlowUntouched = true; } ReflowOutput kidDesiredSize(wm, aDesiredSize.mFlags); // XXX it would be cool to consult the float manager for the // previous block to figure out the region of floats from the // previous column that extend into this column, and subtract // that region from the new float manager. So you could stick a // really big float in the first column and text in following // columns would flow around it. // Reflow the frame LogicalPoint origin(wm, childOrigin.I(wm) + kidReflowInput.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowInput.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowInput, wm, origin, containerSize, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, &kidReflowInput, wm, childOrigin, containerSize, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxBSize) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingBSize = std::max(childContentBEnd, aColData.mMaxOverflowingBSize); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBEnd = std::max(contentBEnd, childContentBEnd); aColData.mLastBSize = childContentBEnd; aColData.mSumBSize += childContentBEnd; // Build a continuation column if necessary nsIFrame* kidNextInFlow = child->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); child = nullptr; break; } else { ++columnCount; // Make sure that the column has a next-in-flow. If not, we must // create one to hold the overflowing stuff, even if we're just // going to put it on our overflow list and let *our* // next in flow handle it. if (!kidNextInFlow) { NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "We have to create a continuation, but the block doesn't want us to reflow it?"); // We need to create a continuing column kidNextInFlow = CreateNextInFlow(child); } // Make sure we reflow a next-in-flow when it switches between being // normal or overflow container if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } if ((contentBEnd > aReflowInput.ComputedMaxBSize() || contentBEnd > aReflowInput.ComputedBSize()) && aConfig.mBalanceColCount < INT32_MAX) { // We overflowed vertically, but have not exceeded the number of // columns. We're going to go into overflow columns now, so balancing // no longer applies. aColData.mHasExcessBSize = true; } if (columnCount >= aConfig.mBalanceColCount) { // No more columns allowed here. Stop. aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); // Move any of our leftover columns to our overflow list. Our // next-in-flow will eventually pick them up. const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); if (continuationColumns.NotEmpty()) { SetOverflowFrames(continuationColumns); } child = nullptr; break; } } if (PresContext()->HasPendingInterrupt()) { // Stop the loop now while |child| still points to the frame that bailed // out. We could keep going here and condition a bunch of the code in // this loop on whether there's an interrupt, or even just keep going and // trying to reflow the blocks (even though we know they'll interrupt // right after their first line), but stopping now is conceptually the // simplest (and probably fastest) thing. break; } // Advance to the next column child = child->GetNextSibling(); if (child) { childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap; #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm)); #endif } } if (PresContext()->CheckForInterrupt(this) && (GetStateBits() & NS_FRAME_IS_DIRTY)) { // Mark all our kids starting with |child| dirty // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, // because we might have interrupted while reflowing |child|, and since // we're about to add a dirty bit to |child| we need to make sure that // |this| is scheduled to have dirty bits marked on it and its ancestors. // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll // bail out immediately, since it'll already have a dirty bit. for (; child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } aColData.mMaxBSize = contentBEnd; LogicalSize contentSize = LogicalSize(wm, contentRect.Size()); contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd); mLastFrameStatus = aStatus; // Apply computed and min/max values if (aConfig.mComputedBSize != NS_INTRINSICSIZE) { if (aReflowInput.AvailableBSize() != NS_INTRINSICSIZE) { contentSize.BSize(wm) = std::min(contentSize.BSize(wm), aConfig.mComputedBSize); } else { contentSize.BSize(wm) = aConfig.mComputedBSize; } } else { // We add the "consumed" block-size back in so that we're applying // constraints to the correct bSize value, then subtract it again // after we've finished with the min/max calculation. This prevents us from // having a last continuation that is smaller than the min bSize. but which // has prev-in-flows, trigger a larger bSize than actually required. contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(contentSize.BSize(wm), aConfig.mConsumedBSize); } if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowInput.ComputedISize(); } else { contentSize.ISize(wm) = aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm)); } contentSize.ISize(wm) += borderPadding.IStartEnd(wm); contentSize.BSize(wm) += borderPadding.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); // In vertical-rl mode, make a second pass if necessary to reposition the // columns with the correct container width. (In other writing modes, // correct containerSize was not required for column positioning so we don't // need this fixup.) if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) { const nsSize finalContainerSize = aDesiredSize.PhysicalSize(); for (nsIFrame* child : mFrames) { // Get the logical position as set previously using a provisional or // dummy containerSize, and reset with the correct container size. child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize), finalContainerSize); } } #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); }
void nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, const nsRect& aContainingBlock, AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, nsOverflowAreas* aOverflowAreas) { #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent); printf("abs pos "); if (aKidFrame) { nsAutoString name; aKidFrame->GetFrameName(name); printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); } char width[16]; char height[16]; PrettyUC(aReflowState.AvailableWidth(), width, 16); PrettyUC(aReflowState.AvailableHeight(), height, 16); printf(" a=%s,%s ", width, height); PrettyUC(aReflowState.ComputedWidth(), width, 16); PrettyUC(aReflowState.ComputedHeight(), height, 16); printf("c=%s,%s \n", width, height); } AutoNoisyIndenter indent(nsBlockFrame::gNoisy); #endif // DEBUG WritingMode wm = aKidFrame->GetWritingMode(); LogicalSize logicalCBSize(wm, aContainingBlock.Size()); nscoord availISize = logicalCBSize.ISize(wm); if (availISize == -1) { NS_ASSERTION(aReflowState.ComputedSize(wm).ISize(wm) != NS_UNCONSTRAINEDSIZE, "Must have a useful inline-size _somewhere_"); availISize = aReflowState.ComputedSizeWithPadding(wm).ISize(wm); } uint32_t rsFlags = 0; if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) { // When a grid container generates the abs.pos. CB for a *child* then // the static-position is the CB origin (i.e. of the grid area rect). // https://drafts.csswg.org/css-grid/#static-position nsIFrame* placeholder = aPresContext->PresShell()->GetPlaceholderFrameFor(aKidFrame); if (placeholder && placeholder->GetParent() == aDelegatingFrame) { rsFlags |= nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN; } } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame, LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE), &logicalCBSize, rsFlags); // Get the border values WritingMode outerWM = aReflowState.GetWritingMode(); const LogicalMargin border(outerWM, aReflowState.mStyleBorder->GetComputedBorder()); const LogicalMargin margin = kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm); bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE) && (aFlags & AbsPosReflowFlags::eConstrainHeight) // Don't split if told not to (e.g. for fixed frames) && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame) //XXX we don't handle splitting frames for inline absolute containing blocks yet && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <= aReflowState.AvailableBSize()); // Don't split things below the fold. (Ideally we shouldn't *have* // anything totally below the fold, but we can't position frames // across next-in-flow breaks yet. if (constrainBSize) { kidReflowState.AvailableBSize() = aReflowState.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) - kidReflowState.ComputedLogicalMargin().BStart(wm); if (NS_AUTOOFFSET != kidReflowState.ComputedLogicalOffsets().BStart(wm)) { kidReflowState.AvailableBSize() -= kidReflowState.ComputedLogicalOffsets().BStart(wm); } } // Do the reflow nsHTMLReflowMetrics kidDesiredSize(kidReflowState); aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus); const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm); LogicalMargin offsets = kidReflowState.ComputedLogicalOffsets().ConvertTo(outerWM, wm); // If we're solving for start in either inline or block direction, // then compute it now that we know the dimensions. if ((NS_AUTOOFFSET == offsets.IStart(outerWM)) || (NS_AUTOOFFSET == offsets.BStart(outerWM))) { if (-1 == logicalCBSize.ISize(wm)) { // Get the containing block width/height logicalCBSize = kidReflowState.ComputeContainingBlockRectangle(aPresContext, &aReflowState); } if (NS_AUTOOFFSET == offsets.IStart(outerWM)) { NS_ASSERTION(NS_AUTOOFFSET != offsets.IEnd(outerWM), "Can't solve for both start and end"); offsets.IStart(outerWM) = logicalCBSize.ConvertTo(outerWM, wm).ISize(outerWM) - offsets.IEnd(outerWM) - margin.IStartEnd(outerWM) - kidSize.ISize(outerWM); } if (NS_AUTOOFFSET == offsets.BStart(outerWM)) { offsets.BStart(outerWM) = logicalCBSize.ConvertTo(outerWM, wm).BSize(outerWM) - offsets.BEnd(outerWM) - margin.BStartEnd(outerWM) - kidSize.BSize(outerWM); } kidReflowState.SetComputedLogicalOffsets(offsets.ConvertTo(wm, outerWM)); } // Position the child relative to our padding edge LogicalRect rect(outerWM, border.IStart(outerWM) + offsets.IStart(outerWM) + margin.IStart(outerWM), border.BStart(outerWM) + offsets.BStart(outerWM) + margin.BStart(outerWM), kidSize.ISize(outerWM), kidSize.BSize(outerWM)); nsRect r = rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) + border.Size(outerWM).GetPhysicalSize(outerWM)); // Offset the frame rect by the given origin of the absolute containing block. // If the frame is auto-positioned on both sides of an axis, it will be // positioned based on its containing block and we don't need to offset // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)). if (aContainingBlock.TopLeft() != nsPoint(0, 0)) { const nsStyleSides& offsets = kidReflowState.mStylePosition->mOffset; if (!(offsets.GetLeftUnit() == eStyleUnit_Auto && offsets.GetRightUnit() == eStyleUnit_Auto) || (rsFlags & nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN)) { r.x += aContainingBlock.x; } if (!(offsets.GetTopUnit() == eStyleUnit_Auto && offsets.GetBottomUnit() == eStyleUnit_Auto) || (rsFlags & nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN)) { r.y += aContainingBlock.y; } } aKidFrame->SetRect(r); nsView* view = aKidFrame->GetView(); if (view) { // Size and position the view and set its opacity, visibility, content // transparency, and clip nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, kidDesiredSize.VisualOverflow()); } else { nsContainerFrame::PositionChildViews(aKidFrame); } aKidFrame->DidReflow(aPresContext, &kidReflowState, nsDidReflowStatus::FINISHED); #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1); printf("abs pos "); if (aKidFrame) { nsAutoString name; aKidFrame->GetFrameName(name); printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); } printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame), r.x, r.y, r.width, r.height); } #endif if (aOverflowAreas) { aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft()); } }
void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE, "Should have a precomputed width!"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- bool reflowInner; bool reflowLegend; nsIFrame* legend = GetLegend(); nsIFrame* inner = GetInner(); if (aReflowState.ShouldReflowAllKids()) { reflowInner = inner != nullptr; reflowLegend = legend != nullptr; } else { reflowInner = inner && NS_SUBTREE_DIRTY(inner); reflowLegend = legend && NS_SUBTREE_DIRTY(legend); } // We don't allow fieldsets to break vertically. If we did, we'd // need logic here to push and pull overflow frames. // Since we're not applying our padding in this frame, we need to add it here // to compute the available width for our children. WritingMode innerWM = inner ? inner->GetWritingMode() : GetWritingMode(); WritingMode legendWM = legend ? legend->GetWritingMode() : GetWritingMode(); LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM); LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM); innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) = NS_UNCONSTRAINEDSIZE; NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, inner, nsLayoutUtils::MIN_ISIZE) <= innerAvailSize.ISize(innerWM), "Bogus availSize.ISize; should be bigger"); NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, legend, nsLayoutUtils::MIN_ISIZE) <= legendAvailSize.ISize(legendWM), "Bogus availSize.ISize; should be bigger"); // get our border and padding nsMargin border = aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding(); // Figure out how big the legend is if there is one. // get the legend's margin nsMargin legendMargin(0,0,0,0); // reflow the legend only if needed Maybe<nsHTMLReflowState> legendReflowState; if (legend) { legendReflowState.emplace(aPresContext, aReflowState, legend, legendAvailSize); } if (reflowLegend) { nsHTMLReflowMetrics legendDesiredSize(aReflowState); ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.Width(), legendDesiredSize.Height()); #endif // figure out the legend's rectangle legendMargin = legend->GetUsedMargin(); mLegendRect.width = legendDesiredSize.Width() + legendMargin.left + legendMargin.right; mLegendRect.height = legendDesiredSize.Height() + legendMargin.top + legendMargin.bottom; mLegendRect.x = 0; mLegendRect.y = 0; nscoord oldSpace = mLegendSpace; mLegendSpace = 0; if (mLegendRect.height > border.top) { // center the border on the legend mLegendSpace = mLegendRect.height - border.top; } else { mLegendRect.y = (border.top - mLegendRect.height)/2; } // if the legend space changes then we need to reflow the // content area as well. if (mLegendSpace != oldSpace && inner) { reflowInner = true; } FinishReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState.ptr(), 0, 0, NS_FRAME_NO_MOVE_FRAME); } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. legendMargin = legend->GetUsedMargin(); } // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding()); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our // height is unconstrained (in which case the child's will be too). if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) { kidReflowState.SetComputedHeight( std::max(0, aReflowState.ComputedHeight() - mLegendSpace)); } if (aReflowState.ComputedMinHeight() > 0) { kidReflowState.ComputedMinHeight() = std::max(0, aReflowState.ComputedMinHeight() - mLegendSpace); } if (aReflowState.ComputedMaxHeight() != NS_UNCONSTRAINEDSIZE) { kidReflowState.ComputedMaxHeight() = std::max(0, aReflowState.ComputedMaxHeight() - mLegendSpace); } nsHTMLReflowMetrics kidDesiredSize(kidReflowState, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); nsPoint pt(border.left, border.top + mLegendSpace); ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, pt.x, pt.y, 0, aStatus); FinishReflowChild(inner, aPresContext, kidDesiredSize, &kidReflowState, pt.x, pt.y, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } LogicalRect contentRect(innerWM); if (inner) { // We don't support margins on inner, so our content rect is just the // inner's border-box. contentRect = inner->GetLogicalRect(aReflowState.ComputedWidth()); } // Our content rect must fill up the available width if (innerAvailSize.ISize(innerWM) > contentRect.ISize(innerWM)) { contentRect.ISize(innerWM) = innerAvailSize.ISize(innerWM); } //XXX temporary! nsRect physicalContentRect = contentRect.GetPhysicalRect(innerWM, aReflowState.ComputedWidth()); if (legend) { // the legend is postioned horizontally within the inner's content rect // (so that padding on the fieldset affects the legend position). nsRect innerContentRect = physicalContentRect; innerContentRect.Deflate(aReflowState.ComputedPhysicalPadding()); // if the inner content rect is larger than the legend, we can align the legend if (innerContentRect.width > mLegendRect.width) { int32_t align = static_cast<nsLegendFrame*> (legend->GetContentInsertionFrame())->GetAlign(); switch (align) { case NS_STYLE_TEXT_ALIGN_RIGHT: mLegendRect.x = innerContentRect.XMost() - mLegendRect.width; break; case NS_STYLE_TEXT_ALIGN_CENTER: // Note: rounding removed; there doesn't seem to be any need mLegendRect.x = innerContentRect.width / 2 - mLegendRect.width / 2 + innerContentRect.x; break; default: mLegendRect.x = innerContentRect.x; break; } } else { // otherwise make place for the legend mLegendRect.x = innerContentRect.x; innerContentRect.width = mLegendRect.width; physicalContentRect.width = mLegendRect.width + aReflowState.ComputedPhysicalPadding().LeftRight(); } // place the legend nsRect actualLegendRect(mLegendRect); actualLegendRect.Deflate(legendMargin); nsPoint actualLegendPos(actualLegendRect.TopLeft()); legendReflowState->ApplyRelativePositioning(&actualLegendPos); legend->SetPosition(actualLegendPos); nsContainerFrame::PositionFrameView(legend); nsContainerFrame::PositionChildViews(legend); } // Return our size and our result. WritingMode wm = aReflowState.GetWritingMode(); nsSize finalSize(physicalContentRect.width + border.LeftRight(), mLegendSpace + border.TopBottom() + (inner ? inner->GetRect().height : 0)); aDesiredSize.SetSize(wm, LogicalSize(wm, finalSize)); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (legend) ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend); if (inner) ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner); // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); InvalidateFrame(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }