Skip to content
Permalink
Browse files
[css-aspect-ratio] The transferred min/max sizes should be constraine…
…d by defined sizes

https://bugs.webkit.org/show_bug.cgi?id=243321

Reviewed by Rob Buis.

The aspect-ratio transferred min/max values should be capped/floored by the definite preferred or min/max sizes, see [1].
This patch added constrainLogicalMinMaxSizesByAspectRatio to handle this scenario. This function also handles the constraint
to the automatic content-based minimum sizes in the ratio-dependent axis which should be capped by the maximum size [2].

[1] https://www.w3.org/TR/css-sizing-4/#aspect-ratio-size-transfers
[2] https://www.w3.org/TR/css-sizing-4/#aspect-ratio-minimum

* LayoutTests/TestExpectations:
* Source/WebCore/rendering/RenderBox.cpp:
(WebCore::RenderBox::constrainLogicalMinMaxSizesByAspectRatio const):
(WebCore::RenderBox::constrainLogicalWidthInFragmentByMinMax const):
(WebCore::RenderBox::constrainLogicalHeightByMinMax const):
(WebCore::RenderBox::computeMinMaxLogicalWidthFromAspectRatio const):
(WebCore::RenderBox::computeMinMaxLogicalHeightFromAspectRatio const):
* Source/WebCore/rendering/RenderBox.h:

Canonical link: https://commits.webkit.org/253262@main
  • Loading branch information
cathiechen committed Aug 9, 2022
1 parent 464b05e commit 94a7be7787d38909554344152db793c2c361eb17
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 32 deletions.
@@ -4688,12 +4688,6 @@ webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/slotted-plac
# aspect-ratio (some of these rely on contain-intrinsic-size)
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/abspos-014.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/abspos-020.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-038.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-039.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-041.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-042.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-043.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/block-aspect-ratio-045.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/flex-aspect-ratio-002.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/flex-aspect-ratio-004.html [ ImageOnlyFailure ]
webkit.org/b/214463 imported/w3c/web-platform-tests/css/css-sizing/aspect-ratio/flex-aspect-ratio-025.html [ ImageOnlyFailure ]
@@ -672,49 +672,101 @@ void RenderBox::applyTransform(TransformationMatrix& t, const RenderStyle& style
style.applyTransform(t, boundingBox, options);
}

