Bug 1690422 - Resolve {min|max}-content contribution with the preferred aspect ratio and the definite block size. r=TYLin

This is simliar with the previous patch, but we use
min-content/max-content for the inner div (i.e. child frame).

Perhaps we could remove the simliar implementation from
ComputeISizeValue(). However, I still keep it because it can avoid some
extra work when resolving {min|max}-content with aspect-ratio when
reflowing.

Differential Revision: https://phabricator.services.mozilla.com/D104476
This commit is contained in:
Boris Chiou 2021-02-17 02:41:24 +00:00
Родитель 181e91cf22
Коммит d9de5c2358
5 изменённых файлов: 176 добавлений и 30 удалений

Просмотреть файл

@ -4478,6 +4478,7 @@ static nscoord GetDefiniteSizeTakenByBoxSizing(
enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
static bool GetIntrinsicCoord(StyleExtremumLength aStyle,
gfxContext* aRenderingContext, nsIFrame* aFrame,
Maybe<nscoord> aInlineSizeFromAspectRatio,
eWidthProperty aProperty, nscoord& aResult) {
MOZ_ASSERT(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH ||
aProperty == PROP_MIN_WIDTH,
@ -4502,23 +4503,27 @@ static bool GetIntrinsicCoord(StyleExtremumLength aStyle,
// wrapping inside of it should not apply font size inflation.
AutoMaybeDisableFontInflation an(aFrame);
if (aStyle == StyleExtremumLength::MaxContent)
if (aInlineSizeFromAspectRatio) {
aResult = *aInlineSizeFromAspectRatio;
} else if (aStyle == StyleExtremumLength::MaxContent) {
aResult = aFrame->GetPrefISize(aRenderingContext);
else
} else {
aResult = aFrame->GetMinISize(aRenderingContext);
}
return true;
}
template <typename SizeOrMaxSize>
static bool GetIntrinsicCoord(const SizeOrMaxSize& aStyle,
gfxContext* aRenderingContext, nsIFrame* aFrame,
Maybe<nscoord> aInlineSizeFromAspectRatio,
eWidthProperty aProperty, nscoord& aResult) {
if (!aStyle.IsExtremumLength()) {
return false;
}
return GetIntrinsicCoord(aStyle.AsExtremumLength(), aRenderingContext, aFrame,
aProperty, aResult);
aInlineSizeFromAspectRatio, aProperty, aResult);
}
#undef DEBUG_INTRINSIC_WIDTH
@ -4545,6 +4550,10 @@ static int32_t gNoiseIndent = 0;
* @param aFixedMaxSize if aStyleMaxSize is a definite size then this points to
* the value, otherwise nullptr
* @param aStyleMaxSize a 'max-width' or 'max-height' property value
* @param aInlineSizeFromAspectRatio the content-box inline size computed from
* aspect-ratio and the definite block size.
* We use this value to resolve
* {min|max}-content.
* @param aFlags same as for IntrinsicForContainer
* @param aContainerWM the container's WM
*/
@ -4554,7 +4563,9 @@ static nscoord AddIntrinsicSizeOffset(
StyleBoxSizing aBoxSizing, nscoord aContentSize, nscoord aContentMinSize,
const StyleSize& aStyleSize, const nscoord* aFixedMinSize,
const StyleSize& aStyleMinSize, const nscoord* aFixedMaxSize,
const StyleMaxSize& aStyleMaxSize, uint32_t aFlags, PhysicalAxis aAxis) {
const StyleMaxSize& aStyleMaxSize,
Maybe<nscoord> aInlineSizeFromAspectRatio, uint32_t aFlags,
PhysicalAxis aAxis) {
nscoord result = aContentSize;
nscoord min = aContentMinSize;
nscoord coordOutsideSize = 0;
@ -4584,13 +4595,14 @@ static nscoord AddIntrinsicSizeOffset(
result = 0; // let |min| handle padding/border/margin
} else if (GetAbsoluteCoord(aStyleSize, size) ||
GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame,
PROP_WIDTH, size)) {
aInlineSizeFromAspectRatio, PROP_WIDTH, size)) {
result = size + coordOutsideSize;
}
nscoord maxSize = aFixedMaxSize ? *aFixedMaxSize : 0;
if (aFixedMaxSize || GetIntrinsicCoord(aStyleMaxSize, aRenderingContext,
aFrame, PROP_MAX_WIDTH, maxSize)) {
if (aFixedMaxSize ||
GetIntrinsicCoord(aStyleMaxSize, aRenderingContext, aFrame,
aInlineSizeFromAspectRatio, PROP_MAX_WIDTH, maxSize)) {
maxSize += coordOutsideSize;
if (result > maxSize) {
result = maxSize;
@ -4598,8 +4610,9 @@ static nscoord AddIntrinsicSizeOffset(
}
nscoord minSize = aFixedMinSize ? *aFixedMinSize : 0;
if (aFixedMinSize || GetIntrinsicCoord(aStyleMinSize, aRenderingContext,
aFrame, PROP_MIN_WIDTH, minSize)) {
if (aFixedMinSize ||
GetIntrinsicCoord(aStyleMinSize, aRenderingContext, aFrame,
aInlineSizeFromAspectRatio, PROP_MIN_WIDTH, minSize)) {
minSize += coordOutsideSize;
if (result < minSize) {
result = minSize;
@ -4749,6 +4762,28 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
: aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
auto getContentBoxSizeToBoxSizingAdjust =
[childWM, &offsets, &aFrame, isInlineAxis,
pmPercentageBasis](const StyleBoxSizing aBoxSizing) {
return aBoxSizing == StyleBoxSizing::Border
? LogicalSize(childWM,
(isInlineAxis ? offsets
: aFrame->IntrinsicISizeOffsets(
pmPercentageBasis))
.BorderPadding(),
(!isInlineAxis ? offsets
: aFrame->IntrinsicBSizeOffsets(
pmPercentageBasis))
.BorderPadding())
: LogicalSize(childWM);
};
Maybe<nscoord> inlineSizeFromAspectRatio;
Maybe<LogicalSize> contentBoxSizeToBoxSizingAdjust;
const bool ignorePadding =
(aFlags & IGNORE_PADDING) || aFrame->IsAbsolutelyPositioned();
// If we have a specified width (or a specified 'min-width' greater
// than the specified 'max-width', which works out to the same thing),
// don't even bother getting the frame's intrinsic width, because in
@ -4805,10 +4840,15 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
// Handle elements with an intrinsic ratio (or size) and a specified
// height, min-height, or max-height.
// NOTE: We treat "min-height:auto" as "0" for the purpose of this code,
// NOTE:
// 1. We treat "min-height:auto" as "0" for the purpose of this code,
// since that's what it means in all cases except for on flex items -- and
// even there, we're supposed to ignore it (i.e. treat it as 0) until the
// flex container explicitly considers it.
// 2. The 'B' in |styleBSize|, |styleMinBSize|, and |styleMaxBSize|
// 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;
StyleSize styleMinBSize =
@ -4835,23 +4875,10 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
AddStateBitToAncestors(
aFrame, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
const bool ignorePadding =
(aFlags & IGNORE_PADDING) || aFrame->IsAbsolutelyPositioned();
nscoord bSizeTakenByBoxSizing = GetDefiniteSizeTakenByBoxSizing(
boxSizing, aFrame, !isInlineAxis, ignorePadding, aPercentageBasis);
auto contentBoxSizeToBoxSizingAdjust =
boxSizing == StyleBoxSizing::Border
? LogicalSize(
childWM,
(isInlineAxis
? offsets
: aFrame->IntrinsicISizeOffsets(pmPercentageBasis))
.BorderPadding(),
(!isInlineAxis
? offsets
: aFrame->IntrinsicBSizeOffsets(pmPercentageBasis))
.BorderPadding())
: LogicalSize(childWM);
contentBoxSizeToBoxSizingAdjust.emplace(
getContentBoxSizeToBoxSizingAdjust(boxSizing));
// NOTE: This is only the minContentSize if we've been passed
// MIN_INTRINSIC_ISIZE (which is fine, because this should only be used
// inside a check for that flag).
@ -4866,7 +4893,10 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
// dimensions of |aFrame|.
result = ratio.ComputeRatioDependentSize(
isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h,
contentBoxSizeToBoxSizingAdjust);
*contentBoxSizeToBoxSizingAdjust);
// We have get the inlineSizeForAspectRatio value, so we don't have to
// recompute this again below.
inlineSizeFromAspectRatio.emplace(result);
}
if (GetDefiniteSize(styleMaxBSize, aFrame, !isInlineAxis,
@ -4876,7 +4906,7 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
h = std::max(0, h - bSizeTakenByBoxSizing);
nscoord maxISize = ratio.ComputeRatioDependentSize(
isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h,
contentBoxSizeToBoxSizingAdjust);
*contentBoxSizeToBoxSizingAdjust);
if (maxISize < result) {
result = maxISize;
}
@ -4892,7 +4922,7 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
h = std::max(0, h - bSizeTakenByBoxSizing);
nscoord minISize = ratio.ComputeRatioDependentSize(
isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h,
contentBoxSizeToBoxSizingAdjust);
*contentBoxSizeToBoxSizingAdjust);
if (minISize > result) {
result = minISize;
}
@ -4916,11 +4946,49 @@ nscoord nsLayoutUtils::IntrinsicForAxis(
min = aFrame->GetMinISize(aRenderingContext);
}
// If we have an aspect-ratio and a definite block size of |aFrame|, we
// resolve the {min|max}-content size by the aspect-ratio and the block size.
// If |aAxis| is not the inline axis of |aFrame|, {min|max}-content should
// behaves as auto, so we don't need this.
// https://github.com/w3c/csswg-drafts/issues/5032
// FIXME: Bug 1670151: Use GetAspectRatio() to cover replaced elements (and
// then we can drop the check of eSupportsAspectRatio).
const AspectRatio ar = stylePos->mAspectRatio.ToLayoutRatio();
if (isInlineAxis && styleISize.IsExtremumLength() && ar &&
aFrame->IsFrameOfType(nsIFrame::eSupportsAspectRatio) &&
!inlineSizeFromAspectRatio) {
// 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;
nscoord bSize;
if (GetDefiniteSize(styleBSize, aFrame, !isInlineAxis, aPercentageBasis,
&bSize) ||
(aPercentageBasis.isNothing() &&
GetPercentBSize(styleBSize, aFrame, horizontalAxis, bSize))) {
// We cannot reuse |boxSizing| because it may be updated to content-box
// in the above if-branch.
const StyleBoxSizing boxSizingForAR = stylePos->mBoxSizing;
if (!contentBoxSizeToBoxSizingAdjust) {
contentBoxSizeToBoxSizingAdjust.emplace(
getContentBoxSizeToBoxSizingAdjust(boxSizingForAR));
}
nscoord bSizeTakenByBoxSizing =
GetDefiniteSizeTakenByBoxSizing(boxSizingForAR, aFrame, !isInlineAxis,
ignorePadding, aPercentageBasis);
bSize -= bSizeTakenByBoxSizing;
inlineSizeFromAspectRatio.emplace(ar.ComputeRatioDependentSize(
LogicalAxis::eLogicalAxisInline, childWM, bSize,
*contentBoxSizeToBoxSizingAdjust));
}
}
nscoord contentBoxSize = result;
result = AddIntrinsicSizeOffset(
aRenderingContext, aFrame, offsets, aType, boxSizing, result, min,
styleISize, haveFixedMinISize ? &minISize : nullptr, styleMinISize,
haveFixedMaxISize ? &maxISize : nullptr, styleMaxISize, aFlags, aAxis);
haveFixedMaxISize ? &maxISize : nullptr, styleMaxISize,
inlineSizeFromAspectRatio, aFlags, aAxis);
nscoord overflow = result - aMarginBoxMinSizeClamp;
if (MOZ_UNLIKELY(overflow > 0)) {
nscoord newContentBoxSize = std::max(nscoord(0), contentBoxSize - overflow);
@ -5050,9 +5118,11 @@ nscoord nsLayoutUtils::MinSizeContributionForAxis(
: aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
nscoord result = 0;
nscoord min = 0;
// Note: aInlineSizeFromAspectRatio is Nothing() here because we don't handle
// "content size" cases here (i.e. |fixedMinSize| is false here).
result = AddIntrinsicSizeOffset(
aRC, aFrame, offsets, aType, stylePos->mBoxSizing, result, min, size,
fixedMinSize, size, nullptr, maxSize, aFlags, aAxis);
fixedMinSize, size, nullptr, maxSize, Nothing(), aFlags, aAxis);
#ifdef DEBUG_INTRINSIC_WIDTH
nsIFrame::IndentBy(stderr, gNoiseIndent);

Просмотреть файл

@ -0,0 +1,24 @@
<!DOCTYPE html>
<title>CSS aspect-ratio: grid track size should respect aspect-ratio when using
intrinsic size keywords in grid items</title>
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-track-sizing">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<style>
#reference-overlapped-red {
position: absolute;
background-color: red;
width: 100px;
height: 100px;
z-index: -1;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div id="reference-overlapped-red"></div>
<div style="display: grid; grid-template-columns: auto; width: min-content; background: green;">
<div style="height: 50px; width: min-content; aspect-ratio: 2/1;"></div>
<div style="height: 50px; width: 1%; min-width: min-content; aspect-ratio: 2/1;"></div>
</div>

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE html>
<title>CSS aspect-ratio: grid track size should respect aspect-ratio when using
intrinsic size keywords in grid items with orthogonal writing mode</title>
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://drafts.csswg.org/css-grid/#algo-track-sizing">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<style>
#reference-overlapped-red {
position: absolute;
background-color: red;
width: 100px;
height: 100px;
z-index: -1;
}
.item {
aspect-ratio: 2/1;
writing-mode: vertical-lr;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div id="reference-overlapped-red"></div>
<div style="display: grid; grid-template-columns: auto; width: min-content; background: green;">
<div class="item" style="width: 100px; height: min-content;"></div>
<div class="item" style="width: 100px; height: 1%; min-height: min-content;"></div>
</div>

Просмотреть файл

@ -0,0 +1,12 @@
<!DOCTYPE html>
<title>CSS aspect-ratio: min-content size keyword together with min-content size contribution</title>
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5032">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width: min-content; background: green;">
<div style="width: min-content; height: 100px; aspect-ratio: 1/1;"></div>
</div>

Просмотреть файл

@ -0,0 +1,12 @@
<!DOCTYPE html>
<title>CSS aspect-ratio: max-content size keyword together with min-content size contribution</title>
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5032">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width: max-content; background: green;">
<div style="width: min-content; height: 100px; aspect-ratio: 1/1;"></div>
</div>