void nsRangeFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRangeFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!"); NS_ASSERTION(mProgressDiv, "::-moz-range-progress div must exist!"); NS_ASSERTION(mThumbDiv, "::-moz-range-thumb div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsRangeFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first."); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } WritingMode wm = aReflowState.GetWritingMode(); nscoord computedBSize = aReflowState.ComputedBSize(); if (computedBSize == NS_AUTOHEIGHT) { computedBSize = 0; } LogicalSize finalSize(wm, aReflowState.ComputedISize() + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm), computedBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); ReflowAnonymousContent(aPresContext, aDesiredSize, aReflowState); aDesiredSize.SetOverflowAreasToDesiredBounds(); nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); if (trackFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, trackFrame); } nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); if (rangeProgressFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rangeProgressFrame); } nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, thumbFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // make sure that the form registers itself on the initial/first reflow if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } // set values of reflow's out parameters WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm, aReflowState.ComputedISize() + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm), aReflowState.ComputedBSize() + aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); // computation of the ascent wrt the input height nscoord lineHeight = aReflowState.ComputedBSize(); float inflation = nsLayoutUtils::FontSizeInflationFor(this); if (!IsSingleLineTextControl()) { lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), NS_AUTOHEIGHT, inflation); } RefPtr<nsFontMetrics> fontMet; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), inflation); // now adjust for our borders and padding aDesiredSize.SetBlockStartAscent( nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight, wm.IsLineInverted()) + aReflowState.ComputedLogicalBorderPadding().BStart(wm)); // overflow handling aDesiredSize.SetOverflowAreasToDesiredBounds(); // perform reflow on all kids nsIFrame* kid = mFrames.FirstChild(); while (kid) { ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize); kid = kid->GetNextSibling(); } // take into account css properties that affect overflow handling FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
nscoord nsColumnSetFrame::GetAvailableContentBSize(const nsHTMLReflowState& aReflowState) { if (aReflowState.AvailableBSize() == NS_INTRINSICSIZE) { return NS_INTRINSICSIZE; } WritingMode wm = aReflowState.GetWritingMode(); LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); bp.BEnd(wm) = aReflowState.ComputedLogicalBorderPadding().BEnd(wm); return std::max(0, aReflowState.AvailableBSize() - bp.BStartEnd(wm)); }
void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) { return; } #ifdef DEBUG SanityCheckAnonymousGridItems(); #endif // DEBUG LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides()); nscoord contentBSize = GetEffectiveComputedBSize(aReflowState); if (contentBSize == NS_AUTOHEIGHT) { contentBSize = 0; } WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm, aReflowState.ComputedISize() + bp.IStartEnd(wm), contentBSize + bp.BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) { return; } #ifdef DEBUG SanityCheckAnonymousGridItems(); #endif // DEBUG LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides()); const nsStylePosition* stylePos = aReflowState.mStylePosition; InitImplicitNamedAreas(stylePos); GridItemCSSOrderIterator normalFlowIter(this, kPrincipalList); mIsNormalFlowInCSSOrder = normalFlowIter.ItemsAreAlreadyInOrder(); PlaceGridItems(normalFlowIter, stylePos); nsAutoTArray<TrackSize, 32> colSizes; nsAutoTArray<TrackSize, 32> rowSizes; WritingMode wm = aReflowState.GetWritingMode(); const nscoord computedBSize = aReflowState.ComputedBSize(); const nscoord computedISize = aReflowState.ComputedISize(); LogicalSize percentageBasis(wm, computedISize, computedBSize == NS_AUTOHEIGHT ? 0 : computedBSize); CalculateTrackSizes(percentageBasis, stylePos, colSizes, rowSizes); nscoord bSize = 0; if (computedBSize == NS_AUTOHEIGHT) { for (uint32_t i = 0; i < mGridRowEnd - 1; ++i) { bSize += rowSizes[i].mBase; } } else { bSize = computedBSize; } bSize = std::max(bSize - GetConsumedBSize(), 0); LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm), bSize + bp.BStartEnd(wm)); aDesiredSize.SetSize(wm, desiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize, bSize); normalFlowIter.Reset(); ReflowChildren(normalFlowIter, contentArea, colSizes, rowSizes, aDesiredSize, aReflowState, aStatus); FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
static nscoord GetAvailableContentISize(const nsHTMLReflowState& aReflowState) { if (aReflowState.AvailableISize() == NS_INTRINSICSIZE) { return NS_INTRINSICSIZE; } WritingMode wm = aReflowState.GetWritingMode(); nscoord borderPaddingISize = aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm); return std::max(0, aReflowState.AvailableISize() - borderPaddingISize); }
/* virtual */ void nsRubyTextFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyBaseFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (!aReflowState.mLineLayout) { NS_ASSERTION(aReflowState.mLineLayout, "No line layout provided to RubyTextFrame reflow method."); aStatus = NS_FRAME_COMPLETE; return; } WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); WritingMode frameWM = aReflowState.GetWritingMode(); LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); aStatus = NS_FRAME_COMPLETE; LogicalSize availSize(lineWM, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); // Begin the span for the ruby text frame nscoord availableISize = aReflowState.AvailableISize(); NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off inline axis border+padding from availableISize availableISize -= borderPadding.IStartEnd(frameWM); aReflowState.mLineLayout->BeginSpan(this, &aReflowState, borderPadding.IStart(frameWM), availableISize, &mBaseline); for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsReflowStatus frameReflowStatus; nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags); bool pushedFrame; aReflowState.mLineLayout->ReflowFrame(e.get(), frameReflowStatus, &metrics, pushedFrame); NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented"); e.get()->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM), metrics.BSize(lineWM))); } aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this); nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, borderPadding, lineWM, frameWM); }
/* virtual */ void nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // All rt children have already been reflowed. All we need to do is clean up // the line layout. aStatus = NS_FRAME_COMPLETE; mozilla::WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); mozilla::WritingMode frameWM = aReflowState.GetWritingMode(); mozilla::LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); aDesiredSize.ISize(lineWM) = mISize; nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, borderPadding, lineWM, frameWM); nscoord bsize = aDesiredSize.BSize(lineWM); if (!mLines.empty()) { // Okay to use BlockStartAscent because it has just been correctly set by // nsLayoutUtils::SetBSizeFromFontMetrics. mLines.begin()->SetLogicalAscent(aDesiredSize.BlockStartAscent()); mLines.begin()->SetBounds(aReflowState.GetWritingMode(), 0, 0, mISize, bsize, mISize); } if (mLineLayout) { mLineLayout->EndLineReflow(); mLineLayout = nullptr; } }
/* virtual */ void nsRubyFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (!aReflowState.mLineLayout) { NS_ASSERTION(aReflowState.mLineLayout, "No line layout provided to RubyFrame reflow method."); aStatus = NS_FRAME_COMPLETE; return; } // Grab overflow frames from prev-in-flow and its own. MoveOverflowToChildList(); // Clear leadings mBStartLeading = mBEndLeading = 0; // Begin the span for the ruby frame WritingMode frameWM = aReflowState.GetWritingMode(); WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); nscoord startEdge = 0; const bool boxDecorationBreakClone = StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE; if (boxDecorationBreakClone || !GetPrevContinuation()) { startEdge = borderPadding.IStart(frameWM); } NS_ASSERTION(aReflowState.AvailableISize() != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); nscoord availableISize = aReflowState.AvailableISize(); availableISize -= startEdge + borderPadding.IEnd(frameWM); aReflowState.mLineLayout->BeginSpan(this, &aReflowState, startEdge, availableISize, &mBaseline); aStatus = NS_FRAME_COMPLETE; for (RubySegmentEnumerator e(this); !e.AtEnd(); e.Next()) { ReflowSegment(aPresContext, aReflowState, e.GetBaseContainer(), aStatus); if (NS_INLINE_IS_BREAK(aStatus)) { // A break occurs when reflowing the segment. // Don't continue reflowing more segments. break; } } ContinuationTraversingState pullState(this); while (aStatus == NS_FRAME_COMPLETE) { nsRubyBaseContainerFrame* baseContainer = PullOneSegment(aReflowState.mLineLayout, pullState); if (!baseContainer) { // No more continuations after, finish now. break; } ReflowSegment(aPresContext, aReflowState, baseContainer, aStatus); } // We never handle overflow in ruby. MOZ_ASSERT(!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)); aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this); if (boxDecorationBreakClone || !GetPrevContinuation()) { aDesiredSize.ISize(lineWM) += borderPadding.IStart(frameWM); } if (boxDecorationBreakClone || NS_FRAME_IS_COMPLETE(aStatus)) { aDesiredSize.ISize(lineWM) += borderPadding.IEnd(frameWM); } nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, borderPadding, lineWM, frameWM); }
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 nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); WritingMode frameWM = aReflowState.GetWritingMode(); WritingMode lineWM = aReflowState.mLineLayout->mRootSpan->mWritingMode; LogicalMargin framePadding = aReflowState.ComputedLogicalBorderPadding(); nscoord startEdge = 0; const bool boxDecorationBreakClone = MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE); // Don't offset by our start borderpadding if we have a prev continuation or // if we're in a part of an {ib} split other than the first one. For // box-decoration-break:clone we always offset our start since all // continuations have border/padding. if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) || boxDecorationBreakClone) { startEdge = framePadding.IStart(frameWM); } nscoord availableISize = aReflowState.AvailableISize(); NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off inline axis border+padding from availableISize availableISize -= startEdge; availableISize -= framePadding.IEnd(frameWM); lineLayout->BeginSpan(this, &aReflowState, startEdge, startEdge + availableISize, &mBaseline); // First reflow our principal children. nsIFrame* frame = mFrames.FirstChild(); bool done = false; while (frame) { // Check if we should lazily set the child frame's parent pointer. if (irs.mSetParentPointer) { bool havePrevBlock = irs.mLineContainer && irs.mLineContainer->GetPrevContinuation(); nsIFrame* child = frame; do { // If our block is the first in flow, then any floats under the pulled // frame must already belong to our block. if (havePrevBlock) { // This has to happen before we update frame's parent; we need to // know frame's ancestry under its old block. // The blockChildren.ContainsFrame check performed by // ReparentFloatsForInlineChild here may be slow, but we can't // easily avoid it because we don't know where 'frame' originally // came from. If we really really have to optimize this we could // cache whether frame->GetParent() is under its containing blocks // overflowList or not. ReparentFloatsForInlineChild(irs.mLineContainer, child, false); } child->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(child); nsLayoutUtils::MarkDescendantsDirty(child); } // We also need to do the same for |frame|'s next-in-flows that are in // the sibling list. Otherwise, if we reflow |frame| and it's complete // we'll crash when trying to delete its next-in-flow. // This scenario doesn't happen often, but it can happen. nsIFrame* nextSibling = child->GetNextSibling(); child = child->GetNextInFlow(); if (MOZ_UNLIKELY(child)) { while (child != nextSibling && nextSibling) { nextSibling = nextSibling->GetNextSibling(); } if (!nextSibling) { child = nullptr; } } MOZ_ASSERT(!child || mFrames.ContainsFrame(child)); } while (child); // Fix the parent pointer for ::first-letter child frame next-in-flows, // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042). nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame); if (realFrame->GetType() == nsGkAtoms::letterFrame) { nsIFrame* child = realFrame->GetFirstPrincipalChild(); if (child) { NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); if (mFrames.ContainsFrame(nextInFlow)) { nextInFlow->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(nextInFlow); nsLayoutUtils::MarkDescendantsDirty(nextInFlow); } } else { #ifdef DEBUG // Once we find a next-in-flow that isn't ours none of the // remaining next-in-flows should be either. for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow), "unexpected letter frame flow"); } #endif break; } } } } } MOZ_ASSERT(frame->GetParent() == this); if (!done) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); done = NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus)); if (done) { if (!irs.mSetParentPointer) { break; } // Keep reparenting the remaining siblings, but don't reflow them. nsFrameList* pushedFrames = GetOverflowFrames(); if (pushedFrames && pushedFrames->FirstChild() == frame) { // Don't bother if |frame| was pushed to our overflow list. break; } } else { irs.mPrevFrame = frame; } } frame = frame->GetNextSibling(); } // Attempt to pull frames from our next-in-flow until we can't if (!done && GetNextInFlow()) { while (true) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); bool isComplete; if (!frame) { // Could be non-null if we pulled a first-letter frame and // it created a continuation, since we don't push those. frame = PullOneFrame(aPresContext, irs, &isComplete); } #ifdef NOISY_PUSHING printf("%p pulled up %p\n", this, frame); #endif if (nullptr == frame) { if (!isComplete) { aStatus = NS_FRAME_NOT_COMPLETE; } break; } ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) || !GetOverflowFrames(), "We can't be complete AND have overflow frames!"); // If after reflowing our children they take up no area then make // sure that we don't either. // // Note: CSS demands that empty inline elements still affect the // line-height calculations. However, continuations of an inline // that are empty we force to empty so that things like collapsed // whitespace in an inline element don't affect the line-height. aMetrics.ISize(lineWM) = lineLayout->EndSpan(this); // Compute final width. // XXX Note that that the padding start and end are in the frame's // writing mode, but the metrics' inline-size is in the line's // writing mode. This makes sense if the line and frame are both // vertical or both horizontal, but what should happen with // orthogonal inlines? // Make sure to not include our start border and padding if we have a prev // continuation or if we're in a part of an {ib} split other than the first // one. For box-decoration-break:clone we always include our start border // and padding since all continuations have them. if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) || boxDecorationBreakClone) { aMetrics.ISize(lineWM) += framePadding.IStart(frameWM); } /* * We want to only apply the end border and padding if we're the last * continuation and either not in an {ib} split or the last part of it. To * be the last continuation we have to be complete (so that we won't get a * next-in-flow) and have no non-fluid continuations on our continuation * chain. For box-decoration-break:clone we always apply the end border and * padding since all continuations have them. */ if ((NS_FRAME_IS_COMPLETE(aStatus) && !LastInFlow()->GetNextContinuation() && !FrameIsNonLastInIBSplit()) || boxDecorationBreakClone) { aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM); } nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, aReflowState, framePadding, lineWM, frameWM); // For now our overflow area is zero. The real value will be // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowAreas.Clear(); #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": metrics=%d,%d ascent=%d\n", aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent()); #endif }
bool nsBlockReflowContext::ComputeCollapsedBStartMargin(const nsHTMLReflowState& aRS, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, bool* aMayNeedRetry, bool* aBlockIsEmpty) { WritingMode wm = aRS.GetWritingMode(); WritingMode parentWM = mMetrics.GetWritingMode(); // Include block-start element of frame's margin aMargin->Include(aRS.ComputedLogicalMargin().ConvertTo(parentWM, wm).BStart(parentWM)); // The inclusion of the block-end margin when empty is done by the caller // since it doesn't need to be done by the top-level (non-recursive) // caller. #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": %d => %d\n", aRS.ComputedLogicalMargin().BStart(wm), aMargin->get()); #endif bool dirtiedLine = false; bool setBlockIsEmpty = false; // Calculate the frame's generational block-start-margin from its child // blocks. Note that if the frame has a non-zero block-start-border or // block-start-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame); nsPresContext* prescontext = frame->PresContext(); nsBlockFrame* block = nullptr; if (0 == aRS.ComputedLogicalBorderPadding().BStart(wm)) { block = nsLayoutUtils::GetAsBlock(frame); if (block) { bool bStartMarginRoot, unused; block->IsMarginRoot(&bStartMarginRoot, &unused); if (bStartMarginRoot) { block = nullptr; } } } // iterate not just through the lines of 'block' but also its // overflow lines and the normal and overflow lines of its next in // flows. Note that this will traverse some frames more than once: // for example, if A contains B and A->nextinflow contains // B->nextinflow, we'll traverse B->nextinflow twice. But this is // OK because our traversal is idempotent. for ( ;block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) { for (int overflowLines = 0; overflowLines <= 1; ++overflowLines) { nsBlockFrame::line_iterator line; nsBlockFrame::line_iterator line_end; bool anyLines = true; if (overflowLines) { nsBlockFrame::FrameLines* frames = block->GetOverflowLines(); nsLineList* lines = frames ? &frames->mLines : nullptr; if (!lines) { anyLines = false; } else { line = lines->begin(); line_end = lines->end(); } } else { line = block->begin_lines(); line_end = block->end_lines(); } for (; anyLines && line != line_end; ++line) { if (!aClearanceFrame && line->HasClearance()) { // If we don't have a clearance frame, then we're computing // the collapsed margin in the first pass, assuming that all // lines have no clearance. So clear their clearance flags. line->ClearHasClearance(); line->MarkDirty(); dirtiedLine = true; } bool isEmpty; if (line->IsInline()) { isEmpty = line->IsEmpty(); } else { nsIFrame* kid = line->mFirstChild; if (kid == aClearanceFrame) { line->SetHasClearance(); line->MarkDirty(); dirtiedLine = true; goto done; } // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow // state for it. // We may have to construct an extra reflow state here if // we drilled down through a block wrapper. At the moment // we can only drill down one level so we only have to support // one extra reflow state. const nsHTMLReflowState* outerReflowState = &aRS; if (frame != aRS.frame) { NS_ASSERTION(frame->GetParent() == aRS.frame, "Can only drill through one level of block wrapper"); LogicalSize availSpace = aRS.ComputedSize(frame->GetWritingMode()); outerReflowState = new nsHTMLReflowState(prescontext, aRS, frame, availSpace); } { LogicalSize availSpace = outerReflowState->ComputedSize(kid->GetWritingMode()); nsHTMLReflowState innerReflowState(prescontext, *outerReflowState, kid, availSpace); // Record that we're being optimistic by assuming the kid // has no clearance if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { *aMayNeedRetry = true; } if (ComputeCollapsedBStartMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) { line->MarkDirty(); dirtiedLine = true; } if (isEmpty) { WritingMode innerWM = innerReflowState.GetWritingMode(); LogicalMargin innerMargin = innerReflowState.ComputedLogicalMargin().ConvertTo(parentWM, innerWM); aMargin->Include(innerMargin.BEnd(parentWM)); } } if (outerReflowState != &aRS) { delete const_cast<nsHTMLReflowState*>(outerReflowState); } } if (!isEmpty) { if (!setBlockIsEmpty && aBlockIsEmpty) { setBlockIsEmpty = true; *aBlockIsEmpty = false; } goto done; } } if (!setBlockIsEmpty && aBlockIsEmpty) { // The first time we reach here is when this is the first block // and we have processed all its normal lines. setBlockIsEmpty = true; // All lines are empty, or we wouldn't be here! *aBlockIsEmpty = aRS.frame->IsSelfEmpty(); } } } done: if (!setBlockIsEmpty && aBlockIsEmpty) { *aBlockIsEmpty = aRS.frame->IsEmpty(); } #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": => %d\n", aMargin->get()); #endif return dirtiedLine; }
void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus); // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child WritingMode wm = aReflowState.GetWritingMode(); LogicalSize availSize = aReflowState.AvailableSize(); const LogicalMargin& bp = aReflowState.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(); nsHTMLReflowMetrics kidMetrics(lineWM); // Reflow the child if (!aReflowState.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 = GetWritingMode(kid); LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm); nsHTMLReflowState rs(aPresContext, aReflowState, kid, kidAvailSize); nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr, nullptr); ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm), availSize.ISize(wm), NS_UNCONSTRAINEDSIZE, false, true, kidWritingMode, nsSize(aReflowState.AvailableWidth(), aReflowState.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 = aReflowState.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowState, 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); 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 (aReflowState.mLineLayout) { aReflowState.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, aReflowState, aMetrics); }
void nsNumberControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsNumberControlFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first"); NS_ASSERTION(!mFrames.FirstChild() || !mFrames.FirstChild()->GetNextSibling(), "We expect at most one direct child frame"); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } const WritingMode myWM = aReflowState.GetWritingMode(); // The ISize of our content box, which is the available ISize // for our anonymous content: const nscoord contentBoxISize = aReflowState.ComputedISize(); nscoord contentBoxBSize = aReflowState.ComputedBSize(); // Figure out our border-box sizes as well (by adding borderPadding to // content-box sizes): const nscoord borderBoxISize = contentBoxISize + aReflowState.ComputedLogicalBorderPadding().IStartEnd(myWM); nscoord borderBoxBSize; if (contentBoxBSize != NS_INTRINSICSIZE) { borderBoxBSize = contentBoxBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize. nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? if (contentBoxBSize == NS_INTRINSICSIZE) { contentBoxBSize = 0; borderBoxBSize = aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); nsHTMLReflowMetrics wrappersDesiredSize(aReflowState); WritingMode wrapperWM = outerWrapperFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wrapperWM); availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, outerWrapperFrame, availSize); // Convert wrapper margin into my own writing-mode (in case it differs): LogicalMargin wrapperMargin = wrapperReflowState.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM); // offsets of wrapper frame within this frame: LogicalPoint wrapperOffset(myWM, aReflowState.ComputedLogicalBorderPadding().IStart(myWM) + wrapperMargin.IStart(myWM), aReflowState.ComputedLogicalBorderPadding().BStart(myWM) + wrapperMargin.BStart(myWM)); nsReflowStatus childStatus; // We initially reflow the child with a dummy containerSize; positioning // will be fixed later. const nsSize dummyContainerSize; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, wrapperReflowState, myWM, wrapperOffset, dummyContainerSize, 0, childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained available block-size, " "so it should be complete"); nscoord wrappersMarginBoxBSize = wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM); if (contentBoxBSize == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's // block-size: contentBoxBSize = wrappersMarginBoxBSize; // 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 // aReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since ComputedMaxBSize and // ComputedMinBSize are content heights. contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize, aReflowState.ComputedMinBSize(), aReflowState.ComputedMaxBSize()); borderBoxBSize = contentBoxBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } // Center child in block axis nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize; wrapperOffset.B(myWM) += std::max(0, extraSpace / 2); // Needed in FinishReflowChild, for logical-to-physical conversion: nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize). GetPhysicalSize(myWM); // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowState, myWM, wrapperOffset, borderBoxSize, 0); nsSize contentBoxSize = LogicalSize(myWM, contentBoxISize, contentBoxBSize). GetPhysicalSize(myWM); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowState.GetWritingMode(), contentBoxSize)); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aDesiredSize.SetSize(myWM, logicalDesiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
/* virtual */ void nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (!aReflowState.mLineLayout) { NS_ASSERTION( aReflowState.mLineLayout, "No line layout provided to RubyBaseContainerFrame reflow method."); aStatus = NS_FRAME_COMPLETE; return; } aStatus = NS_FRAME_COMPLETE; nscoord isize = 0; int baseNum = 0; nscoord leftoverSpace = 0; nscoord spaceApart = 0; WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); WritingMode frameWM = aReflowState.GetWritingMode(); LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); nscoord baseStart = 0; LogicalSize availSize(lineWM, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); // Begin the line layout for each ruby text container in advance. for (uint32_t i = 0; i < mTextContainers.Length(); i++) { nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i); nsHTMLReflowState rtcReflowState(aPresContext, *aReflowState.parentReflowState, rtcFrame, availSize); rtcReflowState.mLineLayout = aReflowState.mLineLayout; // FIXME: Avoid using/needing the rtcReflowState argument rtcFrame->BeginRTCLineLayout(aPresContext, rtcReflowState); } for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsIFrame* rbFrame = e.get(); if (rbFrame->GetType() != nsGkAtoms::rubyBaseFrame) { NS_ASSERTION(false, "Unrecognized child type for ruby base container"); continue; } nsReflowStatus frameReflowStatus; nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags); // Determine if we need more spacing between bases in the inline direction // depending on the inline size of the corresponding annotations // FIXME: The use of GetPrefISize here and below is easier but not ideal. It // would be better to use metrics from reflow. nscoord prefWidth = rbFrame->GetPrefISize(aReflowState.rendContext); nscoord textWidth = 0; for (uint32_t i = 0; i < mTextContainers.Length(); i++) { nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)-> PrincipalChildList().FrameAt(baseNum)); if (rtFrame) { int newWidth = rtFrame->GetPrefISize(aReflowState.rendContext); if (newWidth > textWidth) { textWidth = newWidth; } } } if (textWidth > prefWidth) { spaceApart = std::max((textWidth - prefWidth) / 2, spaceApart); leftoverSpace = spaceApart; } else { spaceApart = leftoverSpace; leftoverSpace = 0; } if (spaceApart > 0) { aReflowState.mLineLayout->AdvanceICoord(spaceApart); } baseStart = aReflowState.mLineLayout->GetCurrentICoord(); bool pushedFrame; aReflowState.mLineLayout->ReflowFrame(rbFrame, frameReflowStatus, &metrics, pushedFrame); NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented"); isize += metrics.ISize(lineWM); rbFrame->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM), metrics.BSize(lineWM))); FinishReflowChild(rbFrame, aPresContext, metrics, &aReflowState, 0, 0, NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW); // Now reflow the ruby text boxes that correspond to this ruby base box. for (uint32_t i = 0; i < mTextContainers.Length(); i++) { nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)-> PrincipalChildList().FrameAt(baseNum)); nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i); if (rtFrame) { nsHTMLReflowMetrics rtcMetrics(*aReflowState.parentReflowState, aDesiredSize.mFlags); nsHTMLReflowState rtcReflowState(aPresContext, *aReflowState.parentReflowState, rtcFrame, availSize); rtcReflowState.mLineLayout = rtcFrame->GetLineLayout(); rtcFrame->ReflowRubyTextFrame(rtFrame, rbFrame, baseStart, aPresContext, rtcMetrics, rtcReflowState); } } baseNum++; } // Reflow ruby annotations which do not have a corresponding ruby base box due // to a ruby base shortage. According to the spec, an empty ruby base is // assumed to exist for each of these annotations. bool continueReflow = true; while (continueReflow) { continueReflow = false; for (uint32_t i = 0; i < mTextContainers.Length(); i++) { nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)-> PrincipalChildList().FrameAt(baseNum)); nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i); if (rtFrame) { continueReflow = true; nsHTMLReflowMetrics rtcMetrics(*aReflowState.parentReflowState, aDesiredSize.mFlags); nsHTMLReflowState rtcReflowState(aPresContext, *aReflowState.parentReflowState, rtcFrame, availSize); rtcReflowState.mLineLayout = rtcFrame->GetLineLayout(); rtcFrame->ReflowRubyTextFrame(rtFrame, nullptr, baseStart, aPresContext, rtcMetrics, rtcReflowState); // Update the inline coord to make space for subsequent ruby annotations // (since there is no corresponding base inline size to use). baseStart += rtcMetrics.ISize(lineWM); } } baseNum++; } aDesiredSize.ISize(lineWM) = isize; nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, borderPadding, lineWM, frameWM); }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, 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 = aReflowState.ComputedLogicalBorderPadding(); borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); 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 // reflowState'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 = aReflowState.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 = aReflowState.AvailableISize(); if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowState.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 = !aReflowState.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(aReflowState); } LogicalSize computedSize = aReflowState.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, &kidCBSize); kidReflowState.mFlags.mIsTopOfPage = true; kidReflowState.mFlags.mTableIsSplittable = false; kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; // We need to reflow any float placeholders, even if our column height // hasn't changed. kidReflowState.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)) { kidReflowState.mFlags.mNextInFlowUntouched = true; } nsHTMLReflowMetrics 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) + kidReflowState.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowState.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, 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, &kidReflowState, 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 > aReflowState.ComputedMaxBSize() || contentBEnd > aReflowState.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 (aReflowState.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) = aReflowState.ApplyMinMaxBSize(contentSize.BSize(wm), aConfig.mConsumedBSize); } if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowState.ComputedISize(); } else { contentSize.ISize(wm) = aReflowState.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 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) { 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(); }