LayoutUnit RenderBox::constrainLogicalWidthInFragmentByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock& cb, RenderFragmentContainer* fragment, AllowIntrinsic allowIntrinsic) const
void RenderBox::constrainLogicalMinMaxSizesByAspectRatio(LayoutUnit& computedMinSize, LayoutUnit& computedMaxSize, LayoutUnit computedSize, MinimumSizeIsAutomaticContentBased minimumSizeType, ConstrainDimension dimension) const
{
// TODO: Here we use isSpecified() to present the definite value. This is not quite correct, for the definite value should also include
// a size of the initial containing block and the “stretch-fit” sizing of non-replaced blocks if they have definite values.
// See https://www.w3.org/TR/css-sizing-3/#definite
const RenderStyle& styleToUse = style();
ASSERT(styleToUse.hasAspectRatio());
auto logicalSize = dimension == ConstrainDimension::Width ? styleToUse.logicalWidth() : styleToUse.logicalHeight();
// https://www.w3.org/TR/css-sizing-4/#aspect-ratio-minimum
if (minimumSizeType == MinimumSizeIsAutomaticContentBased::Yes) {
// Only use Automatic Content-based Minimum Sizes in the ratio-dependent axis.
if (logicalSize.isSpecified())
computedMinSize = std::min(computedMinSize, computedSize);
computedMinSize = std::min(computedMinSize, computedMaxSize);
}

if (shouldComputeLogicalHeightFromAspectRatio()) {
if (!styleToUse.logicalWidth().isSpecified()) {
auto [logicalMinWidth, logicalMaxWidth] = computeMinMaxLogicalWidthFromAspectRatio();
logicalWidth = std::clamp(logicalWidth, logicalMinWidth, logicalMaxWidth);
}
if (logicalSize.isSpecified())
return;

// Sizing constraints in either axis (the origin axis) should be transferred through the preferred aspect ratio. See https://www.w3.org/TR/css-sizing-4/#aspect-ratio-size-transfers
bool shouldCheckTransferredMinSize = dimension == ConstrainDimension::Width ? !styleToUse.logicalMinWidth().isSpecified() : !styleToUse.logicalMinHeight().isSpecified();
bool shouldCheckTransferredMaxSize = dimension == ConstrainDimension::Width ? !styleToUse.logicalMaxWidth().isSpecified() : !styleToUse.logicalMaxHeight().isSpecified();
if (!shouldCheckTransferredMaxSize && !shouldCheckTransferredMinSize)
return;

auto [transferredLogicalMinSize, transferredLogicalMaxSize] = dimension == ConstrainDimension::Width ? computeMinMaxLogicalWidthFromAspectRatio() : computeMinMaxLogicalHeightFromAspectRatio();
if (shouldCheckTransferredMaxSize && transferredLogicalMaxSize != LayoutUnit::max()) {
// The transferred max size should be floored by the definite minimum size.
if (!shouldCheckTransferredMinSize && minimumSizeType == MinimumSizeIsAutomaticContentBased::No)
transferredLogicalMaxSize = std::max(transferredLogicalMaxSize, computedMinSize);
computedMaxSize = std::min(computedMaxSize, transferredLogicalMaxSize);
if (minimumSizeType == MinimumSizeIsAutomaticContentBased::Yes)
computedMinSize = std::min(computedMinSize, computedMaxSize);
}

if (shouldCheckTransferredMinSize && transferredLogicalMinSize > LayoutUnit()) {
// The transferred min size should be capped by the definite maximum size.
if (!shouldCheckTransferredMaxSize)
transferredLogicalMinSize = std::min(transferredLogicalMinSize, computedMaxSize);
computedMinSize = std::max(computedMinSize, transferredLogicalMinSize);
}
}

LayoutUnit RenderBox::constrainLogicalWidthInFragmentByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock& cb, RenderFragmentContainer* fragment, AllowIntrinsic allowIntrinsic) const
{
const RenderStyle& styleToUse = style();
LayoutUnit computedMaxWidth = LayoutUnit::max();
if (!styleToUse.logicalMaxWidth().isUndefined() && (allowIntrinsic == AllowIntrinsic::Yes || !styleToUse.logicalMaxWidth().isIntrinsic()))
logicalWidth = std::min(logicalWidth, computeLogicalWidthInFragmentUsing(MaxSize, styleToUse.logicalMaxWidth(), availableWidth, cb, fragment));
computedMaxWidth = computeLogicalWidthInFragmentUsing(MaxSize, styleToUse.logicalMaxWidth(), availableWidth, cb, fragment);

if (allowIntrinsic == AllowIntrinsic::No && styleToUse.logicalMinWidth().isIntrinsic())
return logicalWidth;
return std::min(logicalWidth, computedMaxWidth);

auto logicalMinWidth = styleToUse.logicalMinWidth();
LayoutUnit computedMinWidth;
MinimumSizeIsAutomaticContentBased minimumSizeType = MinimumSizeIsAutomaticContentBased::No;
if (logicalMinWidth.isAuto() && shouldComputeLogicalWidthFromAspectRatio() && (styleToUse.logicalWidth().isAuto() || styleToUse.logicalWidth().isMinContent() || styleToUse.logicalWidth().isMaxContent()) && !is<RenderReplaced>(*this) && effectiveOverflowInlineDirection() == Overflow::Visible) {
// Make sure we actually used the aspect ratio.
// The automatic minimum size in the ratio-dependent axis is its min-content size. See https://www.w3.org/TR/css-sizing-4/#aspect-ratio-minimum
logicalMinWidth = Length(LengthType::MinContent);
minimumSizeType = MinimumSizeIsAutomaticContentBased::Yes;
}
return std::max(logicalWidth, computeLogicalWidthInFragmentUsing(MinSize, logicalMinWidth, availableWidth, cb, fragment));
computedMinWidth = computeLogicalWidthInFragmentUsing(MinSize, logicalMinWidth, availableWidth, cb, fragment);

if (styleToUse.hasAspectRatio())
constrainLogicalMinMaxSizesByAspectRatio(computedMinWidth, computedMaxWidth, logicalWidth, minimumSizeType, ConstrainDimension::Width);

logicalWidth = std::min(logicalWidth, computedMaxWidth);
return std::max(logicalWidth, computedMinWidth);
}

LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, std::optional<LayoutUnit> intrinsicContentHeight) const
{
const RenderStyle& styleToUse = style();
if (!styleToUse.logicalMaxHeight().isUndefined()) {
if (std::optional<LayoutUnit> maxH = computeLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight))
logicalHeight = std::min(logicalHeight, maxH.value());
}
std::optional<LayoutUnit> computedLogicalMaxHeight;
if (!styleToUse.logicalMaxHeight().isUndefined())
computedLogicalMaxHeight = computeLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight);

