Skip to content
Permalink
Browse files
Implement aspect-ratio mapping for canvas element.
https://bugs.webkit.org/show_bug.cgi?id=240887

Reviewed by Chris Dumez.

This patch adds the mapping between the width and height attributes,
which are both presentational hints, to the aspect-ratio property on
canvas elements. When a canvas element is created, its own
implementations of hasPresentationalHintsForAttribute and
collectPresentationalHintsForAttribute will be used to set the intrinsic
width and height of the element as well as set the aspect-ratio property
if both are provided.

https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio-expected.txt:
* Source/WebCore/html/HTMLCanvasElement.cpp:
(WebCore::HTMLCanvasElement::hasPresentationalHintsForAttribute const):
(WebCore::HTMLCanvasElement::collectPresentationalHintsForAttribute):
* Source/WebCore/html/HTMLCanvasElement.h:

* Source/WebCore/html/HTMLElement.cpp:
(WebCore::HTMLElement::addParsedWidthAndHeightToAspectRatioList):
(WebCore::HTMLElement::applyAspectRatioFromWidthAndHeightAttributesToStyle):
(WebCore::HTMLElement::applyAspectRatioWithoutDimensionalRulesFromWidthAndHeightAttributesToStyle):
There are two different ways to map the width and height to the aspect ratio:
1.) map to the aspect-ratio property (using dimension rules)
2.) map to the aspect-ratio property

The main difference in the parsing process is that only integer values
are outputted when parsing without dimension rules and dimension rules
will output any sort of number. For example, 0.5 will be parsed as 0.5
when usig 1, but will be 0 when parsed with 2. This new method parses
the width and height without dimensional rules and uses the results
for the aspect-ratio property.

https://html.spec.whatwg.org/multipage/rendering.html#map-to-the-aspect-ratio-property

* Source/WebCore/html/HTMLElement.h:
* Source/WebCore/rendering/RenderBox.h:
(WebCore::RenderBox::isAspectRatioDegenerate const):
The CSS spec defines a degenerate aspect-ratio as one that is either
0 or infinite. This helper method can be used in places where we
perform an if(aspectRatio) check like in RenderReplaced::computeReplacedLogicalWidth
so that we can catch both of the cases. Before adding this method, the
case where the aspect ratio is infinite would fall through into the
if branch when it should not have.

* Source/WebCore/rendering/RenderReplaced.cpp:
(WebCore::RenderReplaced::computeReplacedLogicalWidth const):

Canonical link: https://commits.webkit.org/252001@main
  • Loading branch information
Sammy Gill authored and cdumez committed Jun 30, 2022
1 parent 3ab9efd commit 8c0165e26f14260d13eabf2d7c8ca675f3bc0197
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 14 deletions.
@@ -4812,9 +4812,6 @@ webkit.org/b/241778 [ Debug ] imported/w3c/web-platform-tests/css/css-contain/co
# Flaky css-contain test
imported/w3c/web-platform-tests/css/css-contain/content-visibility/animation-display-lock.html [ Failure Pass ]

# Canvas doesn't get default aspect-ratio for width and height attributes.
webkit.org/b/217529 imported/w3c/web-platform-tests/css/css-flexbox/canvas-contain-size.html [ ImageOnlyFailure ]

# Multicolumn does not paint the horizontal overflow area of a relative child.
webkit.org/b/41796 imported/w3c/web-platform-tests/css/css-contain/contain-size-monolithic-002.html [ ImageOnlyFailure ]

@@ -1,13 +1,13 @@


FAIL Canvas width and height attributes are used as the surface size with contain:size assert_approx_equals: expected 2.5 +/- 0.001 but got Infinity
PASS Canvas width and height attributes are used as the surface size with contain:size
PASS Canvas width and height attributes are used as the surface size
FAIL Computed style test: canvas with {"width":"10","height":"20"} assert_equals: expected "auto 10 / 20" but got "auto"
FAIL Computed style test: canvas with {"width":"0","height":"1"} assert_equals: expected "auto 0 / 1" but got "auto"
FAIL Computed style test: canvas with {"width":"1","height":"0"} assert_equals: expected "auto 1 / 0" but got "auto"
FAIL Computed style test: canvas with {"width":"0","height":"0"} assert_equals: expected "auto 0 / 0" but got "auto"
FAIL Computed style test: canvas with {"width":"0.5","height":"1.5"} assert_equals: expected "auto 0 / 1" but got "auto"
FAIL Computed style test: canvas with {"width":"10%","height":"20"} assert_equals: expected "auto 10 / 20" but got "auto"
PASS Computed style test: canvas with {"width":"10","height":"20"}
PASS Computed style test: canvas with {"width":"0","height":"1"}
PASS Computed style test: canvas with {"width":"1","height":"0"}
PASS Computed style test: canvas with {"width":"0","height":"0"}
PASS Computed style test: canvas with {"width":"0.5","height":"1.5"}
PASS Computed style test: canvas with {"width":"10%","height":"20"}
PASS Computed style test: canvas with {"width":null,"height":null}
PASS Computed style test: canvas with {"width":"10","height":null}
PASS Computed style test: canvas with {"width":null,"height":"20"}
@@ -151,6 +151,23 @@ HTMLCanvasElement::~HTMLCanvasElement()
setImageBuffer(nullptr);
}

