Bug 1742042 Part 4 - Apply flex item's stretched cross-size when computing flex container's intrinsic inline size. r=dholbert

For flex items with an aspect-ratio, the expectation is that the stretched
cross-sizes can be transferred to the main axis, affecting their intrinsic
inline-size contribution to the flex container's intrinsic inline-size.

Add `aspect-ratio-intrinsic-size-008.html` (adapted from 003.html) to test a
flex item with border and padding since the existing tests do not cover this.

Differential Revision: https://phabricator.services.mozilla.com/D222050
This commit is contained in:
Ting-Yu Lin 2024-09-13 06:52:13 +00:00
parent 3550beeb50
commit 0f96ec1864
15 changed files with 134 additions and 40 deletions

View File

@ -4530,7 +4530,8 @@ static void AddStateBitToAncestors(nsIFrame* aFrame, nsFrameState aBit) {
nscoord nsLayoutUtils::IntrinsicForAxis(
PhysicalAxis aAxis, gfxContext* aRenderingContext, nsIFrame* aFrame,
IntrinsicISizeType aType, const Maybe<LogicalSize>& aPercentageBasis,
uint32_t aFlags, nscoord aMarginBoxMinSizeClamp) {
uint32_t aFlags, nscoord aMarginBoxMinSizeClamp,
const StyleSizeOverrides& aSizeOverrides) {
MOZ_ASSERT(aFrame, "null frame");
MOZ_ASSERT(aFrame->GetParent(),
"IntrinsicForAxis called on frame not in tree");
@ -4549,23 +4550,28 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
// its parent, we'll need to look at its BSize instead of min/pref-ISize.
const nsStylePosition* stylePos = aFrame->StylePosition();
StyleBoxSizing boxSizing = stylePos->mBoxSizing;
PhysicalAxis ourInlineAxis =
aFrame->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
const bool isInlineAxis = aAxis == ourInlineAxis;
StyleSize styleMinISize =
horizontalAxis ? stylePos->mMinWidth : stylePos->mMinHeight;
StyleSize styleISize =
(aFlags & MIN_INTRINSIC_ISIZE)
? styleMinISize
: (horizontalAxis ? stylePos->mWidth : stylePos->mHeight);
StyleSize styleISize = [&]() {
if (aFlags & MIN_INTRINSIC_ISIZE) {
return styleMinISize;
}
const Maybe<StyleSize>& styleISizeOverride =
isInlineAxis ? aSizeOverrides.mStyleISize : aSizeOverrides.mStyleBSize;
return styleISizeOverride
? *styleISizeOverride
: (horizontalAxis ? stylePos->mWidth : stylePos->mHeight);
}();
MOZ_ASSERT(!(aFlags & MIN_INTRINSIC_ISIZE) || styleISize.IsAuto() ||
nsIFrame::ToExtremumLength(styleISize),
"should only use MIN_INTRINSIC_ISIZE for intrinsic values");
StyleMaxSize styleMaxISize =
horizontalAxis ? stylePos->mMaxWidth : stylePos->mMaxHeight;
PhysicalAxis ourInlineAxis =
aFrame->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
const bool isInlineAxis = aAxis == ourInlineAxis;
auto ResetIfKeywords = [](StyleSize& aSize, StyleSize& aMinSize,
StyleMaxSize& aMaxSize) {
if (!aSize.IsLengthPercentage()) {
@ -4616,7 +4622,12 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
// represents the ratio-determining axis of |aFrame|. It could be the inline
// axis or the block axis of |aFrame|. (So we are calculating the size
// along the ratio-dependent axis in this if-branch.)
StyleSize styleBSize = horizontalAxis ? stylePos->mHeight : stylePos->mWidth;
const Maybe<StyleSize>& styleBSizeOverride =
isInlineAxis ? aSizeOverrides.mStyleBSize : aSizeOverrides.mStyleISize;
StyleSize styleBSize =
styleBSizeOverride
? *styleBSizeOverride
: (horizontalAxis ? stylePos->mHeight : stylePos->mWidth);
StyleSize styleMinBSize =
horizontalAxis ? stylePos->mMinHeight : stylePos->mMinWidth;
StyleMaxSize styleMaxBSize =
@ -4859,10 +4870,6 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
(nsIFrame::IsIntrinsicKeyword(styleISize) ||
nsIFrame::IsIntrinsicKeyword(styleMinISize) ||
nsIFrame::IsIntrinsicKeyword(styleMaxISize))) {
// This 'B' in |styleBSize| means the block size of |aFrame|. We go into
// this branch only if |aAxis| is the inline axis of |aFrame|.
const StyleSize& styleBSize =
horizontalAxis ? stylePos->mHeight : stylePos->mWidth;
if (Maybe<nscoord> bSize = GetBSize(styleBSize)) {
// We cannot reuse |boxSizing| because it may be updated to content-box
// in the above if-branch.
@ -4898,13 +4905,14 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
/* static */
nscoord nsLayoutUtils::IntrinsicForContainer(
gfxContext* aRenderingContext, nsIFrame* aFrame, IntrinsicISizeType aType,
const Maybe<LogicalSize>& aPercentageBasis, uint32_t aFlags) {
const Maybe<LogicalSize>& aPercentageBasis, uint32_t aFlags,
const StyleSizeOverrides& aSizeOverrides) {
MOZ_ASSERT(aFrame && aFrame->GetParent());
// We want the size aFrame will contribute to its parent's inline-size.
PhysicalAxis axis =
aFrame->GetParent()->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType,
aPercentageBasis, aFlags);
aPercentageBasis, aFlags, NS_MAXSIZE, aSizeOverrides);
}
/* static */

View File

@ -16,6 +16,7 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/layers/ScrollableLayerGuid.h"
#include "mozilla/LayoutStructs.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
@ -1470,6 +1471,8 @@ class nsLayoutUtils {
* @param aMarginBoxMinSizeClamp make the result fit within this margin-box
* size by reducing the *content size* (flooring at zero). This is used for:
* https://drafts.csswg.org/css-grid/#min-size-auto
* @param aSizeOverrides optional override values for size properties, which
* this function will use internally instead of the actual property values.
*/
enum {
IGNORE_PADDING = 0x01,
@ -1480,7 +1483,8 @@ class nsLayoutUtils {
mozilla::PhysicalAxis aAxis, gfxContext* aRenderingContext,
nsIFrame* aFrame, mozilla::IntrinsicISizeType aType,
const mozilla::Maybe<LogicalSize>& aPercentageBasis = mozilla::Nothing(),
uint32_t aFlags = 0, nscoord aMarginBoxMinSizeClamp = NS_MAXSIZE);
uint32_t aFlags = 0, nscoord aMarginBoxMinSizeClamp = NS_MAXSIZE,
const mozilla::StyleSizeOverrides& aSizeOverrides = {});
/**
* Calls IntrinsicForAxis with aFrame's parent's inline physical axis.
*/
@ -1488,7 +1492,8 @@ class nsLayoutUtils {
gfxContext* aRenderingContext, nsIFrame* aFrame,
mozilla::IntrinsicISizeType aType,
const mozilla::Maybe<LogicalSize>& aPercentageBasis = mozilla::Nothing(),
uint32_t aFlags = 0);
uint32_t aFlags = 0,
const mozilla::StyleSizeOverrides& aSizeOverrides = {});
/**
* Get the definite size contribution of aFrame for the given physical axis.

View File

@ -3863,6 +3863,7 @@ void FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize) {
// We stretch IFF we are align-self:stretch, have no auto margins in
// cross axis, and have cross-axis size property == "auto". If any of those
// conditions don't hold up, we won't stretch.
// https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch
if (mAlignSelf._0 != StyleAlignFlags::STRETCH ||
NumAutoMarginsInCrossAxis() != 0 || !IsCrossSizeAuto()) {
return;
@ -6433,6 +6434,8 @@ nscoord nsFlexContainerFrame::ComputeIntrinsicISize(
const bool useMozBoxCollapseBehavior =
StyleVisibility()->UseLegacyCollapseBehavior();
const bool isSingleLine = StyleFlexWrap::Nowrap == stylePos->mFlexWrap;
const auto flexWM = GetWritingMode();
// The loop below sets aside space for a gap before each item besides the
// first. This bool helps us handle that special-case.
@ -6451,11 +6454,79 @@ nscoord nsFlexContainerFrame::ComputeIntrinsicISize(
continue;
}
const IntrinsicSizeInput childInput(aInput, childFrame->GetWritingMode(),
GetWritingMode());
const auto childWM = childFrame->GetWritingMode();
const IntrinsicSizeInput childInput(aInput, childWM, flexWM);
const auto* childStylePos =
nsLayoutUtils::GetStyleFrame(childFrame)->StylePosition();
// A flex item with a preferred aspect-ratio and a definite size in the flex
// container's block axis can transfer its block size to the inline axis,
// affecting its intrinsic inline size contribution to the flex container's
// intrinsic inline size. This helper function determines whether we should
// "pre-stretch" a flex item's cross-size (with that size considered to be
// definite) based on the flex container's definite cross-size.
//
// Note: The logic here is similar to the "pre-stretch" in
// GenerateFlexItemForChild(), except that we do not construct a full
// FlexItem object.
const bool childShouldStretchCrossSize = [&]() {
if (!isSingleLine || axisTracker.IsColumnOriented()) {
// We only perform "pre-stretch" for the item's cross-size if the flex
// container is single-line and row-oriented.
return false;
}
if (!aInput.mPercentageBasisForChildren ||
aInput.mPercentageBasisForChildren->BSize(flexWM) ==
NS_UNCONSTRAINEDSIZE) {
// The flex container does not have a definite cross-size to stretch the
// items.
//
// Note: if the flex container has a definite cross-size (for items to
// pre-stretch to fill), it should be passed down in
// mPercentageBasisForChildren -- specifically in the BSize component,
// given that we know the flex container is row-oriented at this point.
return false;
}
const StyleAlignFlags alignSelf =
childStylePos->UsedAlignSelf(Style())._0;
if ((alignSelf != StyleAlignFlags::STRETCH &&
alignSelf != StyleAlignFlags::NORMAL) ||
childFrame->StyleMargin()->HasBlockAxisAuto(flexWM) ||
!childStylePos->BSize(flexWM).IsAuto()) {
// Similar to FlexItem::ResolveStretchedCrossSize(), we only stretch
// the item if it satisfies all the following conditions:
// - align-self: stretch or align-self: normal (which behaves as
// stretch) https://drafts.csswg.org/css-align-3/#align-flex
// - no auto margins in the cross axis
// - a cross-axis size property of value "auto"
// https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch
return false;
}
// Let's stretch the item's cross-size.
return true;
}();
StyleSizeOverrides sizeOverrides;
if (childShouldStretchCrossSize) {
nscoord stretchedCrossSize =
aInput.mPercentageBasisForChildren->BSize(flexWM);
if (childStylePos->mBoxSizing == StyleBoxSizing::Content) {
const nscoord mbp =
childFrame->IntrinsicBSizeOffsets().MarginBorderPadding();
stretchedCrossSize = std::max(0, stretchedCrossSize - mbp);
}
const auto stretchedStyleCrossSize = StyleSize::LengthPercentage(
LengthPercentage::FromAppUnits(stretchedCrossSize));
// The size override is in the child's own writing mode.
if (flexWM.IsOrthogonalTo(childWM)) {
sizeOverrides.mStyleISize.emplace(stretchedStyleCrossSize);
} else {
sizeOverrides.mStyleBSize.emplace(stretchedStyleCrossSize);
}
}
nscoord childISize = nsLayoutUtils::IntrinsicForContainer(
childInput.mContext, childFrame, aType,
childInput.mPercentageBasisForChildren);
childInput.mPercentageBasisForChildren, 0, sizeOverrides);
// * For a row-oriented single-line flex container, the intrinsic
// {min/pref}-isize is the sum of its items' {min/pref}-isizes and
@ -6464,7 +6535,6 @@ nscoord nsFlexContainerFrame::ComputeIntrinsicISize(
// is the max of its items' min isizes.
// * For a row-oriented multi-line flex container, the intrinsic
// pref isize is former (sum), and its min isize is the latter (max).
bool isSingleLine = (StyleFlexWrap::Nowrap == stylePos->mFlexWrap);
if (axisTracker.IsRowOriented() &&
(isSingleLine || aType == IntrinsicISizeType::PrefISize)) {
containerISize += childISize;

View File

@ -2815,6 +2815,7 @@ class nsIFrame : public nsQueryFrame {
nscoord border = 0;
nscoord margin = 0;
nscoord BorderPadding() const { return border + padding; };
nscoord MarginBorderPadding() const { return margin + border + padding; }
};
/**

View File

@ -256,8 +256,8 @@ nscoord nsTableWrapperFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
// margin-box inline size as the contribution in the inline axis.
const IntrinsicSizeOffsetData offset =
InnerTableFrame()->IntrinsicISizeOffsets();
const nscoord innerTableMinISize = InnerTableFrame()->GetMinISize(input) +
offset.BorderPadding() + offset.margin;
const nscoord innerTableMinISize =
InnerTableFrame()->GetMinISize(input) + offset.MarginBorderPadding();
iSize = std::max(iSize, innerTableMinISize);
}

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-001.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-002.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-003.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-004.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-005.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-006.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[flexbox-min-width-auto-005.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[flexbox-min-width-auto-006.html]
expected: FAIL

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes">
<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="This checks that a flex item with an aspect-ratio transfers its size when it also has stretch alignment.">
<p>Test passes if there is a filled green square.</p>
<div style="display: inline-flex; height: 100px; background: green;">
<div style="padding: 3px; border: 7px solid green; margin: 10px;
aspect-ratio: 1/1;"></div>
</div>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes">
<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="This checks that a flex item with an aspect-ratio transfers its size when it also has stretch alignment.">
<p>Test passes if there is a filled green square.</p>
<div style="display: inline-flex; height: 100px; background: green;">
<div style="padding: 3px; border: 7px solid green; margin: 10px;
aspect-ratio: 1/1; writing-mode: vertical-rl;"></div>
</div>