LibWeb: Move presentation attrs from SVGGraphicsElement to SVGElement

This matches how other browser behave and fixes a bunch of WPT tests.
This commit is contained in:
Andreas Kling 2025-04-24 19:48:58 +02:00 committed by Jelle Raaijmakers
commit 10cff06a95
Notes: github-actions[bot] 2025-04-25 08:19:32 +00:00
11 changed files with 700 additions and 69 deletions

View file

@ -30,6 +30,91 @@ void SVGElement::initialize(JS::Realm& realm)
Base::initialize(realm);
}
struct NamedPropertyID {
NamedPropertyID(CSS::PropertyID property_id)
: id(property_id)
, name(CSS::string_from_property_id(property_id))
{
}
CSS::PropertyID id;
StringView name;
};
static Array const attribute_style_properties {
// FIXME: The `fill` attribute and CSS `fill` property are not the same! But our support is limited enough that they are equivalent for now.
NamedPropertyID(CSS::PropertyID::Fill),
// FIXME: The `stroke` attribute and CSS `stroke` property are not the same! But our support is limited enough that they are equivalent for now.
NamedPropertyID(CSS::PropertyID::ClipPath),
NamedPropertyID(CSS::PropertyID::ClipRule),
NamedPropertyID(CSS::PropertyID::Color),
NamedPropertyID(CSS::PropertyID::Cursor),
NamedPropertyID(CSS::PropertyID::Direction),
NamedPropertyID(CSS::PropertyID::Display),
NamedPropertyID(CSS::PropertyID::FillOpacity),
NamedPropertyID(CSS::PropertyID::FillRule),
NamedPropertyID(CSS::PropertyID::FontFamily),
NamedPropertyID(CSS::PropertyID::FontSize),
NamedPropertyID(CSS::PropertyID::FontStyle),
NamedPropertyID(CSS::PropertyID::FontWeight),
NamedPropertyID(CSS::PropertyID::ImageRendering),
NamedPropertyID(CSS::PropertyID::LetterSpacing),
NamedPropertyID(CSS::PropertyID::Mask),
NamedPropertyID(CSS::PropertyID::MaskType),
NamedPropertyID(CSS::PropertyID::Opacity),
NamedPropertyID(CSS::PropertyID::Overflow),
NamedPropertyID(CSS::PropertyID::PointerEvents),
NamedPropertyID(CSS::PropertyID::StopColor),
NamedPropertyID(CSS::PropertyID::StopOpacity),
NamedPropertyID(CSS::PropertyID::Stroke),
NamedPropertyID(CSS::PropertyID::StrokeDasharray),
NamedPropertyID(CSS::PropertyID::StrokeDashoffset),
NamedPropertyID(CSS::PropertyID::StrokeLinecap),
NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
NamedPropertyID(CSS::PropertyID::StrokeOpacity),
NamedPropertyID(CSS::PropertyID::StrokeWidth),
NamedPropertyID(CSS::PropertyID::TextAnchor),
NamedPropertyID(CSS::PropertyID::TextOverflow),
NamedPropertyID(CSS::PropertyID::TransformOrigin),
NamedPropertyID(CSS::PropertyID::UnicodeBidi),
NamedPropertyID(CSS::PropertyID::Visibility),
NamedPropertyID(CSS::PropertyID::WhiteSpace),
NamedPropertyID(CSS::PropertyID::WordSpacing),
NamedPropertyID(CSS::PropertyID::WritingMode),
};
bool SVGElement::is_presentational_hint(FlyString const& name) const
{
if (Base::is_presentational_hint(name))
return true;
return any_of(attribute_style_properties, [&](auto& property) { return name.equals_ignoring_ascii_case(property.name); });
}
void SVGElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
{
CSS::Parser::ParsingParams parsing_context { document(), CSS::Parser::ParsingMode::SVGPresentationAttribute };
for_each_attribute([&](auto& name, auto& value) {
for (auto property : attribute_style_properties) {
if (!name.equals_ignoring_ascii_case(property.name))
continue;
if (property.id == CSS::PropertyID::Mask) {
// Mask is a shorthand property in CSS, but parse_css_value does not take that into account. For now,
// just parse as 'mask-image' as anything else is currently not supported.
// FIXME: properly parse longhand 'mask' property
if (auto style_value = parse_css_value(parsing_context, value, CSS::PropertyID::MaskImage)) {
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::MaskImage, style_value.release_nonnull());
}
} else {
if (auto style_value = parse_css_value(parsing_context, value, property.id))
cascaded_properties->set_property_from_presentational_hint(property.id, style_value.release_nonnull());
}
break;
}
});
}
bool SVGElement::should_include_in_accessibility_tree() const
{
bool has_title_or_desc = false;

View file

@ -28,6 +28,9 @@ public:
bool should_include_in_accessibility_tree() const;
virtual Optional<ARIA::Role> default_role() const override;
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
protected:
SVGElement(DOM::Document&, DOM::QualifiedName);

View file

@ -131,72 +131,6 @@ Gfx::AffineTransform SVGGraphicsElement::get_transform() const
return transform;
}
struct NamedPropertyID {
NamedPropertyID(CSS::PropertyID property_id)
: id(property_id)
, name(CSS::string_from_property_id(property_id))
{
}
CSS::PropertyID id;
StringView name;
};
static Array const attribute_style_properties {
// FIXME: The `fill` attribute and CSS `fill` property are not the same! But our support is limited enough that they are equivalent for now.
NamedPropertyID(CSS::PropertyID::Fill),
// FIXME: The `stroke` attribute and CSS `stroke` property are not the same! But our support is limited enough that they are equivalent for now.
NamedPropertyID(CSS::PropertyID::Stroke),
NamedPropertyID(CSS::PropertyID::StrokeDasharray),
NamedPropertyID(CSS::PropertyID::StrokeDashoffset),
NamedPropertyID(CSS::PropertyID::StrokeLinecap),
NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
NamedPropertyID(CSS::PropertyID::StrokeWidth),
NamedPropertyID(CSS::PropertyID::FillRule),
NamedPropertyID(CSS::PropertyID::FillOpacity),
NamedPropertyID(CSS::PropertyID::StrokeOpacity),
NamedPropertyID(CSS::PropertyID::Opacity),
NamedPropertyID(CSS::PropertyID::TextAnchor),
NamedPropertyID(CSS::PropertyID::FontSize),
NamedPropertyID(CSS::PropertyID::Mask),
NamedPropertyID(CSS::PropertyID::MaskType),
NamedPropertyID(CSS::PropertyID::ClipPath),
NamedPropertyID(CSS::PropertyID::ClipRule),
NamedPropertyID(CSS::PropertyID::Display),
};
bool SVGGraphicsElement::is_presentational_hint(FlyString const& name) const
{
if (Base::is_presentational_hint(name))
return true;
return any_of(attribute_style_properties, [&](auto& property) { return name.equals_ignoring_ascii_case(property.name); });
}
void SVGGraphicsElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
{
CSS::Parser::ParsingParams parsing_context { document(), CSS::Parser::ParsingMode::SVGPresentationAttribute };
for_each_attribute([&](auto& name, auto& value) {
for (auto property : attribute_style_properties) {
if (!name.equals_ignoring_ascii_case(property.name))
continue;
if (property.id == CSS::PropertyID::Mask) {
// Mask is a shorthand property in CSS, but parse_css_value does not take that into account. For now,
// just parse as 'mask-image' as anything else is currently not supported.
// FIXME: properly parse longhand 'mask' property
if (auto style_value = parse_css_value(parsing_context, value, CSS::PropertyID::MaskImage)) {
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::MaskImage, style_value.release_nonnull());
}
} else {
if (auto style_value = parse_css_value(parsing_context, value, property.id))
cascaded_properties->set_property_from_presentational_hint(property.id, style_value.release_nonnull());
}
break;
}
});
}
static FillRule to_svg_fill_rule(CSS::FillRule fill_rule)
{
switch (fill_rule) {

View file

@ -29,9 +29,6 @@ class SVGGraphicsElement : public SVGElement {
WEB_PLATFORM_OBJECT(SVGGraphicsElement, SVGElement);
public:
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
Optional<Gfx::Color> fill_color() const;

View file

@ -0,0 +1,48 @@
Harness status: OK
Found 42 tests
37 Pass
5 Fail
Pass clip-path presentation attribute supported on an irrelevant element
Pass clip-rule presentation attribute supported on an irrelevant element
Pass color presentation attribute supported on an irrelevant element
Pass cursor presentation attribute supported on an irrelevant element
Pass direction presentation attribute supported on an irrelevant element
Pass display presentation attribute supported on an irrelevant element
Pass fill presentation attribute supported on an irrelevant element
Pass fill-opacity presentation attribute supported on an irrelevant element
Pass fill-rule presentation attribute supported on an irrelevant element
Fail filter presentation attribute supported on an irrelevant element
Pass font-family presentation attribute supported on an irrelevant element
Pass font-size presentation attribute supported on an irrelevant element
Fail font-stretch presentation attribute supported on an irrelevant element
Pass font-style presentation attribute supported on an irrelevant element
Fail font-variant presentation attribute supported on an irrelevant element
Pass font-weight presentation attribute supported on an irrelevant element
Pass image-rendering presentation attribute supported on an irrelevant element
Pass letter-spacing presentation attribute supported on an irrelevant element
Pass mask-type presentation attribute supported on an irrelevant element
Pass mask presentation attribute supported on an irrelevant element
Pass opacity presentation attribute supported on an irrelevant element
Fail overflow presentation attribute supported on an irrelevant element
Pass pointer-events presentation attribute supported on an irrelevant element
Pass stop-color presentation attribute supported on an irrelevant element
Pass stop-opacity presentation attribute supported on an irrelevant element
Pass stroke presentation attribute supported on an irrelevant element
Pass stroke-dasharray presentation attribute supported on an irrelevant element
Pass stroke-dashoffset presentation attribute supported on an irrelevant element
Pass stroke-linecap presentation attribute supported on an irrelevant element
Pass stroke-linejoin presentation attribute supported on an irrelevant element
Pass stroke-miterlimit presentation attribute supported on an irrelevant element
Pass stroke-opacity presentation attribute supported on an irrelevant element
Pass stroke-width presentation attribute supported on an irrelevant element
Pass text-anchor presentation attribute supported on an irrelevant element
Fail text-decoration presentation attribute supported on an irrelevant element
Pass text-overflow presentation attribute supported on an irrelevant element
Pass transform-origin presentation attribute supported on an irrelevant element
Pass unicode-bidi presentation attribute supported on an irrelevant element
Pass visibility presentation attribute supported on an irrelevant element
Pass white-space presentation attribute supported on an irrelevant element
Pass word-spacing presentation attribute supported on an irrelevant element
Pass writing-mode presentation attribute supported on an irrelevant element

View file

@ -0,0 +1,58 @@
Harness status: OK
Found 52 tests
40 Pass
12 Fail
Pass clip-path presentation attribute supported on a relevant element
Pass clip-rule presentation attribute supported on a relevant element
Pass color presentation attribute supported on a relevant element
Pass cursor presentation attribute supported on a relevant element
Pass cx presentation attribute supported on a relevant element
Pass cy presentation attribute supported on a relevant element
Pass direction presentation attribute supported on a relevant element
Pass display presentation attribute supported on a relevant element
Pass fill presentation attribute supported on a relevant element
Pass fill-opacity presentation attribute supported on a relevant element
Pass fill-rule presentation attribute supported on a relevant element
Fail filter presentation attribute supported on a relevant element
Pass font-family presentation attribute supported on a relevant element
Pass font-size presentation attribute supported on a relevant element
Fail font-stretch presentation attribute supported on a relevant element
Pass font-style presentation attribute supported on a relevant element
Fail font-variant presentation attribute supported on a relevant element
Pass font-weight presentation attribute supported on a relevant element
Fail height presentation attribute supported on a relevant element
Pass image-rendering presentation attribute supported on a relevant element
Pass letter-spacing presentation attribute supported on a relevant element
Pass mask-type presentation attribute supported on a relevant element
Pass mask presentation attribute supported on a relevant element
Pass opacity presentation attribute supported on a relevant element
Fail overflow presentation attribute supported on a relevant element
Pass pointer-events presentation attribute supported on a relevant element
Pass r presentation attribute supported on a relevant element
Fail rx presentation attribute supported on a relevant element
Fail ry presentation attribute supported on a relevant element
Pass stop-color presentation attribute supported on a relevant element
Pass stop-opacity presentation attribute supported on a relevant element
Pass stroke presentation attribute supported on a relevant element
Pass stroke-dasharray presentation attribute supported on a relevant element
Pass stroke-dashoffset presentation attribute supported on a relevant element
Pass stroke-linecap presentation attribute supported on a relevant element
Pass stroke-linejoin presentation attribute supported on a relevant element
Pass stroke-miterlimit presentation attribute supported on a relevant element
Pass stroke-opacity presentation attribute supported on a relevant element
Pass stroke-width presentation attribute supported on a relevant element
Pass text-anchor presentation attribute supported on a relevant element
Fail text-decoration presentation attribute supported on a relevant element
Pass text-overflow presentation attribute supported on a relevant element
Pass transform-origin presentation attribute supported on a relevant element
Fail transform presentation attribute supported on a relevant element
Pass unicode-bidi presentation attribute supported on a relevant element
Pass visibility presentation attribute supported on a relevant element
Pass white-space presentation attribute supported on a relevant element
Fail width presentation attribute supported on a relevant element
Pass word-spacing presentation attribute supported on a relevant element
Pass writing-mode presentation attribute supported on a relevant element
Fail x presentation attribute supported on a relevant element
Fail y presentation attribute supported on a relevant element

View file

@ -0,0 +1,48 @@
Harness status: OK
Found 42 tests
37 Pass
5 Fail
Pass clip-path presentation attribute supported on an unknown SVG element
Pass clip-rule presentation attribute supported on an unknown SVG element
Pass color presentation attribute supported on an unknown SVG element
Pass cursor presentation attribute supported on an unknown SVG element
Pass direction presentation attribute supported on an unknown SVG element
Pass display presentation attribute supported on an unknown SVG element
Pass fill presentation attribute supported on an unknown SVG element
Pass fill-opacity presentation attribute supported on an unknown SVG element
Pass fill-rule presentation attribute supported on an unknown SVG element
Fail filter presentation attribute supported on an unknown SVG element
Pass font-family presentation attribute supported on an unknown SVG element
Pass font-size presentation attribute supported on an unknown SVG element
Fail font-stretch presentation attribute supported on an unknown SVG element
Pass font-style presentation attribute supported on an unknown SVG element
Fail font-variant presentation attribute supported on an unknown SVG element
Pass font-weight presentation attribute supported on an unknown SVG element
Pass image-rendering presentation attribute supported on an unknown SVG element
Pass letter-spacing presentation attribute supported on an unknown SVG element
Pass mask-type presentation attribute supported on an unknown SVG element
Pass mask presentation attribute supported on an unknown SVG element
Pass opacity presentation attribute supported on an unknown SVG element
Fail overflow presentation attribute supported on an unknown SVG element
Pass pointer-events presentation attribute supported on an unknown SVG element
Pass stop-color presentation attribute supported on an unknown SVG element
Pass stop-opacity presentation attribute supported on an unknown SVG element
Pass stroke presentation attribute supported on an unknown SVG element
Pass stroke-dasharray presentation attribute supported on an unknown SVG element
Pass stroke-dashoffset presentation attribute supported on an unknown SVG element
Pass stroke-linecap presentation attribute supported on an unknown SVG element
Pass stroke-linejoin presentation attribute supported on an unknown SVG element
Pass stroke-miterlimit presentation attribute supported on an unknown SVG element
Pass stroke-opacity presentation attribute supported on an unknown SVG element
Pass stroke-width presentation attribute supported on an unknown SVG element
Pass text-anchor presentation attribute supported on an unknown SVG element
Fail text-decoration presentation attribute supported on an unknown SVG element
Pass text-overflow presentation attribute supported on an unknown SVG element
Pass transform-origin presentation attribute supported on an unknown SVG element
Pass unicode-bidi presentation attribute supported on an unknown SVG element
Pass visibility presentation attribute supported on an unknown SVG element
Pass white-space presentation attribute supported on an unknown SVG element
Pass word-spacing presentation attribute supported on an unknown SVG element
Pass writing-mode presentation attribute supported on an unknown SVG element

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>SVG presentation attributes - on irrelevant elements</title>
<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="presentation-attributes.js"></script>
<svg id="svg"></svg>
<script>
// Test that all of the presentation attributes with no special rules are
// also supported on elements that the corresponding property does not
// apply to.
for (let p in PROPERTIES) {
if (CSS.supports(p, "initial") && PROPERTIES[p].irrelevantElement) {
test(function() {
assertPresentationAttributeIsSupported(PROPERTIES[p].irrelevantElement, p, PROPERTIES[p].value, p);
}, `${p} presentation attribute supported on an irrelevant element`);
}
}
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>SVG presentation attributes - on relevant elements</title>
<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="presentation-attributes.js"></script>
<svg id="svg"></svg>
<script>
// Test that all of the presentation attributes with no special rules are
// supported on elements that the corresponding properties apply to.
for (let p in PROPERTIES) {
if (CSS.supports(p, "initial")) {
test(function() {
assertPresentationAttributeIsSupported(PROPERTIES[p].relevantElement, p, PROPERTIES[p].value, p);
}, `${p} presentation attribute supported on a relevant element`);
}
}
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>SVG presentation attributes - on unknown SVG elements</title>
<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="presentation-attributes.js"></script>
<svg id="svg"></svg>
<script>
// Test that all of the presentation attributes with no special
// rules are supported on unknown SVG elements.
for (let p in PROPERTIES) {
if (CSS.supports(p, "initial") && PROPERTIES[p].irrelevantElement) {
test(function() {
assertPresentationAttributeIsSupported("unknown", p, PROPERTIES[p].value, p);
}, `${p} presentation attribute supported on an unknown SVG element`);
}
}
</script>

View file

@ -0,0 +1,397 @@
const PROPERTIES = {
"alignment-baseline": {
value: "middle",
relevantElement: "text",
irrelevantElement: "rect",
},
"baseline-shift": {
value: "1",
relevantElement: "text",
irrelevantElement: "rect",
},
"clip-path": {
value: "url(#e)",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"clip-rule": {
value: "evenodd",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"color": {
value: "blue",
relevantElement: "g",
irrelevantElement: "image",
},
"color-interpolation-filters": {
value: "sRGB",
relevantElement: "filter",
irrelevantElement: "linearGradient",
},
"color-interpolation": {
value: "linearRGB",
relevantElement: "linearGradient",
irrelevantElement: "image",
},
"cursor": {
value: "pointer",
relevantElement: "g",
irrelevantElement: "defs",
},
"cx": {
value: "1",
relevantElement: "circle",
irrelevantElement: null,
},
"cy": {
value: "1",
relevantElement: "circle",
irrelevantElement: null,
},
"direction": {
value: "rtl",
relevantElement: "text",
irrelevantElement: "rect",
},
"display": {
value: "block",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"d": {
value: "M0,0 L1,1",
relevantElement: "path",
irrelevantElement: null,
},
"dominant-baseline": {
value: "middle",
relevantElement: "text",
irrelevantElement: "rect",
},
"fill": {
value: "blue",
relevantElement: "g",
irrelevantElement: "image",
},
"fill-opacity": {
value: "0.5",
relevantElement: "g",
irrelevantElement: "image",
},
"fill-rule": {
value: "evenodd",
relevantElement: "path",
irrelevantElement: "image",
},
"filter": {
value: "url(#e)",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"flood-color": {
value: "blue",
relevantElement: "feFlood",
irrelevantElement: "rect",
},
"flood-opacity": {
value: "0.5",
relevantElement: "feFlood",
irrelevantElement: "rect",
},
"font-family": {
value: "Test Family",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-size": {
value: "50",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-size-adjust": {
value: "0.5",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-stretch": {
value: "expanded",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-style": {
value: "italic",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-variant": {
value: "small-caps",
relevantElement: "text",
irrelevantElement: "rect",
},
"font-weight": {
value: "900",
relevantElement: "text",
irrelevantElement: "rect",
},
"glyph-orientation-vertical": {
value: "90",
relevantElement: "text",
irrelevantElement: "rect",
},
"height": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
"image-rendering": {
value: ["optimizeSpeed", "pixelated"],
relevantElement: "image",
irrelevantElement: "path",
},
"letter-spacing": {
value: "1px",
relevantElement: "text",
irrelevantElement: "rect",
},
"lighting-color": {
value: "blue",
relevantElement: "feDiffuseLighting",
irrelevantElement: "rect",
},
"marker-end": {
value: "url(#e)",
relevantElement: "path",
irrelevantElement: "image",
},
"marker-mid": {
value: "url(#e)",
relevantElement: "path",
irrelevantElement: "image",
},
"marker-start": {
value: "url(#e)",
relevantElement: "path",
irrelevantElement: "image",
},
"mask-type": {
value: "alpha",
relevantElement: "mask",
irrelevantElement: "rect",
},
"mask": {
value: "url(#e)",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"opacity": {
value: "0.5",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"overflow": {
value: "scroll",
relevantElement: "svg",
irrelevantElement: "rect",
},
"paint-order": {
value: "fill stroke",
relevantElement: "path",
irrelevantElement: "image",
},
"pointer-events": {
value: "none",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"r": {
value: "1",
relevantElement: "circle",
irrelevantElement: null,
},
"rx": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
"ry": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
"shape-rendering": {
value: "geometricPrecision",
relevantElement: "path",
irrelevantElement: "image",
},
"stop-color": {
value: "blue",
relevantElement: "stop",
irrelevantElement: "rect",
},
"stop-opacity": {
value: "0.5",
relevantElement: "stop",
irrelevantElement: "rect",
},
"stroke": {
value: "blue",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-dasharray": {
value: "1 1",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-dashoffset": {
value: "1",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-linecap": {
value: "round",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-linejoin": {
value: "round",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-miterlimit": {
value: "1",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-opacity": {
value: "0.5",
relevantElement: "path",
irrelevantElement: "image",
},
"stroke-width": {
value: "2",
relevantElement: "path",
irrelevantElement: "image",
},
"text-anchor": {
value: "middle",
relevantElement: "text",
irrelevantElement: "rect",
},
"text-decoration": {
value: "underline",
relevantElement: "text",
irrelevantElement: "rect",
},
"text-overflow": {
value: "ellipsis",
relevantElement: "text",
irrelevantElement: "rect",
},
"text-rendering": {
value: "geometricPrecision",
relevantElement: "text",
irrelevantElement: "rect",
},
"transform-origin": {
value: "1px 1px",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"transform": {
value: "scale(2)",
relevantElement: "g",
irrelevantElement: null,
},
"unicode-bidi": {
value: "embed",
relevantElement: "text",
irrelevantElement: "rect",
},
"vector-effect": {
value: "non-scaling-stroke",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"visibility": {
value: "hidden",
relevantElement: "g",
irrelevantElement: "linearGradient",
},
"white-space": {
value: "pre",
relevantElement: "text",
irrelevantElement: "rect",
},
"width": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
"word-spacing": {
value: "1",
relevantElement: "text",
irrelevantElement: "rect",
},
"writing-mode": {
value: "vertical-rl",
relevantElement: "text",
irrelevantElement: "rect",
},
"x": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
"y": {
value: "1",
relevantElement: "rect",
irrelevantElement: null,
},
};
function presentationAttributeIsSupported(element, attribute, value, property) {
let e = document.createElementNS("http://www.w3.org/2000/svg", element);
svg.append(e);
let propertyValueBefore = getComputedStyle(e).getPropertyValue(property);
e.setAttribute(attribute, value);
// Also set another attribute that is likely to be a presentation attribute,
// in order to provoke bugs.
const otherAttribute = attribute === 'stroke' ? 'fill' : 'stroke';
e.setAttribute(otherAttribute, 'red');
let propertyValueAfter = getComputedStyle(e).getPropertyValue(property);
e.remove();
return propertyValueBefore != propertyValueAfter;
}
function assertPresentationAttributeIsSupported(element, attribute, values, property) {
if (typeof values === 'string')
values = [values];
let supported = values.some(
value => presentationAttributeIsSupported(element, attribute, value, property));
assert_true(
supported,
`Presentation attribute ${attribute}="${values.join(" | ")}" should be supported on ${element} element`
);
}
function assertPresentationAttributeIsNotSupported(element, attribute, values, property) {
if (typeof values === 'string')
values = [values];
let supported = values.some(
value => presentationAttributeIsSupported(element, attribute, value, property));
assert_false(
supported,
`Presentation attribute ${attribute}="${values.join(" | ")}" should not be supported on ${element} element`
);
}
function propertiesAreSupported(properties) {
for (let p of properties) {
if (!CSS.supports(p, "initial")) {
return false;
}
}
return true;
}