void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); LogicalSize availSize = aButtonReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_INTRINSICSIZE; // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: LogicalMargin focusPadding = LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding()); // See whether out availSize's inline-size is big enough. If it's // smaller than our intrinsic min iSize, that means that the kid // wouldn't really fit. In that case, we overflow into our internal // focuspadding (which other browsers don't have) so that there's a // little more space for it. // Note that GetMinISize includes the focusPadding. nscoord IOverflow = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedISize(); nscoord IFocusPadding = focusPadding.IStartEnd(wm); nscoord focusPaddingReduction = std::min(IFocusPadding, std::max(IOverflow, 0)); if (focusPaddingReduction > 0) { nscoord startReduction = focusPadding.IStart(wm); if (focusPaddingReduction != IFocusPadding) { startReduction = NSToCoordRound(startReduction * (float(focusPaddingReduction) / float(IFocusPadding))); } focusPadding.IStart(wm) -= startReduction; focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction; } // shorthand for a value we need to use in a bunch of places const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding(); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= focusPadding.IStartEnd(wm); LogicalPoint childPos(wm); childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm); availSize.ISize(wm) = std::max(availSize.ISize(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding.GetPhysicalMargin(wm)); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); childPos.B(wm) = 0; // This will be set properly later, after reflowing the // child to determine its size. // We just pass a dummy containerSize here, as the child will be // repositioned later by FinishReflowChild. nsSize dummyContainerSize; ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, wm, childPos, dummyContainerSize, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box size: LogicalSize buttonContentBox(wm); if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBox.BSize(wm) = aButtonReflowState.ComputedBSize(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' bSize, plus any focus-padding space: buttonContentBox.BSize(wm) = contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm); // Make sure we obey min/max-bSize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since mComputedMaxBSize and // mComputedMinBSize are content bSizes. buttonContentBox.BSize(wm) = NS_CSS_MINMAX(buttonContentBox.BSize(wm), aButtonReflowState.ComputedMinBSize(), aButtonReflowState.ComputedMaxBSize()); } if (aButtonReflowState.ComputedISize() != NS_INTRINSICSIZE) { buttonContentBox.ISize(wm) = aButtonReflowState.ComputedISize(); } else { buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm); buttonContentBox.ISize(wm) = NS_CSS_MINMAX(buttonContentBox.ISize(wm), aButtonReflowState.ComputedMinISize(), aButtonReflowState.ComputedMaxISize()); } // Center child in the block-direction in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) - contentsDesiredSize.BSize(wm); childPos.B(wm) = std::max(0, extraSpace / 2); // Adjust childPos.B() to be in terms of the button's frame-rect, instead of // its focus-padding rect: childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm); nsSize containerSize = (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, wm, childPos, containerSize, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.SetSize(wm, LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm), buttonContentBox.BSize(wm) + clbp.BStartEnd(wm))); // * Button's ascent is its child's ascent, plus the child's block-offset // within our frame... unless it's orthogonal, in which case we'll use the // contents inline-size as an approximation for now. // XXX is there a better strategy? should we include border-padding? if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm)); } else { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + childPos.B(wm)); } aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }