Bug 1755565 - Implement contain: inline-size. r=emilio,dholbert

Differential Revision: https://phabricator.services.mozilla.com/D143501
This commit is contained in:
David Shin 2022-04-19 17:44:16 +00:00
parent c582dbf6d7
commit ec93876623
34 changed files with 343 additions and 128 deletions

View file

@ -5937,6 +5937,7 @@ exports.CSS_PROPERTIES = {
"content",
"inherit",
"initial",
"inline-size",
"layout",
"none",
"paint",

View file

@ -322,7 +322,8 @@ nscoord nsComboboxControlFrame::GetIntrinsicISize(gfxContext* aRenderingContext,
IntrinsicISizeType aType) {
nscoord displayISize = mDisplayFrame->IntrinsicISizeOffsets().padding;
if (!StyleDisplay()->IsContainSize() && !StyleContent()->mContent.IsNone()) {
if (!StyleDisplay()->GetContainSizeAxes().mIContained &&
!StyleContent()->mContent.IsNone()) {
// Compute the width of each option's (potentially text-transformed) text,
// and use the widest one as part of our intrinsic size.
nscoord maxOptionSize = 0;

View file

@ -353,7 +353,7 @@ nscoord nsFieldSetFrame::GetIntrinsicISize(gfxContext* aRenderingContext,
IntrinsicISizeType aType) {
nscoord legendWidth = 0;
nscoord contentWidth = 0;
if (!StyleDisplay()->IsContainSize()) {
if (!StyleDisplay()->GetContainSizeAxes().mIContained) {
// Both inner and legend are children, and if the fieldset is
// size-contained they should not contribute to the intrinsic size.
if (nsIFrame* legend = GetLegend()) {
@ -743,9 +743,9 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
LogicalSize finalSize(
wm, contentRect.ISize(wm) + border.IStartEnd(wm),
mLegendSpace + border.BStartEnd(wm) + (inner ? inner->BSize(wm) : 0));
if (aReflowInput.mStyleDisplay->IsContainSize()) {
// If we're size-contained, then we must set finalSize to be what
// it'd be if we had no children (i.e. if we had no legend and if
if (aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
// If we're size-contained in block axis, then we must set finalSize to be
// what it'd be if we had no children (i.e. if we had no legend and if
// 'inner' were empty). Note: normally the fieldset's own padding
// (which we still must honor) would be accounted for as part of
// inner's size (see kidReflowInput.Init() call above). So: since

View file

@ -121,7 +121,7 @@ void nsHTMLButtonControlFrame::BuildDisplayList(
nscoord nsHTMLButtonControlFrame::GetMinISize(gfxContext* aRenderingContext) {
nscoord result;
DISPLAY_MIN_INLINE_SIZE(this, result);
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
result = 0;
} else {
nsIFrame* kid = mFrames.FirstChild();
@ -134,7 +134,7 @@ nscoord nsHTMLButtonControlFrame::GetMinISize(gfxContext* aRenderingContext) {
nscoord nsHTMLButtonControlFrame::GetPrefISize(gfxContext* aRenderingContext) {
nscoord result;
DISPLAY_PREF_INLINE_SIZE(this, result);
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
result = 0;
} else {
nsIFrame* kid = mFrames.FirstChild();
@ -237,8 +237,9 @@ void nsHTMLButtonControlFrame::ReflowButtonContents(
if (aButtonReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
// Button has a fixed block-size -- that's its content-box bSize.
buttonContentBox.BSize(wm) = aButtonReflowInput.ComputedBSize();
} else if (aButtonReflowInput.mStyleDisplay->IsContainSize()) {
// Button is intrinsically sized and has size containment.
} else if (aButtonReflowInput.mStyleDisplay->GetContainSizeAxes()
.mBContained) {
// Button is intrinsically sized and has size containment in block axis.
// It should have a BSize that is either zero or the minimum
// specified BSize.
buttonContentBox.BSize(wm) = aButtonReflowInput.ComputedMinBSize();
@ -258,7 +259,8 @@ void nsHTMLButtonControlFrame::ReflowButtonContents(
}
if (aButtonReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE) {
buttonContentBox.ISize(wm) = aButtonReflowInput.ComputedISize();
} else if (aButtonReflowInput.mStyleDisplay->IsContainSize()) {
} else if (aButtonReflowInput.mStyleDisplay->GetContainSizeAxes()
.mIContained) {
buttonContentBox.ISize(wm) = aButtonReflowInput.ComputedMinISize();
} else {
buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm);

View file

@ -220,10 +220,11 @@ nscoord nsListControlFrame::CalcBSizeOfARow() {
// either of which may be visible or invisible, may use different
// fonts, etc.
nscoord rowBSize(0);
if (StyleDisplay()->IsContainSize() ||
if (StyleDisplay()->GetContainSizeAxes().mBContained ||
!GetMaxRowBSize(GetOptionsContainer(), GetWritingMode(), &rowBSize)) {
// We don't have any <option>s or <optgroup> labels with a frame.
// (Or we're size-contained, which has the same outcome for our sizing.)
// (Or we're size-contained in block axis, which has the same outcome for
// our sizing.)
float inflation = nsLayoutUtils::FontSizeInflationFor(this);
rowBSize = CalcFallbackRowBSize(inflation);
}
@ -239,7 +240,7 @@ nscoord nsListControlFrame::GetPrefISize(gfxContext* aRenderingContext) {
// dropdown, and standalone listboxes are overflow:scroll so they need
// it too.
WritingMode wm = GetWritingMode();
result = StyleDisplay()->IsContainSize()
result = StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: GetScrolledFrame()->GetPrefISize(aRenderingContext);
LogicalMargin scrollbarSize(
@ -257,7 +258,7 @@ nscoord nsListControlFrame::GetMinISize(gfxContext* aRenderingContext) {
// dropdown, and standalone listboxes are overflow:scroll so they need
// it too.
WritingMode wm = GetWritingMode();
result = StyleDisplay()->IsContainSize()
result = StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: GetScrolledFrame()->GetMinISize(aRenderingContext);
LogicalMargin scrollbarSize(

View file

@ -154,11 +154,11 @@ nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
nscoord iSize = 0;
DISPLAY_MIN_INLINE_SIZE(this, iSize);
if (StyleDisplay()->IsContainSize()) {
// If we're size-contained, we determine our minimum intrinsic size purely
// from our column styling, as if we had no descendants. This should match
// what happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
// scenario.
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
// If we're size-contained in inline axis, we determine our minimum
// intrinsic size purely from our column styling, as if we had no
// descendants. This should match what happens in
// nsColumnSetFrame::GetMinISize in an actual no-descendants scenario.
const nsStyleColumn* colStyle = StyleColumn();
if (colStyle->mColumnWidth.IsLength()) {
// As available inline size reduces to zero, our number of columns reduces
@ -189,7 +189,7 @@ nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
nscoord iSize = 0;
DISPLAY_PREF_INLINE_SIZE(this, iSize);
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
const nsStyleColumn* colStyle = StyleColumn();
nscoord colISize;
if (colStyle->mColumnWidth.IsLength()) {

View file

@ -421,8 +421,8 @@ void ReflowInput::Init(nsPresContext* aPresContext,
}
}
if (mStyleDisplay->IsContainSize()) {
// In the case that a box is size contained, we want to ensure
if (mStyleDisplay->GetContainSizeAxes().mBContained) {
// In the case that a box is size contained in block axis, we want to ensure
// that it is also monolithic. We do this by unsetting
// AvailableBSize() to avoid fragmentaiton.
AvailableBSize() = NS_UNCONSTRAINEDSIZE;

View file

@ -783,7 +783,7 @@ nscoord nsBlockFrame::GetMinISize(gfxContext* aRenderingContext) {
return mCachedMinISize;
}
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
mCachedMinISize = 0;
return mCachedMinISize;
}
@ -873,7 +873,7 @@ nscoord nsBlockFrame::GetPrefISize(gfxContext* aRenderingContext) {
return mCachedPrefISize;
}
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
mCachedPrefISize = 0;
return mCachedPrefISize;
}
@ -1947,11 +1947,11 @@ void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
// is replaced by the block size from aspect-ratio and inline size.
aMetrics.mCarriedOutBEndMargin.Zero();
} else if (!IsComboboxControlFrame() &&
aReflowInput.mStyleDisplay->IsContainSize()) {
// If we're size-containing and we don't have a specified size, then our
// final size should actually be computed from only our border and padding,
// as though we were empty.
// Hence this case is a simplified version of the case below.
aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
// If we're size-containing in block axis and we don't have a specified
// block size, then our final size should actually be computed from only our
// border and padding, as though we were empty. Hence this case is a
// simplified version of the case below.
//
// NOTE: We exempt the nsComboboxControlFrame subclass from taking this
// special case, because comboboxes implicitly honors the size-containment

View file

@ -4313,9 +4313,9 @@ nscoord nsFlexContainerFrame::ComputeMainSize(
return aTentativeContentBoxMainSize;
}
// Column-oriented case, with size-containment:
// Column-oriented case, with size-containment in block axis:
// Behave as if we had no content and just use our MinBSize.
if (aReflowInput.mStyleDisplay->IsContainSize()) {
if (aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
return aReflowInput.ComputedMinBSize();
}
@ -4360,9 +4360,9 @@ nscoord nsFlexContainerFrame::ComputeCrossSize(
return computedBSize;
}
// Row-oriented case, with size-containment:
// Row-oriented case, with size-containment in block axis:
// Behave as if we had no content and just use our MinBSize.
if (aReflowInput.mStyleDisplay->IsContainSize()) {
if (aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
*aIsDefinite = true;
return aReflowInput.ComputedMinBSize();
}
@ -5770,7 +5770,7 @@ nscoord nsFlexContainerFrame::GetMinISize(gfxContext* aRenderingContext) {
DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
mCachedMinISize =
StyleDisplay()->IsContainSize()
StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: IntrinsicISize(aRenderingContext, IntrinsicISizeType::MinISize);
}
@ -5783,7 +5783,7 @@ nscoord nsFlexContainerFrame::GetPrefISize(gfxContext* aRenderingContext) {
DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize);
if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
mCachedPrefISize =
StyleDisplay()->IsContainSize()
StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: IntrinsicISize(aRenderingContext, IntrinsicISizeType::PrefISize);
}

View file

@ -584,9 +584,9 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
// First, compute our inside-border size and scrollport size
// XXXldb Can we depend more on ComputeSize here?
nsSize kidSize = aState.mReflowInput.mStyleDisplay->IsContainSize()
? nsSize(0, 0)
: aKidMetrics->PhysicalSize();
nsSize kidSize =
aState.mReflowInput.mStyleDisplay->GetContainSizeAxes().ContainSize(
aKidMetrics->PhysicalSize(), wm);
const nsSize desiredInsideBorderSize = kidSize + scrollbarGutterSize;
aState.mInsideBorderSize =
ComputeInsideBorderSize(aState, desiredInsideBorderSize);
@ -969,7 +969,8 @@ bool nsHTMLScrollFrame::InInitialReflow() const {
void nsHTMLScrollFrame::ReflowContents(ScrollReflowInput& aState,
const ReflowOutput& aDesiredSize) {
ReflowOutput kidDesiredSize(aDesiredSize.GetWritingMode());
const WritingMode desiredWm = aDesiredSize.GetWritingMode();
ReflowOutput kidDesiredSize(desiredWm);
ReflowScrolledFrame(aState, GuessHScrollbarNeeded(aState),
GuessVScrollbarNeeded(aState), &kidDesiredSize);
@ -996,9 +997,9 @@ void nsHTMLScrollFrame::ReflowContents(ScrollReflowInput& aState,
aState.mReflowedContentsWithVScrollbar) &&
aState.mVScrollbar != ShowScrollbar::Always &&
aState.mHScrollbar != ShowScrollbar::Always) {
nsSize kidSize = aState.mReflowInput.mStyleDisplay->IsContainSize()
? nsSize(0, 0)
: kidDesiredSize.PhysicalSize();
nsSize kidSize =
aState.mReflowInput.mStyleDisplay->GetContainSizeAxes().ContainSize(
kidDesiredSize.PhysicalSize(), desiredWm);
nsSize insideBorderSize = ComputeInsideBorderSize(aState, kidSize);
nsRect scrolledRect = mHelper.GetUnsnappedScrolledRectInternal(
kidDesiredSize.ScrollableOverflow(), insideBorderSize);
@ -1160,7 +1161,7 @@ static bool IsMarqueeScrollbox(const nsIFrame& aScrollFrame) {
/* virtual */
nscoord nsHTMLScrollFrame::GetMinISize(gfxContext* aRenderingContext) {
nscoord result = [&] {
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
return 0;
}
if (MOZ_UNLIKELY(IsMarqueeScrollbox(*this))) {
@ -1176,7 +1177,7 @@ nscoord nsHTMLScrollFrame::GetMinISize(gfxContext* aRenderingContext) {
/* virtual */
nscoord nsHTMLScrollFrame::GetPrefISize(gfxContext* aRenderingContext) {
nscoord result =
StyleDisplay()->IsContainSize()
StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: mHelper.mScrolledFrame->GetPrefISize(aRenderingContext);
DISPLAY_PREF_INLINE_SIZE(this, result);

View file

@ -8601,7 +8601,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
// XXX Technically incorrect: We're ignoring our row sizes, when really
// we should use them but *they* should be computed as if we had no
// children. To be fixed in bug 1488878.
if (!aReflowInput.mStyleDisplay->IsContainSize()) {
if (!aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
if (IsMasonry(eLogicalAxisBlock)) {
bSize = computedBSize;
} else {
@ -8625,7 +8625,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
// XXX Technically incorrect: We're ignoring our row sizes, when really
// we should use them but *they* should be computed as if we had no
// children. To be fixed in bug 1488878.
if (!aReflowInput.mStyleDisplay->IsContainSize()) {
if (!aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
bSize = gridReflowInput.mRows.GridLineEdge(numRows,
GridLineSide::AfterGridGap);
@ -9352,7 +9352,7 @@ nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) {
DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
mCachedMinISize = StyleDisplay()->IsContainSize()
mCachedMinISize = StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: IntrinsicISize(aRC, IntrinsicISizeType::MinISize);
}
@ -9367,7 +9367,7 @@ nscoord nsGridContainerFrame::GetPrefISize(gfxContext* aRC) {
DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize);
if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
mCachedPrefISize = StyleDisplay()->IsContainSize()
mCachedPrefISize = StyleDisplay()->GetContainSizeAxes().mIContained
? 0
: IntrinsicISize(aRC, IntrinsicISizeType::PrefISize);
}

View file

@ -403,7 +403,7 @@ nscoord nsHTMLCanvasFrame::GetMinISize(gfxContext* aRenderingContext) {
// min-height, and max-height properties.
bool vertical = GetWritingMode().IsVertical();
nscoord result;
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
result = 0;
} else {
result = nsPresContext::CSSPixelsToAppUnits(
@ -419,7 +419,7 @@ nscoord nsHTMLCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) {
// min-height, and max-height properties.
bool vertical = GetWritingMode().IsVertical();
nscoord result;
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
result = 0;
} else {
result = nsPresContext::CSSPixelsToAppUnits(
@ -431,15 +431,17 @@ nscoord nsHTMLCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) {
/* virtual */
IntrinsicSize nsHTMLCanvasFrame::GetIntrinsicSize() {
if (StyleDisplay()->IsContainSize()) {
const auto containAxes = StyleDisplay()->GetContainSizeAxes();
if (containAxes.IsBoth()) {
return IntrinsicSize(0, 0);
}
return IntrinsicSizeFromCanvasSize(GetCanvasSize());
return containAxes.ContainIntrinsicSize(
IntrinsicSizeFromCanvasSize(GetCanvasSize()), GetWritingMode());
}
/* virtual */
AspectRatio nsHTMLCanvasFrame::GetIntrinsicRatio() const {
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().IsAny()) {
return AspectRatio();
}

View file

@ -749,7 +749,7 @@ void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
if (disp->IsContainLayout() && disp->IsContainSize() &&
if (disp->IsContainLayout() && disp->GetContainSizeAxes().IsBoth() &&
// All frames that support contain:layout also support contain:size.
IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
// In general, frames that have contain:layout+size can be reflow roots.

View file

@ -622,7 +622,8 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
nsImageFrame::Kind aKind,
const nsImageFrame& aFrame) {
const ComputedStyle& style = *aFrame.Style();
if (style.StyleDisplay()->IsContainSize()) {
const auto containAxes = style.StyleDisplay()->GetContainSizeAxes();
if (containAxes.IsBoth()) {
return IntrinsicSize(0, 0);
}
@ -648,19 +649,23 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
ScaleIntrinsicSizeForDensity(intrinsicSize,
aFrame.GetImageFromStyle()->GetResolution());
}
return intrinsicSize;
return containAxes.ContainIntrinsicSize(intrinsicSize,
aFrame.GetWritingMode());
}
if (aKind == nsImageFrame::Kind::ListStyleImage) {
// Note: images are handled above, this handles gradients etc.
nscoord defaultLength = ListImageDefaultLength(aFrame);
return IntrinsicSize(defaultLength, defaultLength);
return containAxes.ContainIntrinsicSize(
IntrinsicSize(defaultLength, defaultLength), aFrame.GetWritingMode());
}
if (aFrame.ShouldShowBrokenImageIcon()) {
nscoord edgeLengthToUse = nsPresContext::CSSPixelsToAppUnits(
ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
return IntrinsicSize(edgeLengthToUse, edgeLengthToUse);
return containAxes.ContainIntrinsicSize(
IntrinsicSize(edgeLengthToUse, edgeLengthToUse),
aFrame.GetWritingMode());
}
if (aUseMappedRatio && style.StylePosition()->mAspectRatio.HasRatio()) {
@ -700,7 +705,7 @@ static AspectRatio ComputeIntrinsicRatio(imgIContainer* aImage,
bool aUseMappedRatio,
const nsImageFrame& aFrame) {
const ComputedStyle& style = *aFrame.Style();
if (style.StyleDisplay()->IsContainSize()) {
if (style.StyleDisplay()->GetContainSizeAxes().IsAny()) {
return AspectRatio();
}
@ -1137,7 +1142,7 @@ bool nsImageFrame::IsForMarkerPseudo() const {
}
void nsImageFrame::EnsureIntrinsicSizeAndRatio() {
if (StyleDisplay()->IsContainSize()) {
if (StyleDisplay()->GetContainSizeAxes().IsBoth()) {
// If we have 'contain:size', then our intrinsic size and ratio are 0,0
// regardless of what our underlying image may think.
mIntrinsicSize = IntrinsicSize(0, 0);

View file

@ -618,7 +618,8 @@ nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) {
/* virtual */
IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
if (StyleDisplay()->IsContainSize()) {
const auto containAxes = StyleDisplay()->GetContainSizeAxes();
if (containAxes.IsBoth()) {
// Intrinsic size of 'contain:size' replaced elements is 0,0.
return IntrinsicSize(0, 0);
}
@ -628,7 +629,7 @@ IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
if (auto size = olc->GetSubdocumentIntrinsicSize()) {
// Use the intrinsic size from the child SVG document, if available.
return *size;
return containAxes.ContainIntrinsicSize(*size, GetWritingMode());
}
}
@ -641,7 +642,8 @@ IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
}
// We must be an HTML <iframe>. Return fallback size.
return IntrinsicSize(kFallbackIntrinsicSize);
return containAxes.ContainIntrinsicSize(IntrinsicSize(kFallbackIntrinsicSize),
GetWritingMode());
}
/* virtual */

View file

@ -162,6 +162,9 @@ class nsSubDocumentFrame final : public nsAtomicContainerFrame,
}
nscoord GetIntrinsicISize() {
if (StyleDisplay()->GetContainSizeAxes().mIContained) {
return 0;
}
auto size = GetIntrinsicSize();
Maybe<nscoord> iSize =
GetWritingMode().IsVertical() ? size.height : size.width;

View file

@ -306,7 +306,7 @@ void nsVideoFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
// Resolve our own BSize based on the controls' size in the
// same axis. Unless we're size-contained, in which case we
// have to behave as if we have an intrinsic size of 0.
if (aReflowInput.mStyleDisplay->IsContainSize()) {
if (aReflowInput.mStyleDisplay->GetContainSizeAxes().mBContained) {
contentBoxBSize = 0;
} else {
contentBoxBSize = myWM.IsOrthogonalTo(wm) ? kidDesiredSize.ISize(wm)
@ -389,20 +389,25 @@ nsIFrame::SizeComputationResult nsVideoFrame::ComputeSize(
nscoord nsVideoFrame::GetMinISize(gfxContext* aRenderingContext) {
nscoord result;
// Bind the result variable to a RAII-based debug object - the variable
// therefore must match the function's return value.
DISPLAY_MIN_INLINE_SIZE(this, result);
nsSize size = kFallbackIntrinsicSize;
nsSize size;
const auto wm = GetWritingMode();
if (HasVideoElement()) {
// This call handles size-containment
size = GetVideoIntrinsicSize();
} else {
const auto containAxes = StyleDisplay()->GetContainSizeAxes();
// We expect last and only child of audio elements to be control if
// "controls" attribute is present.
if (StyleDisplay()->IsContainSize() || !mFrames.LastChild()) {
if (containAxes.IsBoth() || !mFrames.LastChild()) {
size = nsSize();
} else {
size = containAxes.ContainSize(kFallbackIntrinsicSize, wm);
}
}
result = GetWritingMode().IsVertical() ? size.height : size.width;
result = wm.IsVertical() ? size.height : size.width;
return result;
}
@ -423,8 +428,8 @@ AspectRatio nsVideoFrame::GetIntrinsicRatio() const {
return AspectRatio();
}
// 'contain:size' replaced elements have no intrinsic ratio.
if (StyleDisplay()->IsContainSize()) {
// 'contain:[inline-]size' replaced elements have no intrinsic ratio.
if (StyleDisplay()->GetContainSizeAxes().IsAny()) {
return AspectRatio();
}
@ -470,23 +475,25 @@ nsSize nsVideoFrame::GetVideoIntrinsicSize() const {
return nsSize(0, 0);
}
const auto containAxes = StyleDisplay()->GetContainSizeAxes();
// 'contain:size' replaced elements have intrinsic size 0,0.
if (StyleDisplay()->IsContainSize()) {
if (containAxes.IsBoth()) {
return nsSize(0, 0);
}
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
if (Maybe<CSSIntSize> size = element->GetVideoSize()) {
return CSSPixel::ToAppUnits(*size);
return containAxes.ContainSize(CSSPixel::ToAppUnits(*size),
GetWritingMode());
}
if (ShouldDisplayPoster()) {
if (Maybe<nsSize> imgSize = PosterImageSize()) {
return *imgSize;
return containAxes.ContainSize(*imgSize, GetWritingMode());
}
}
return kFallbackIntrinsicSize;
return containAxes.ContainSize(kFallbackIntrinsicSize, GetWritingMode());
}
IntrinsicSize nsVideoFrame::GetIntrinsicSize() {

View file

@ -3525,3 +3525,40 @@ nscoord StyleCalcNode::Resolve(nscoord aBasis,
CoordPercentageRounder aRounder) const {
return ResolveInternal(aBasis, aRounder);
}
nsSize ContainSizeAxes::ContainSize(const nsSize& aUncontainedSize,
const WritingMode& aWM) const {
if (!IsAny()) {
return aUncontainedSize;
}
if (IsBoth()) {
return nsSize();
}
// At this point, we know that precisely one of our dimensions is contained.
const bool zeroWidth =
(!aWM.IsVertical() && mIContained) || (aWM.IsVertical() && mBContained);
if (zeroWidth) {
return nsSize(0, aUncontainedSize.Height());
}
return nsSize(aUncontainedSize.Width(), 0);
}
IntrinsicSize ContainSizeAxes::ContainIntrinsicSize(
const IntrinsicSize& aUncontainedSize, const WritingMode& aWM) const {
if (!IsAny()) {
return aUncontainedSize;
}
if (IsBoth()) {
return IntrinsicSize(0, 0);
}
// At this point, we know that precisely one of our dimensions is contained.
const bool zeroWidth =
(!aWM.IsVertical() && mIContained) || (aWM.IsVertical() && mBContained);
IntrinsicSize result(aUncontainedSize);
if (zeroWidth) {
result.width = Some(0);
} else {
result.height = Some(0);
}
return result;
}

View file

@ -12,6 +12,7 @@
#ifndef nsStyleStruct_h___
#define nsStyleStruct_h___
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/ServoStyleConstsInlines.h"
@ -39,6 +40,7 @@ struct nsStyleDisplay;
struct nsStyleVisibility;
namespace mozilla {
class ComputedStyle;
struct IntrinsicSize;
} // namespace mozilla
@ -66,6 +68,29 @@ inline Position Position::FromPercentage(float aPercent) {
LengthPercentage::FromPercentage(aPercent)};
}
/**
* Convenience struct for querying if a given box has size-containment in
* either axis.
*/
struct ContainSizeAxes {
ContainSizeAxes(bool aIContained, bool aBContained)
: mIContained(aIContained), mBContained(aBContained) {}
bool IsBoth() const { return mIContained && mBContained; }
bool IsAny() const { return mIContained || mBContained; }
/**
* Return a contained size from an uncontained size.
*/
nsSize ContainSize(const nsSize& aUncontainedSize,
const WritingMode& aWM) const;
IntrinsicSize ContainIntrinsicSize(const IntrinsicSize& aUncontainedSize,
const WritingMode& aWM) const;
const bool mIContained;
const bool mBContained;
};
} // namespace mozilla
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleFont {
@ -1179,6 +1204,11 @@ struct StyleAnimation {
} // namespace mozilla
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
private:
using StyleContain = mozilla::StyleContain;
using StyleContentVisibility = mozilla::StyleContentVisibility;
public:
explicit nsStyleDisplay(const mozilla::dom::Document&);
nsStyleDisplay(const nsStyleDisplay& aOther);
~nsStyleDisplay();
@ -1533,12 +1563,21 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
}
bool IsContainPaint() const {
return (mContain & mozilla::StyleContain::PAINT ||
!IsContentVisibilityVisible()) &&
!IsInternalRubyDisplayType() && !IsInternalTableStyleExceptCell();
const auto contain = EffectiveContainment();
// Short circuit for no containment whatsoever
if (!contain) {
return false;
}
return (contain & StyleContain::PAINT) && !IsInternalRubyDisplayType() &&
!IsInternalTableStyleExceptCell();
}
bool IsContainLayout() const {
const auto contain = EffectiveContainment();
// Short circuit for no containment whatsoever
if (!contain) {
return false;
}
// Note: The spec for layout containment says it should
// have no effect on non-atomic, inline-level boxes. We
// don't check for these here because we don't know
@ -1546,12 +1585,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
// responsible for checking if the box in question is
// non-atomic and inline-level, and creating an
// exemption as necessary.
return (mContain & mozilla::StyleContain::LAYOUT ||
!IsContentVisibilityVisible()) &&
!IsInternalRubyDisplayType() && !IsInternalTableStyleExceptCell();
return (contain & StyleContain::LAYOUT) && !IsInternalRubyDisplayType() &&
!IsInternalTableStyleExceptCell();
}
bool IsContainSize() const {
mozilla::ContainSizeAxes GetContainSizeAxes() const {
const auto contain = EffectiveContainment();
// Short circuit for no containment whatsoever
if (!contain) {
return mozilla::ContainSizeAxes(false, false);
}
// Note: The spec for size containment says it should
// have no effect on non-atomic, inline-level boxes. We
// don't check for these here because we don't know
@ -1559,19 +1602,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
// responsible for checking if the box in question is
// non-atomic and inline-level, and creating an
// exemption as necessary.
return (mContain & mozilla::StyleContain::SIZE ||
mContentVisibility == mozilla::StyleContentVisibility::Hidden) &&
!IsInternalRubyDisplayType() &&
DisplayInside() != mozilla::StyleDisplayInside::Table &&
!IsInnerTableStyle();
if (IsInternalRubyDisplayType() ||
DisplayInside() == mozilla::StyleDisplayInside::Table ||
IsInnerTableStyle()) {
return mozilla::ContainSizeAxes(false, false);
}
return mozilla::ContainSizeAxes(
static_cast<bool>(contain & StyleContain::INLINE_SIZE),
static_cast<bool>(contain & StyleContain::BLOCK_SIZE));
}
bool IsContentVisibilityVisible() const {
return mContentVisibility == mozilla::StyleContentVisibility::Visible;
return mContentVisibility == StyleContentVisibility::Visible;
}
bool IsContentVisibilityHidden() const {
return mContentVisibility == mozilla::StyleContentVisibility::Hidden;
return mContentVisibility == StyleContentVisibility::Hidden;
}
/* Returns whether the element has the transform property or a related
@ -1686,6 +1732,27 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
inline bool IsFixedPosContainingBlockForTransformSupportingFrames() const;
void GenerateCombinedIndividualTransform();
private:
StyleContain EffectiveContainment() const {
// content-visibility and container-type values implicitly enable some
// containment flags.
// FIXME(dshin, bug 1463600): Add in STYLE containment flag for `auto` &
// `hidden` when implemented
// FIXME(dshin, bug 1764640): Add in the effect of `container-type`
switch (mContentVisibility) {
case StyleContentVisibility::Visible:
// Most likely case.
return mContain;
case StyleContentVisibility::Auto:
return mContain | StyleContain::LAYOUT | StyleContain::PAINT;
case StyleContentVisibility::Hidden:
return mContain | StyleContain::LAYOUT | StyleContain::PAINT |
StyleContain::INLINE_SIZE | StyleContain::BLOCK_SIZE;
}
MOZ_ASSERT_UNREACHABLE("Invalid content visibility.");
return mContain;
}
};
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable {

View file

@ -90,9 +90,12 @@ SVGOuterSVGFrame::SVGOuterSVGFrame(ComputedStyle* aStyle,
// they're not really "replaced", and there's no outer context to contain sizes
// from leaking into). Hence, we check for a parent element before we bother
// testing for 'contain:size'.
static inline bool IsReplacedAndContainSize(const SVGOuterSVGFrame* aFrame) {
return aFrame->GetContent()->GetParent() &&
aFrame->StyleDisplay()->IsContainSize();
static inline ContainSizeAxes ContainSizeAxesIfApplicable(
const SVGOuterSVGFrame* aFrame) {
if (!aFrame->GetContent()->GetParent()) {
return ContainSizeAxes(false, false);
}
return aFrame->StyleDisplay()->GetContainSizeAxes();
}
void SVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
@ -171,7 +174,7 @@ nscoord SVGOuterSVGFrame::GetPrefISize(gfxContext* aRenderingContext) {
wm.IsVertical() ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]
: svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
if (IsReplacedAndContainSize(this)) {
if (ContainSizeAxesIfApplicable(this).mIContained) {
result = nscoord(0);
} else if (isize.IsPercentage()) {
// It looks like our containing block's isize may depend on our isize. In
@ -208,7 +211,8 @@ IntrinsicSize SVGOuterSVGFrame::GetIntrinsicSize() {
// XXXjwatt Note that here we want to return the CSS width/height if they're
// specified and we're embedded inside an nsIObjectLoadingContent.
if (IsReplacedAndContainSize(this)) {
const auto containAxes = ContainSizeAxesIfApplicable(this);
if (containAxes.IsBoth()) {
// Intrinsic size of 'contain:size' replaced elements is 0,0.
return IntrinsicSize(0, 0);
}
@ -233,12 +237,12 @@ IntrinsicSize SVGOuterSVGFrame::GetIntrinsicSize() {
intrinsicSize.height.emplace(std::max(val, 0));
}
return intrinsicSize;
return containAxes.ContainIntrinsicSize(intrinsicSize, GetWritingMode());
}
/* virtual */
AspectRatio SVGOuterSVGFrame::GetIntrinsicRatio() const {
if (IsReplacedAndContainSize(this)) {
if (ContainSizeAxesIfApplicable(this).IsAny()) {
return AspectRatio();
}

View file

@ -1267,22 +1267,26 @@ impl TouchAction {
bitflags! {
#[derive(MallocSizeOf, Parse, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
#[css(bitflags(single = "none,strict,content", mixed="size,layout,paint"))]
#[css(bitflags(single = "none,strict,content", mixed="size,layout,paint,inline-size", overlapping_bits))]
#[repr(C)]
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
pub struct Contain: u8 {
/// `none` variant, just for convenience.
const NONE = 0;
/// 'size' variant, turns on size containment
const SIZE = 1 << 0;
/// `inline-size` variant, turns on single-axis inline size containment
const INLINE_SIZE = 1 << 0;
/// `block-size` variant, turns on single-axis block size containment, internal only
const BLOCK_SIZE = 1 << 1;
/// `layout` variant, turns on layout containment
const LAYOUT = 1 << 1;
const LAYOUT = 1 << 2;
/// `paint` variant, turns on paint containment
const PAINT = 1 << 2;
const PAINT = 1 << 3;
/// `strict` variant, turns on all types of containment
const STRICT = 1 << 3 | Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
/// 'content' variant, turns on layout and paint containment
const CONTENT = 1 << 4 | Contain::LAYOUT.bits | Contain::PAINT.bits;
const STRICT = 1 << 4 | Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
/// `content` variant, turns on layout and paint containment
const CONTENT = 1 << 5 | Contain::LAYOUT.bits | Contain::PAINT.bits;
/// 'size' variant, turns on size containment
const SIZE = 1 << 6 | Contain::INLINE_SIZE.bits | Contain::BLOCK_SIZE.bits;
}
}

View file

@ -1,2 +0,0 @@
[contain-inline-size-bfc-floats-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-fieldset.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-flex.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-flexitem.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-grid.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-legend.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-multicol.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-regular-container.html]
expected: FAIL

View file

@ -0,0 +1,4 @@
[contain-inline-size-replaced.html]
[.inline-contained 23]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1764457
expected: FAIL

View file

@ -1,2 +0,0 @@
[contain-inline-size-vertical-rl-.html]
expected: FAIL

View file

@ -11,8 +11,5 @@
[Property contain value 'size style layout paint']
expected: FAIL
[Property contain value 'inline-size']
expected: FAIL
[Property contain value 'inline-size layout style paint']
expected: FAIL

View file

@ -11,8 +11,5 @@
[e.style['contain'\] = "layout paint style size" should set the property value]
expected: FAIL
[e.style['contain'\] = "inline-size" should set the property value]
expected: FAIL
[e.style['contain'\] = "layout inline-size" should set the property value]
expected: FAIL

View file

@ -0,0 +1,98 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Containment Test: intrinsic size of inline-size-contained replaced elems</title>
<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="help" href="https://drafts.csswg.org/css-contain/#containment-size">
<meta name=assert
content="This test checks that various replaced elements with contain: inline-size have an intrinsic inline size of 0 regardless of their content.">
<style>
.inline-contained {
contain: inline-size;
width: auto;
height: auto;
border: none;
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<body onload="checkLayout('.inline-contained')">
<div>
<!-- video element: -->
<video class="inline-contained" data-expected-width="0" data-expected-height="150"></video>
<video class="inline-contained" data-expected-width="0" data-expected-height="150" controls></video>
<video class="inline-contained" data-expected-width="0" data-expected-height="100"
poster="support/blue-100x100.png"></video>
<video class="inline-contained" data-expected-width="0" data-expected-height="100" poster="support/blue-100x100.png"
controls></video>
<video class="inline-contained" data-expected-width="0" data-expected-height="240" src="support/white.webm"
controls></video>
<video class="inline-contained" data-expected-width="0" data-expected-height="240" src="support/white.webm"
controls></video>
<br>
<!-- audio element with controls, and a few other misc replaced elements: -->
<audio class="inline-contained" data-expected-width="0" data-expected-height="40" controls></audio>
<canvas class="inline-contained" data-expected-width="0" data-expected-height="150"></canvas>
<svg class="inline-contained" data-expected-bounding-client-rect-width="0"
data-expected-bounding-client-rect-height="150"></svg>
<br>
<!-- Image elements: -->
<img class="inline-contained" data-expected-width="0" data-expected-height="24" src="broken">
<img class="inline-contained" data-expected-width="0" data-expected-height="100" src="support/blue-100x100.png">
<picture>
<source srcset="support/blue-100x100.png"><img class="inline-contained" data-expected-width="0"
data-expected-height="100">
</picture>
<br>
<!-- Document-embedding elements (with a target resource that
could provide an intrinsic ratio in some cases, in the absence of
contain:inline-size): -->
<embed class="inline-contained" data-expected-width="0" data-expected-height="100" src="support/blue-100x100.png">
<object class="inline-contained" data-expected-width="0" data-expected-height="100"
data="support/blue-100x100.png"></object>
<iframe class="inline-contained" data-expected-width="0" data-expected-height="150"></iframe>
<iframe class="inline-contained" data-expected-width="0" data-expected-height="150"
src="support/blue-100x100.png"></iframe>
<br>
</div>
<!-- Same, but in vertical mode -->
<div style="writing-mode: vertical-rl;">
<video class="inline-contained" data-expected-width="300" data-expected-height="0"></video>
<video class="inline-contained" data-expected-width="300" data-expected-height="0" controls></video>
<video class="inline-contained" data-expected-width="100" data-expected-height="0"
poster="support/blue-100x100.png"></video>
<video class="inline-contained" data-expected-width="100" data-expected-height="0" poster="support/blue-100x100.png"
controls></video>
<video class="inline-contained" data-expected-width="320" data-expected-height="0" src="support/white.webm"
controls></video>
<video class="inline-contained" data-expected-width="320" data-expected-height="0" src="support/white.webm"
controls></video>
<audio class="inline-contained" data-expected-width="300" data-expected-height="0" controls></audio>
<canvas class="inline-contained" data-expected-width="300" data-expected-height="0"></canvas>
<svg class="inline-contained" data-expected-bounding-client-rect-width="300"
data-expected-bounding-client-rect-height="0"></svg>
<br>
<img class="inline-contained" data-expected-width="24" data-expected-height="0" src="broken">
<img class="inline-contained" data-expected-width="100" data-expected-height="0" src="support/blue-100x100.png">
<picture>
<source srcset="support/blue-100x100.png"><img class="inline-contained" data-expected-width="100"
data-expected-height="0">
</picture>
<br>
<embed class="inline-contained" data-expected-width="100" data-expected-height="0" src="support/blue-100x100.png">
<object class="inline-contained" data-expected-width="100" data-expected-height="0"
data="support/blue-100x100.png"></object>
<iframe class="inline-contained" data-expected-width="300" data-expected-height="0"></iframe>
<iframe class="inline-contained" data-expected-width="300" data-expected-height="0"
src="support/blue-100x100.png"></iframe>
<br>
</div>
</body>