MinimumSizeIsAutomaticContentBased minimumSizeType = MinimumSizeIsAutomaticContentBased::No;
auto logicalMinHeight = styleToUse.logicalMinHeight();
if (logicalMinHeight.isAuto() && shouldComputeLogicalHeightFromAspectRatio() && intrinsicContentHeight && !is<RenderReplaced>(*this) && effectiveOverflowBlockDirection() == Overflow::Visible) {
auto heightFromAspectRatio = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth()) - borderAndPaddingLogicalHeight();
heightFromAspectRatio = std::min(heightFromAspectRatio, logicalHeight);
if (firstChild())
heightFromAspectRatio = std::max(heightFromAspectRatio, *intrinsicContentHeight);
logicalMinHeight = Length(heightFromAspectRatio, LengthType::Fixed);
minimumSizeType = MinimumSizeIsAutomaticContentBased::Yes;
}
if (logicalMinHeight.isMinContent() || logicalMinHeight.isMaxContent())
logicalMinHeight = Length();
if (std::optional<LayoutUnit> computedLogicalHeight = computeLogicalHeightUsing(MinSize, logicalMinHeight, intrinsicContentHeight))
return std::max(logicalHeight, computedLogicalHeight.value());
return logicalHeight;
std::optional<LayoutUnit> computedLogicalMinHeight = computeLogicalHeightUsing(MinSize, logicalMinHeight, intrinsicContentHeight);
LayoutUnit maxHeight = computedLogicalMaxHeight ? computedLogicalMaxHeight.value() : LayoutUnit::max();
LayoutUnit minHeight = computedLogicalMinHeight ? computedLogicalMinHeight.value() : LayoutUnit();
if (styleToUse.hasAspectRatio())
constrainLogicalMinMaxSizesByAspectRatio(minHeight, maxHeight, logicalHeight, minimumSizeType, ConstrainDimension::Height);
logicalHeight = std::min(logicalHeight, maxHeight);
return std::max(logicalHeight, minHeight);
}

LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, std::optional<LayoutUnit> intrinsicContentHeight) const
@@ -5478,16 +5530,34 @@ LayoutUnit RenderBox::computeLogicalWidthFromAspectRatio(RenderFragmentContainer

std::pair<LayoutUnit, LayoutUnit> RenderBox::computeMinMaxLogicalWidthFromAspectRatio() const
{
LayoutUnit blockMinSize = constrainLogicalHeightByMinMax(LayoutUnit(), std::nullopt);
LayoutUnit blockMaxSize = constrainLogicalHeightByMinMax(LayoutUnit::max(), std::nullopt);
ASSERT(style().hasAspectRatio());
LayoutUnit transferredMinSize = LayoutUnit();
LayoutUnit transferredMaxSize = LayoutUnit::max();
if (blockMinSize > LayoutUnit())
transferredMinSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMinSize);
if (blockMaxSize != LayoutUnit::max())
transferredMaxSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMaxSize);
// Minimum size wins over maximum size.
transferredMaxSize = std::max(transferredMaxSize, transferredMinSize);
if (style().logicalMinHeight().isSpecified()) {
if (LayoutUnit blockMinSize = constrainLogicalHeightByMinMax(LayoutUnit(), std::nullopt); blockMinSize > LayoutUnit())
transferredMinSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMinSize);
}
if (style().logicalMaxHeight().isSpecified()) {
if (LayoutUnit blockMaxSize = constrainLogicalHeightByMinMax(LayoutUnit::max(), std::nullopt); blockMaxSize != LayoutUnit::max())
transferredMaxSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMaxSize);
}
return { transferredMinSize, transferredMaxSize };
}

std::pair<LayoutUnit, LayoutUnit> RenderBox::computeMinMaxLogicalHeightFromAspectRatio() const
{
ASSERT(style().hasAspectRatio());
LayoutUnit transferredMinSize = LayoutUnit();
LayoutUnit transferredMaxSize = LayoutUnit::max();
if (style().logicalMinWidth().isSpecified()) {
if (LayoutUnit inlineMinSize = computeLogicalWidthInFragmentUsing(MinSize, style().logicalMinWidth(), availableWidth(), *containingBlock(), nullptr); inlineMinSize > LayoutUnit())
transferredMinSize = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), inlineMinSize);
}

if (style().logicalMaxWidth().isSpecified()) {
if (LayoutUnit inlineMaxSize = computeLogicalWidthInFragmentUsing(MaxSize, style().logicalMaxWidth(), availableWidth(), *containingBlock(), nullptr); inlineMaxSize != LayoutUnit::max())
transferredMaxSize = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), inlineMaxSize);
}
return { transferredMinSize, transferredMaxSize };
}

@@ -722,6 +722,10 @@ override;
LayoutUnit computeLogicalWidthFromAspectRatioInternal() const;
LayoutUnit computeLogicalWidthFromAspectRatio(RenderFragmentContainer* = nullptr) const;
std::pair<LayoutUnit, LayoutUnit> computeMinMaxLogicalWidthFromAspectRatio() const;
std::pair<LayoutUnit, LayoutUnit> computeMinMaxLogicalHeightFromAspectRatio() const;
enum class ConstrainDimension { Width, Height };
enum class MinimumSizeIsAutomaticContentBased { Yes, No };
void constrainLogicalMinMaxSizesByAspectRatio(LayoutUnit& minSize, LayoutUnit& maxSize, LayoutUnit computedSize, MinimumSizeIsAutomaticContentBased, ConstrainDimension) const;

static LayoutUnit blockSizeFromAspectRatio(LayoutUnit borderPaddingInlineSum, LayoutUnit borderPaddingBlockSum, double aspectRatio, BoxSizing boxSizing, LayoutUnit inlineSize)
{

0 comments on commit 94a7be7

Please sign in to comment.