bool HTMLCanvasElement::hasPresentationalHintsForAttribute(const QualifiedName& name) const
{
if (name == widthAttr || name == heightAttr)
return true;
return HTMLElement::hasPresentationalHintsForAttribute(name);
}

void HTMLCanvasElement::collectPresentationalHintsForAttribute(const QualifiedName& name, const AtomString& value, MutableStyleProperties& style)
{
if (name == widthAttr)
applyAspectRatioWithoutDimensionalRulesFromWidthAndHeightAttributesToStyle(value, attributeWithoutSynchronization(heightAttr), style);
else if (name == heightAttr)
applyAspectRatioWithoutDimensionalRulesFromWidthAndHeightAttributesToStyle(attributeWithoutSynchronization(widthAttr), value, style);
else
HTMLElement::collectPresentationalHintsForAttribute(name, value, style);
}

void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomString& value)
{
if (name == widthAttr || name == heightAttr)
@@ -153,6 +153,8 @@ class HTMLCanvasElement final : public HTMLElement, public CanvasBase, public Ac
void eventListenersDidChange() final;

void parseAttribute(const QualifiedName&, const AtomString&) final;
bool hasPresentationalHintsForAttribute(const QualifiedName&) const final;
void collectPresentationalHintsForAttribute(const QualifiedName&, const AtomString&, MutableStyleProperties&) final;
RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) final;

bool canContainRangeEndPoint() const final;
@@ -511,9 +511,29 @@ void HTMLElement::applyAspectRatioFromWidthAndHeightAttributesToStyle(StringView
if (!dimensionHeight || dimensionHeight->type != HTMLDimension::Type::Pixel)
return;

addParsedWidthAndHeightToAspectRatioList(dimensionWidth->number, dimensionHeight->number, style);
}

void HTMLElement::applyAspectRatioWithoutDimensionalRulesFromWidthAndHeightAttributesToStyle(StringView widthAttribute, StringView heightAttribute, MutableStyleProperties& style)
{
if (!document().settings().aspectRatioOfImgFromWidthAndHeightEnabled())
return;

auto dimensionWidth = parseHTMLNonNegativeInteger(widthAttribute);
if (!dimensionWidth)
return;
auto dimensionHeight = parseHTMLNonNegativeInteger(heightAttribute);
if (!dimensionHeight)
return;

addParsedWidthAndHeightToAspectRatioList(dimensionWidth.value(), dimensionHeight.value(), style);
}

void HTMLElement::addParsedWidthAndHeightToAspectRatioList(double width, double height, MutableStyleProperties& style)
{
auto ratioList = CSSValueList::createSlashSeparated();
ratioList->append(CSSValuePool::singleton().createValue(dimensionWidth->number, CSSUnitType::CSS_NUMBER));
ratioList->append(CSSValuePool::singleton().createValue(dimensionHeight->number, CSSUnitType::CSS_NUMBER));
ratioList->append(CSSValuePool::singleton().createValue(width, CSSUnitType::CSS_NUMBER));
ratioList->append(CSSValuePool::singleton().createValue(height, CSSUnitType::CSS_NUMBER));
auto list = CSSValueList::createSpaceSeparated();
list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueAuto));
list->append(ratioList);
@@ -150,6 +150,9 @@ class HTMLElement : public StyledElement {
void addHTMLColorToStyle(MutableStyleProperties&, CSSPropertyID, const String& color);

void applyAspectRatioFromWidthAndHeightAttributesToStyle(StringView widthAttribute, StringView heightAttribute, MutableStyleProperties&);
void applyAspectRatioWithoutDimensionalRulesFromWidthAndHeightAttributesToStyle(StringView widthAttribute, StringView heightAttribute, MutableStyleProperties&);
void addParsedWidthAndHeightToAspectRatioList(double width, double height, MutableStyleProperties&);

void applyAlignmentAttributeToStyle(const AtomString&, MutableStyleProperties&);
void applyBorderAttributeToStyle(const AtomString&, MutableStyleProperties&);

@@ -731,7 +731,9 @@ override;
}

void computePreferredLogicalWidths(const Length& minWidth, const Length& maxWidth, LayoutUnit borderAndPadding);


bool isAspectRatioDegenerate(double aspectRatio) const { return !aspectRatio || isnan(aspectRatio); }

private:
bool replacedMinMaxLogicalHeightComputesAsNone(SizeType) const;

@@ -593,7 +593,7 @@ LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred sh
if (computedHeightIsAuto && hasIntrinsicWidth)
return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred);

if (intrinsicRatio) {
if (!isAspectRatioDegenerate(intrinsicRatio)) {
// If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio;
// or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value
// of 'width' is: (used height) * (intrinsic ratio)

0 comments on commit 8c0165e

Please sign in to comment.