void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // printf("OuterFrame::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); aStatus = NS_FRAME_COMPLETE; NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen"); // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed> nsLeafFrame::DoReflow(aPresContext, aDesiredSize, aReflowState, aStatus); // "offset" is the offset of our content area from our frame's // top-left corner. nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left, aReflowState.ComputedPhysicalBorderPadding().top); nsSize innerSize(aDesiredSize.Width(), aDesiredSize.Height()); innerSize.width -= aReflowState.ComputedPhysicalBorderPadding().LeftRight(); innerSize.height -= aReflowState.ComputedPhysicalBorderPadding().TopBottom(); if (mInnerView) { nsViewManager* vm = mInnerView->GetViewManager(); vm->MoveViewTo(mInnerView, offset.x, offset.y); vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true); } aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!ShouldClipSubdocument()) { nsIFrame* subdocRootFrame = GetSubdocumentRootFrame(); if (subdocRootFrame) { aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset); } } FinishAndStoreOverflow(&aDesiredSize); if (!aPresContext->IsPaginated() && !mPostedReflowCallback) { PresContext()->PresShell()->PostReflowCallback(this); mPostedReflowCallback = true; } // printf("OuterFrame::Reflow DONE %X (%d,%d)\n", this, // aDesiredSize.Width(), aDesiredSize.Height()); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x", aDesiredSize.Width(), aDesiredSize.Height(), aStatus)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
// XXX how should border&padding effect baseline alignment? // => descent = borderPadding.bottom for example void nsLeafFrame::AddBordersAndPadding(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aMetrics) { aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom(); }
void nsRangeFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { 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); } nscoord computedHeight = aReflowState.ComputedHeight(); if (computedHeight == NS_AUTOHEIGHT) { computedHeight = 0; } aDesiredSize.Width() = aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = computedHeight + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 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); }
nscoord nsColumnSetFrame::GetAvailableContentHeight(const nsHTMLReflowState& aReflowState) { if (aReflowState.AvailableHeight() == NS_INTRINSICSIZE) { return NS_INTRINSICSIZE; } nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); bp.ApplySkipSides(GetSkipSides(&aReflowState)); bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom; return std::max(0, aReflowState.AvailableHeight() - bp.TopBottom()); }
NS_IMETHODIMP nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { 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 aDesiredSize.Width() = aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = aReflowState.ComputedHeight() + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); // computation of the ascent wrt the input height nscoord lineHeight = aReflowState.ComputedHeight(); float inflation = nsLayoutUtils::FontSizeInflationFor(this); if (!IsSingleLineTextControl()) { lineHeight = nsHTMLReflowState::CalcLineHeight(StyleContext(), NS_AUTOHEIGHT, inflation); } nsRefPtr<nsFontMetrics> fontMet; nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), inflation); NS_ENSURE_SUCCESS(rv, rv); // now adjust for our borders and padding aDesiredSize.SetTopAscent( nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight) + aReflowState.ComputedPhysicalBorderPadding().top); // 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); return NS_OK; }
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; } nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); ApplySkipSides(bp); nscoord contentHeight = GetEffectiveComputedHeight(aReflowState); if (contentHeight == NS_AUTOHEIGHT) { contentHeight = 0; } aDesiredSize.Width() = aReflowState.ComputedWidth() + bp.LeftRight(); aDesiredSize.Height() = contentHeight + bp.TopBottom(); aDesiredSize.SetOverflowAreasToDesiredBounds(); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, nsHTMLReflowMetrics& aParentDesiredSize) { // compute available size and frame offsets for child nsSize availSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); availSize.width = std::max(availSize.width, 0); availSize.height = std::max(availSize.height, 0); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKid, availSize); // Set computed width and computed height for the child nscoord width = availSize.width; width -= kidReflowState.ComputedPhysicalMargin().LeftRight() + kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); width = std::max(width, 0); kidReflowState.SetComputedWidth(width); nscoord height = availSize.height; height -= kidReflowState.ComputedPhysicalMargin().TopBottom() + kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); height = std::max(height, 0); kidReflowState.SetComputedHeight(height); // compute the offsets nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left + kidReflowState.ComputedPhysicalMargin().left; nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top + kidReflowState.ComputedPhysicalMargin().top; // reflow the child nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode()); ReflowChild(aKid, aPresContext, desiredSize, kidReflowState, xOffset, yOffset, 0, aStatus); // place the child FinishReflowChild(aKid, aPresContext, &kidReflowState, desiredSize, xOffset, yOffset, 0); // consider the overflow aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas); }
void nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { bool vertical = ResolvedOrientationIsVertical(); WritingMode wm = aBarFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, availSize); nscoord size = vertical ? aReflowState.ComputedHeight() : aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; // NOTE: Introduce a new function getPosition in the content part ? HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(mContent); double max = meterElement->Max(); double min = meterElement->Min(); double value = meterElement->Value(); double position = max - min; position = position != 0 ? (value - min) / position : 1; size = NSToCoordRound(size * position); if (!vertical && (wm.IsVertical() ? wm.IsVerticalRL() : !wm.IsBidiLTR())) { xoffset += aReflowState.ComputedWidth() - size; } // The bar position is *always* constrained. if (vertical) { // We want the bar to begin at the bottom. yoffset += aReflowState.ComputedHeight() - size; size -= reflowState.ComputedPhysicalMargin().TopBottom() + reflowState.ComputedPhysicalBorderPadding().TopBottom(); size = std::max(size, 0); reflowState.SetComputedHeight(size); } else { size -= reflowState.ComputedPhysicalMargin().LeftRight() + reflowState.ComputedPhysicalBorderPadding().LeftRight(); size = std::max(size, 0); reflowState.SetComputedWidth(size); } xoffset += reflowState.ComputedPhysicalMargin().left; yoffset += reflowState.ComputedPhysicalMargin().top; nsHTMLReflowMetrics barDesiredSize(reflowState); ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, xoffset, yoffset, 0); }
static nscoord GetAvailableContentWidth(const nsHTMLReflowState& aReflowState) { if (aReflowState.AvailableWidth() == NS_INTRINSICSIZE) { return NS_INTRINSICSIZE; } nscoord borderPaddingWidth = aReflowState.ComputedPhysicalBorderPadding().left + aReflowState.ComputedPhysicalBorderPadding().right; return std::max(0, aReflowState.AvailableWidth() - borderPaddingWidth); }
void nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, nsHTMLReflowMetrics& aParentDesiredSize) { // compute available size and frame offsets for child WritingMode wm = aKid->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKid, availSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override padding with our computed padding in case we got it from theming or percentage kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Set computed width and computed height for the child kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); // Offset the frame by the size of the parent's border nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left - aReflowState.ComputedPhysicalPadding().left; nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top - aReflowState.ComputedPhysicalPadding().top; // reflow the child nsHTMLReflowMetrics desiredSize(aReflowState); ReflowChild(aKid, aPresContext, desiredSize, kidReflowState, xOffset, yOffset, 0, aStatus); // place the child FinishReflowChild(aKid, aPresContext, desiredSize, &kidReflowState, xOffset, yOffset, 0); // consider the overflow aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas); }
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(); }
nsresult nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { nsresult rv = NS_OK; aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); WritingMode wm = aReflowState.GetWritingMode(); nscoord startEdge = 0; // 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. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { startEdge = aReflowState.ComputedLogicalBorderPadding().IStart(wm); } 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 -= aReflowState.ComputedLogicalBorderPadding().IEnd(wm); 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); } // 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); } } 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(); rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); done = NS_FAILED(rv) || 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; } rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_FAILED(rv) || 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() = lineLayout->EndSpan(this); // Compute final width. // 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. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { aMetrics.ISize() += aReflowState.ComputedLogicalBorderPadding().IStart(wm); } /* * 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. */ if (NS_FRAME_IS_COMPLETE(aStatus) && !LastInFlow()->GetNextContinuation() && !FrameIsNonLastInIBSplit()) { aMetrics.Width() += aReflowState.ComputedLogicalBorderPadding().IEnd(wm); } nsRefPtr<nsFontMetrics> fm; float inflation = nsLayoutUtils::FontSizeInflationFor(this); nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation); aReflowState.rendContext->SetFont(fm); if (fm) { // Compute final height of the frame. // // Do things the standard css2 way -- though it's hard to find it // in the css2 spec! It's actually found in the css1 spec section // 4.4 (you will have to read between the lines to really see // it). // // The height of our box is the sum of our font size plus the top // and bottom border and padding. The height of children do not // affect our height. aMetrics.SetTopAscent(fm->MaxAscent()); aMetrics.Height() = fm->MaxHeight(); } else { NS_WARNING("Cannot get font metrics - defaulting sizes to 0"); aMetrics.SetTopAscent(aMetrics.Height() = 0); } aMetrics.SetTopAscent(aMetrics.TopAscent() + aReflowState.ComputedPhysicalBorderPadding().top); aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().top + aReflowState.ComputedPhysicalBorderPadding().bottom; // 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 return rv; }
void nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aDesiredSize.Width() = aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = aReflowState.ComputedHeight() + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent); nsSVGOuterSVGAnonChildFrame *anonKid = static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); if (mState & NS_FRAME_FIRST_REFLOW) { // Initialize svgElem->UpdateHasChildrenOnlyTransform(); } // If our SVG viewport has changed, update our content and notify. // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace svgFloatSize newViewportSize( nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight())); svgFloatSize oldViewportSize = svgElem->GetViewportSize(); uint32_t changeBits = 0; if (newViewportSize != oldViewportSize) { // When our viewport size changes, we may need to update the overflow rects // of our child frames. This is the case if: // // * We have a real/synthetic viewBox (a children-only transform), since // the viewBox transform will change as the viewport dimensions change. // // * We do not have a real/synthetic viewBox, but the last time we // reflowed (or the last time UpdateOverflow() was called) we did. // // We only handle the former case here, in which case we mark all our child // frames as dirty so that we reflow them below and update their overflow // rects. // // In the latter case, updating of overflow rects is handled for removal of // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic // viewBox "removal" (e.g. a document references the same SVG via both an // <svg:image> and then as a CSS background image (a synthetic viewBox is // used when painting the former, but not when painting the latter)) is // handled in SVGSVGElement::FlushImageTransformInvalidation. // if (svgElem->HasViewBoxOrSyntheticViewBox()) { nsIFrame* anonChild = GetFirstPrincipalChild(); anonChild->AddStateBits(NS_FRAME_IS_DIRTY); for (nsIFrame* child = anonChild->GetFirstPrincipalChild(); child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } changeBits |= COORD_CONTEXT_CHANGED; svgElem->SetViewportSize(newViewportSize); } if (mFullZoom != PresContext()->GetFullZoom()) { changeBits |= FULL_ZOOM_CHANGED; mFullZoom = PresContext()->GetFullZoom(); } if (changeBits) { NotifyViewportOrTransformChanged(changeBits); } mViewportInitialized = true; // Now that we've marked the necessary children as dirty, call // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending // on whether we are non-display. mCallingReflowSVG = true; if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { ReflowSVGNonDisplayText(this); } else { // Update the mRects and visual overflow rects of all our descendants, // including our anonymous wrapper kid: anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY); anonKid->ReflowSVG(); NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(), "We should have one anonymous child frame wrapping our real children"); } mCallingReflowSVG = false; // Set our anonymous kid's offset from our border box: anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft()); // Including our size in our overflow rects regardless of the value of // 'background', 'border', etc. makes sure that we usually (when we clip to // our content area) don't have to keep changing our overflow rects as our // descendants move about (see perf comment below). Including our size in our // scrollable overflow rect also makes sure that we scroll if we're too big // for our viewport. // // <svg> never allows scrolling to anything outside its mRect (only panning), // so we must always keep our scrollable overflow set to our size. // // With regards to visual overflow, we always clip root-<svg> (see our // BuildDisplayList method) regardless of the value of the 'overflow' // property since that is per-spec, even for the initial 'visible' value. For // that reason there's no point in adding descendant visual overflow to our // own when this frame is for a root-<svg>. That said, there's also a very // good performance reason for us wanting to avoid doing so. If we did, then // the frame's overflow would often change as descendants that are partially // or fully outside its rect moved (think animation on/off screen), and that // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the // entire document tree each such move (see bug 875175). // // So it's only non-root outer-<svg> that has the visual overflow of its // descendants added to its own. (Note that the default user-agent style // sheet makes 'hidden' the default value for :not(root(svg)), so usually // FinishAndStoreOverflow will still clip this back to the frame's rect.) // // WARNING!! Keep UpdateBounds below in sync with whatever we do for our // overflow rects here! (Again, see bug 875175.) // aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!mIsRootContent) { aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect( aDesiredSize.mOverflowAreas.VisualOverflow(), anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); } FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d", aDesiredSize.Width(), aDesiredSize.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
nsresult nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus) { DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus); nsresult rv = NS_OK; // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding(); nscoord lr = bp.left + bp.right; nscoord tb = bp.top + bp.bottom; NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE, "should no longer use unconstrained widths"); availSize.width -= lr; if (NS_UNCONSTRAINEDSIZE != availSize.height) { availSize.height -= tb; } // 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. nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize); nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr); ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE, false, true, ll.LineContainerFrame()->GetWritingMode(kid), aReflowState.AvailableWidth()); rs.mLineLayout = ≪ ll.SetInFirstLetter(true); ll.SetFirstLetterStyleOK(true); kid->WillReflow(aPresContext); kid->Reflow(aPresContext, aMetrics, 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 = aMetrics.TopAscent(); } 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.left, availSize.width, &mBaseline); ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame); ll->EndSpan(this); ll->SetInFirstLetter(false); } // Place and size the child and update the output metrics kid->SetRect(nsRect(bp.left, bp.top, aMetrics.Width(), aMetrics.Height())); kid->FinishAndStoreOverflow(&aMetrics); kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); aMetrics.Width() += lr; aMetrics.Height() += tb; aMetrics.SetTopAscent(aMetrics.TopAscent() + bp.top); // 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); 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 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent()) ->DeleteNextInFlowChild(kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already // have one. if (!IsFloating()) { nsIFrame* nextInFlow; rv = CreateNextInFlow(kid, nextInFlow); if (NS_FAILED(rv)) { return rv; } // 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; rv = CreateContinuationForFloatingParent(aPresContext, kid, &continuation, true); } } } FinishAndStoreOverflow(&aMetrics); NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics); return rv; }
void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_ASSERTION(aReflowState.ComputedWidth() != NS_UNCONSTRAINEDSIZE, "Shouldn't have unconstrained stuff here " "thanks to the rules of reflow"); NS_ASSERTION(NS_INTRINSICSIZE != aReflowState.ComputedHeight(), "Shouldn't have unconstrained stuff here " "thanks to ComputeAutoSize"); aStatus = NS_FRAME_COMPLETE; NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen"); // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed> aDesiredSize.SetSize(aReflowState.GetWritingMode(), aReflowState.ComputedSizeWithBorderPadding()); // "offset" is the offset of our content area from our frame's // top-left corner. nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left, aReflowState.ComputedPhysicalBorderPadding().top); if (mInnerView) { const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding(); nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(), aDesiredSize.Height() - bp.TopBottom()); // Size & position the view according to 'object-fit' & 'object-position'. nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); IntrinsicSize intrinsSize; nsSize intrinsRatio; if (subDocRoot) { intrinsSize = subDocRoot->GetIntrinsicSize(); intrinsRatio = subDocRoot->GetIntrinsicRatio(); } nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize), intrinsSize, intrinsRatio, StylePosition()); nsViewManager* vm = mInnerView->GetViewManager(); vm->MoveViewTo(mInnerView, destRect.x, destRect.y); vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true); } aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!ShouldClipSubdocument()) { nsIFrame* subdocRootFrame = GetSubdocumentRootFrame(); if (subdocRootFrame) { aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset); } } FinishAndStoreOverflow(&aDesiredSize); if (!aPresContext->IsPaginated() && !mPostedReflowCallback) { PresContext()->PresShell()->PostReflowCallback(this); mPostedReflowCallback = true; } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x", aDesiredSize.Width(), aDesiredSize.Height(), aStatus)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsNumberControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { 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); } // The width of our content box, which is the available width // for our anonymous content: const nscoord contentBoxWidth = aReflowState.ComputedWidth(); nscoord contentBoxHeight = aReflowState.ComputedHeight(); nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? if (contentBoxHeight == NS_INTRINSICSIZE) { contentBoxHeight = 0; } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); nsHTMLReflowMetrics wrappersDesiredSize(aReflowState); WritingMode wm = outerWrapperFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, outerWrapperFrame, availSize); // offsets of wrapper frame nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left + wrapperReflowState.ComputedPhysicalMargin().left; nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top + wrapperReflowState.ComputedPhysicalMargin().top; nsReflowStatus childStatus; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, wrapperReflowState, xoffset, yoffset, 0, childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained height, so it should be complete"); nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() + wrapperReflowState.ComputedPhysicalMargin().TopBottom(); if (contentBoxHeight == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's // height: contentBoxHeight = wrappersMarginBoxHeight; // 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 // aReflowState.ComputedHeight()). Note that we do this before // adjusting for borderpadding, since mComputedMaxHeight and // mComputedMinHeight are content heights. contentBoxHeight = NS_CSS_MINMAX(contentBoxHeight, aReflowState.ComputedMinHeight(), aReflowState.ComputedMaxHeight()); } // Center child vertically nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight; yoffset += std::max(0, extraSpace / 2); // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowState, xoffset, yoffset, 0); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowState.GetWritingMode(), contentBoxWidth)); } aDesiredSize.Width() = contentBoxWidth + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = contentBoxHeight + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.Width() = aReflowState.ComputedWidth(); aMetrics.Height() = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame* child : mFrames) { if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, &cbSize); nsRect posterRenderRect; if (ShouldDisplayPoster()) { posterRenderRect = nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top), nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); } kidReflowState.SetComputedWidth(posterRenderRect.width); kidReflowState.SetComputedHeight(posterRenderRect.height); ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, posterRenderRect.x, posterRenderRect.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState, posterRenderRect.x, posterRenderRect.y, 0); } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsSize size = child->GetSize(); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); if (child->GetSize() != size) { RefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } else if (child->GetContent() == mCaptionDiv) { // Reflow to caption div nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, &cbSize); nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); kidReflowState.SetComputedWidth(std::max(size.width, 0)); kidReflowState.SetComputedHeight(std::max(size.height, 0)); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowState, mBorderPadding.left, mBorderPadding.top, 0); } } aMetrics.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aMetrics); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.Width(), aMetrics.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); }
void nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState) { // The width/height of our content box, which is the available width/height // for our anonymous content: nscoord rangeFrameContentBoxWidth = aReflowState.ComputedWidth(); nscoord rangeFrameContentBoxHeight = aReflowState.ComputedHeight(); if (rangeFrameContentBoxHeight == NS_AUTOHEIGHT) { rangeFrameContentBoxHeight = 0; } nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); if (trackFrame) { // display:none? // Position the track: // The idea here is that we allow content authors to style the width, // height, border and padding of the track, but we ignore margin and // positioning properties and do the positioning ourself to keep the center // of the track's border box on the center of the nsRangeFrame's content // box. nsHTMLReflowState trackReflowState(aPresContext, aReflowState, trackFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); // Find the x/y position of the track frame such that it will be positioned // as described above. These coordinates are with respect to the // nsRangeFrame's border-box. nscoord trackX = rangeFrameContentBoxWidth / 2; nscoord trackY = rangeFrameContentBoxHeight / 2; // Account for the track's border and padding (we ignore its margin): trackX -= trackReflowState.ComputedPhysicalBorderPadding().left + trackReflowState.ComputedWidth() / 2; trackY -= trackReflowState.ComputedPhysicalBorderPadding().top + trackReflowState.ComputedHeight() / 2; // Make relative to our border box instead of our content box: trackX += aReflowState.ComputedPhysicalBorderPadding().left; trackY += aReflowState.ComputedPhysicalBorderPadding().top; nsReflowStatus frameStatus; nsHTMLReflowMetrics trackDesiredSize(aReflowState); ReflowChild(trackFrame, aPresContext, trackDesiredSize, trackReflowState, trackX, trackY, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(trackFrame, aPresContext, trackDesiredSize, &trackReflowState, trackX, trackY, 0); } nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { // display:none? nsHTMLReflowState thumbReflowState(aPresContext, aReflowState, thumbFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); // Where we position the thumb depends on its size, so we first reflow // the thumb at {0,0} to obtain its size, then position it afterwards. nsReflowStatus frameStatus; nsHTMLReflowMetrics thumbDesiredSize(aReflowState); ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, thumbReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(thumbFrame, aPresContext, thumbDesiredSize, &thumbReflowState, 0, 0, 0); DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); if (rangeProgressFrame) { // display:none? nsHTMLReflowState progressReflowState(aPresContext, aReflowState, rangeProgressFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); // We first reflow the range-progress frame at {0,0} to obtain its // unadjusted dimensions, then we adjust it to so that the appropriate edge // ends at the thumb. nsReflowStatus frameStatus; nsHTMLReflowMetrics progressDesiredSize(aReflowState); ReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, progressReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, &progressReflowState, 0, 0, 0); DoUpdateRangeProgressFrame(rangeProgressFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } }
nsresult nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.Width() = aReflowState.ComputedWidth(); aMetrics.Height() = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame *child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize(aReflowState.GetWritingMode()); nsSize availableSize = nsSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.Width(), aMetrics.Height()); uint32_t posterHeight, posterWidth; nsSize scaledPosterSize(0, 0); nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); nsPoint posterTopLeft(0, 0); nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage); NS_ENSURE_TRUE(posterImage, NS_ERROR_FAILURE); posterImage->GetNaturalHeight(&posterHeight); posterImage->GetNaturalWidth(&posterWidth); if (ShouldDisplayPoster() && posterHeight && posterWidth) { gfxFloat scale = std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)), static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight))); gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight); scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width)); scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height)); } kidReflowState.SetComputedWidth(scaledPosterSize.width); kidReflowState.SetComputedHeight(scaledPosterSize.height); posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left; posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top; ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, posterTopLeft.x, posterTopLeft.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState, posterTopLeft.x, posterTopLeft.y, 0); } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsSize size = child->GetSize(); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); if (child->GetSize() != size) { nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } else if (child->GetContent() == mCaptionDiv) { // Reflow to caption div nsHTMLReflowMetrics kidDesiredSize(aReflowState.GetWritingMode()); nsSize availableSize = nsSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, aMetrics.Width(), aMetrics.Height()); nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); kidReflowState.SetComputedWidth(std::max(size.width, 0)); kidReflowState.SetComputedHeight(std::max(size.height, 0)); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowState, mBorderPadding.left, mBorderPadding.top, 0); } } aMetrics.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aMetrics); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.Width(), aMetrics.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; }
void nsColumnSetFrame::FindBestBalanceHeight(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, ReflowConfig& aConfig, ColumnBalanceData& aColData, nsHTMLReflowMetrics& aDesiredSize, nsCollapsingMargin& aOutMargin, bool& aUnboundedLastColumn, bool& aRunWasFeasible, nsReflowStatus& aStatus) { bool feasible = aRunWasFeasible; nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); bp.ApplySkipSides(GetSkipSides()); bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom; nscoord availableContentHeight = GetAvailableContentHeight(aReflowState); // Termination of the algorithm below is guaranteed because // aConfig.knownFeasibleHeight - aConfig.knownInfeasibleHeight decreases in every // iteration. // We set this flag when we detect that we may contain a frame // that can break anywhere (thus foiling the linear decrease-by-one // search) bool maybeContinuousBreakingDetected = false; while (!aPresContext->HasPendingInterrupt()) { nscoord lastKnownFeasibleHeight = aConfig.mKnownFeasibleHeight; // Record what we learned from the last reflow if (feasible) { // maxHeight is feasible. Also, mLastBalanceHeight is feasible. aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, aColData.mMaxHeight); aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, mLastBalanceHeight); // Furthermore, no height less than the height of the last // column can ever be feasible. (We might be able to reduce the // height of a non-last column by moving content to a later column, // but we can't do that with the last column.) if (mFrames.GetLength() == aConfig.mBalanceColCount) { aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, aColData.mLastHeight - 1); } } else { aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, mLastBalanceHeight); // If a column didn't fit in its available height, then its current // height must be the minimum height for unbreakable content in // the column, and therefore no smaller height can be feasible. aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, aColData.mMaxOverflowingHeight - 1); if (aUnboundedLastColumn) { // The last column is unbounded, so all content got reflowed, so the // mColMaxHeight is feasible. aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, aColData.mMaxHeight); } } #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n", aConfig.mKnownInfeasibleHeight, aConfig.mKnownFeasibleHeight); #endif if (aConfig.mKnownInfeasibleHeight >= aConfig.mKnownFeasibleHeight - 1) { // aConfig.mKnownFeasibleHeight is where we want to be break; } if (aConfig.mKnownInfeasibleHeight >= availableContentHeight) { break; } if (lastKnownFeasibleHeight - aConfig.mKnownFeasibleHeight == 1) { // We decreased the feasible height by one twip only. This could // indicate that there is a continuously breakable child frame // that we are crawling through. maybeContinuousBreakingDetected = true; } nscoord nextGuess = (aConfig.mKnownFeasibleHeight + aConfig.mKnownInfeasibleHeight)/2; // The constant of 600 twips is arbitrary. It's about two line-heights. if (aConfig.mKnownFeasibleHeight - nextGuess < 600 && !maybeContinuousBreakingDetected) { // We're close to our target, so just try shrinking just the // minimum amount that will cause one of our columns to break // differently. nextGuess = aConfig.mKnownFeasibleHeight - 1; } else if (aUnboundedLastColumn) { // Make a guess by dividing that into N columns. Add some slop // to try to make it on the feasible side. The constant of // 600 twips is arbitrary. It's about two line-heights. nextGuess = aColData.mSumHeight/aConfig.mBalanceColCount + 600; // Sanitize it nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleHeight + 1, aConfig.mKnownFeasibleHeight - 1); } else if (aConfig.mKnownFeasibleHeight == NS_INTRINSICSIZE) { // This can happen when we had a next-in-flow so we didn't // want to do an unbounded height measuring step. Let's just increase // from the infeasible height by some reasonable amount. nextGuess = aConfig.mKnownInfeasibleHeight*2 + 600; } // Don't bother guessing more than our height constraint. nextGuess = std::min(availableContentHeight, nextGuess); #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess); #endif aConfig.mColMaxHeight = nextGuess; aUnboundedLastColumn = false; AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, false, &aOutMargin, aColData); if (!aConfig.mIsBalancing) { // Looks like we had excess height when balancing, so we gave up on // trying to balance. break; } } if (aConfig.mIsBalancing && !feasible && !aPresContext->HasPendingInterrupt()) { // We may need to reflow one more time at the feasible height to // get a valid layout. bool skip = false; if (aConfig.mKnownInfeasibleHeight >= availableContentHeight) { aConfig.mColMaxHeight = availableContentHeight; if (mLastBalanceHeight == availableContentHeight) { skip = true; } } else { aConfig.mColMaxHeight = aConfig.mKnownFeasibleHeight; } if (!skip) { // If our height is unconstrained, make sure that the last column is // allowed to have arbitrary height here, even though we were balancing. // Otherwise we'd have to split, and it's not clear what we'd do with // that. AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, availableContentHeight == NS_UNCONSTRAINEDSIZE, &aOutMargin, aColData); } } aRunWasFeasible = feasible; }
bool nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, bool* aMayNeedRetry, bool* aBlockIsEmpty) { // Include frame's top margin aMargin->Include(aRS.ComputedPhysicalMargin().top); // The inclusion of the bottom 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_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": %d => %d\n", aRS.ComputedPhysicalMargin().top, aMargin->get()); #endif bool dirtiedLine = false; bool setBlockIsEmpty = false; // Calculate the frame's generational top-margin from its child // blocks. Note that if the frame has a non-zero top-border or // top-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.ComputedPhysicalBorderPadding().top) { block = nsLayoutUtils::GetAsBlock(frame); if (block) { bool topMarginRoot, unused; block->IsMarginRoot(&topMarginRoot, &unused); if (topMarginRoot) { 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"); nsSize availSpace(aRS.ComputedWidth(), aRS.ComputedHeight()); outerReflowState = new nsHTMLReflowState(prescontext, aRS, frame, availSpace); } { nsSize availSpace(outerReflowState->ComputedWidth(), outerReflowState->ComputedHeight()); 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 (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) { line->MarkDirty(); dirtiedLine = true; } if (isEmpty) aMargin->Include(innerReflowState.ComputedPhysicalMargin().bottom); } 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_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": => %d\n", aMargin->get()); #endif return dirtiedLine; }
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); }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aBottomMarginCarriedOut, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; bool RTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; bool shrinkingHeightOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceHeight > aConfig.mColMaxHeight; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n", mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount, aConfig.mColWidth, aConfig.mColGap); #endif DrainOverflowColumns(); const bool colHeightChanged = mLastBalanceHeight != aConfig.mColMaxHeight; if (colHeightChanged) { mLastBalanceHeight = aConfig.mColMaxHeight; // 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 nsMargin borderPadding = aReflowState.ComputedPhysicalBorderPadding(); borderPadding.ApplySkipSides(GetSkipSides(&aReflowState)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top); // For RTL, figure out where the last column's left edge should be. 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 unnecessarily. if (RTL) { nscoord availWidth = aReflowState.AvailableWidth(); if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { availWidth = aReflowState.ComputedWidth(); } if (availWidth != NS_INTRINSICSIZE) { childOrigin.x += availWidth - aConfig.mColWidth; #ifdef DEBUG_roc printf("*** childOrigin.x = %d\n", childOrigin.x); #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 skipResizeHeightShrink = shrinkingHeightOnly && child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight; nscoord childContentBEnd = 0; WritingMode wm = child->GetWritingMode(); if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(this, child, childOrigin); // 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 height shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus); #endif } else { nsSize physicalSize(aConfig.mColWidth, aConfig.mColMaxHeight); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { physicalSize.height = GetAvailableContentHeight(aReflowState); } LogicalSize availSize(wm, physicalSize); LogicalSize computedSize = aReflowState.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, availSize.ISize(wm), computedSize.BSize(wm)); 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 = !colHeightChanged; #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 ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, childOrigin.x + kidReflowState.ComputedPhysicalMargin().left, childOrigin.y + kidReflowState.ComputedPhysicalMargin().top, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBottomMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, &kidReflowState, childOrigin.x, childOrigin.y, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxHeight) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingHeight = std::max(childContentBEnd, aColData.mMaxOverflowingHeight); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBEnd = std::max(contentBEnd, childContentBEnd); aColData.mLastHeight = childContentBEnd; aColData.mSumHeight += 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 nsresult rv = CreateNextInFlow(child, kidNextInFlow); if (NS_FAILED(rv)) { NS_NOTREACHED("Couldn't create continuation"); child = nullptr; break; } } // 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.mHasExcessHeight = 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) { if (!RTL) { childOrigin.x += aConfig.mColWidth + aConfig.mColGap; } else { childOrigin.x -= aConfig.mColWidth + aConfig.mColGap; } #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x); #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.mMaxHeight = contentBEnd; contentRect.height = std::max(contentRect.height, contentBEnd); mLastFrameStatus = aStatus; // contentRect included the borderPadding.left,borderPadding.top of the child rects contentRect -= nsPoint(borderPadding.left, borderPadding.top); WritingMode wm = aReflowState.GetWritingMode(); LogicalSize contentSize(wm, nsSize(contentRect.XMost(), contentRect.YMost())); // Apply computed and min/max values // (aConfig members need to be converted from Width/Height to ISize/BSize) if (aConfig.mComputedHeight != NS_INTRINSICSIZE) { if (aReflowState.AvailableHeight() != NS_INTRINSICSIZE) { contentSize.BSize(wm) = std::min(contentSize.BSize(wm), aConfig.mComputedHeight); } else { contentSize.BSize(wm) = aConfig.mComputedHeight; } } else { // We add the "consumed" height back in so that we're applying // constraints to the correct height 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 height. but which // has prev-in-flows, trigger a larger height than actually required. contentSize.BSize(wm) = aReflowState.ApplyMinMaxHeight(contentSize.BSize(wm), aConfig.mConsumedHeight); } if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowState.ComputedISize(); } else { contentSize.ISize(wm) = aReflowState.ApplyMinMaxWidth(contentSize.ISize(wm)); } LogicalMargin bp(wm, borderPadding); contentSize.ISize(wm) += bp.IStartEnd(wm); contentSize.BSize(wm) += bp.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); #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 nsLeafBoxFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { // This is mostly a copy of nsBoxFrame::Reflow(). // We aren't able to share an implementation because of the frame // class hierarchy. If you make changes here, please keep // nsBoxFrame::Reflow in sync. DO_GLOBAL_REFLOW_COUNT("nsLeafBoxFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(aReflowState.ComputedWidth() >=0 && aReflowState.ComputedHeight() >= 0, "Computed Size < 0"); #ifdef DO_NOISY_REFLOW printf("\n-------------Starting LeafBoxFrame Reflow ----------------------------\n"); printf("%p ** nsLBF::Reflow %d R: ", this, myCounter++); switch (aReflowState.reason) { case eReflowReason_Initial: printf("Ini");break; case eReflowReason_Incremental: printf("Inc");break; case eReflowReason_Resize: printf("Rsz");break; case eReflowReason_StyleChange: printf("Sty");break; case eReflowReason_Dirty: printf("Drt "); break; default:printf("<unknown>%d", aReflowState.reason);break; } printSize("AW", aReflowState.AvailableWidth()); printSize("AH", aReflowState.AvailableHeight()); printSize("CW", aReflowState.ComputedWidth()); printSize("CH", aReflowState.ComputedHeight()); printf(" *\n"); #endif aStatus = NS_FRAME_COMPLETE; // create the layout state nsBoxLayoutState state(aPresContext, aReflowState.rendContext); nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight()); nsMargin m; m = aReflowState.ComputedPhysicalBorderPadding(); //GetBorderAndPadding(m); // this happens sometimes. So lets handle it gracefully. if (aReflowState.ComputedHeight() == 0) { nsSize minSize = GetMinSize(state); computedSize.height = minSize.height - m.top - m.bottom; } nsSize prefSize(0,0); // if we are told to layout intrinic then get our preferred size. if (computedSize.width == NS_INTRINSICSIZE || computedSize.height == NS_INTRINSICSIZE) { prefSize = GetPrefSize(state); nsSize minSize = GetMinSize(state); nsSize maxSize = GetMaxSize(state); prefSize = BoundsCheck(minSize, prefSize, maxSize); } // get our desiredSize if (aReflowState.ComputedWidth() == NS_INTRINSICSIZE) { computedSize.width = prefSize.width; } else { computedSize.width += m.left + m.right; } if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { computedSize.height = prefSize.height; } else { computedSize.height += m.top + m.bottom; } // handle reflow state min and max sizes // XXXbz the width handling here seems to be wrong, since // mComputedMin/MaxWidth is a content-box size, whole // computedSize.width is a border-box size... if (computedSize.width > aReflowState.ComputedMaxWidth()) computedSize.width = aReflowState.ComputedMaxWidth(); if (computedSize.width < aReflowState.ComputedMinWidth()) computedSize.width = aReflowState.ComputedMinWidth(); // Now adjust computedSize.height for our min and max computed // height. The only problem is that those are content-box sizes, // while computedSize.height is a border-box size. So subtract off // m.TopBottom() before adjusting, then readd it. computedSize.height = std::max(0, computedSize.height - m.TopBottom()); computedSize.height = NS_CSS_MINMAX(computedSize.height, aReflowState.ComputedMinHeight(), aReflowState.ComputedMaxHeight()); computedSize.height += m.TopBottom(); nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height); SetBounds(state, r); // layout our children Layout(state); // ok our child could have gotten bigger. So lets get its bounds aDesiredSize.Width() = mRect.width; aDesiredSize.Height() = mRect.height; aDesiredSize.SetTopAscent(GetBoxAscent(state)); // the overflow rect is set in SetBounds() above aDesiredSize.mOverflowAreas = GetOverflowAreas(); #ifdef DO_NOISY_REFLOW { printf("%p ** nsLBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height()); if (maxElementWidth) { printf("MW:%d\n", *maxElementWidth); } else { printf("MW:?\n"); } } #endif }
void nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { bool vertical = ResolvedOrientationIsVertical(); WritingMode wm = aBarFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, availSize); nscoord size = vertical ? aReflowState.ComputedHeight() : aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; double position = static_cast<HTMLProgressElement*>(mContent)->Position(); // Force the bar's size to match the current progress. // When indeterminate, the progress' size will be 100%. if (position >= 0.0) { size *= position; } if (!vertical && (wm.IsVertical() ? wm.IsVerticalRL() : !wm.IsBidiLTR())) { xoffset += aReflowState.ComputedWidth() - size; } // The bar size is fixed in these cases: // - the progress position is determined: the bar size is fixed according // to it's value. // - the progress position is indeterminate and the bar appearance should be // shown as native: the bar size is forced to 100%. // Otherwise (when the progress is indeterminate and the bar appearance isn't // native), the bar size isn't fixed and can be set by the author. if (position != -1 || ShouldUseNativeStyle()) { if (vertical) { // We want the bar to begin at the bottom. yoffset += aReflowState.ComputedHeight() - size; size -= reflowState.ComputedPhysicalMargin().TopBottom() + reflowState.ComputedPhysicalBorderPadding().TopBottom(); size = std::max(size, 0); reflowState.SetComputedHeight(size); } else { size -= reflowState.ComputedPhysicalMargin().LeftRight() + reflowState.ComputedPhysicalBorderPadding().LeftRight(); size = std::max(size, 0); reflowState.SetComputedWidth(size); } } else if (vertical) { // For vertical progress bars, we need to position the bar specificly when // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) // because aReflowState.ComputedHeight() - size == 0. yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight(); } xoffset += reflowState.ComputedPhysicalMargin().left; yoffset += reflowState.ComputedPhysicalMargin().top; nsHTMLReflowMetrics barDesiredSize(aReflowState); ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, xoffset, yoffset, 0); }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aTopMarginRoot, bool aBottomMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedHeight) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mPushedFloats(nullptr), mOverflowTracker(nullptr), mPrevBottomMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedHeight(aConsumedHeight) { SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); const nsMargin& borderPadding = BorderPadding(); if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) { SetFlag(BRS_ISTOPMARGINROOT, true); } if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) { SetFlag(BRS_ISBOTTOMMARGINROOT, true); } if (GetFlag(BRS_ISTOPMARGINROOT)) { SetFlag(BRS_APPLYTOPMARGIN, true); } if (aBlockNeedsFloatManager) { SetFlag(BRS_FLOAT_MGR, true); } mFloatManager = aReflowState.mFloatManager; NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } mReflowStatus = NS_FRAME_COMPLETE; mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedWidth(), "have unconstrained width; this should only result from " "very large sizes, not attempts at intrinsic width " "calculation"); mContentArea.width = aReflowState.ComputedWidth(); // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight()) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom; mContentArea.height = std::max(0, mBottomEdge - borderPadding.top); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDHEIGHT, true); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.x = borderPadding.left; mY = mContentArea.y = borderPadding.top; mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }