diff --git a/Libraries/LibWeb/SVG/SVGElement.cpp b/Libraries/LibWeb/SVG/SVGElement.cpp index c4582a8d134..474e14f49e4 100644 --- a/Libraries/LibWeb/SVG/SVGElement.cpp +++ b/Libraries/LibWeb/SVG/SVGElement.cpp @@ -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 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; diff --git a/Libraries/LibWeb/SVG/SVGElement.h b/Libraries/LibWeb/SVG/SVGElement.h index c1c1615d525..a58140524de 100644 --- a/Libraries/LibWeb/SVG/SVGElement.h +++ b/Libraries/LibWeb/SVG/SVGElement.h @@ -28,6 +28,9 @@ public: bool should_include_in_accessibility_tree() const; virtual Optional default_role() const override; + virtual bool is_presentational_hint(FlyString const&) const override; + virtual void apply_presentational_hints(GC::Ref) const override; + protected: SVGElement(DOM::Document&, DOM::QualifiedName); diff --git a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index 89b044800b4..88033f8d02c 100644 --- a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -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 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) { diff --git a/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Libraries/LibWeb/SVG/SVGGraphicsElement.h index d4fb41cc8d7..1f83cea5a58 100644 --- a/Libraries/LibWeb/SVG/SVGGraphicsElement.h +++ b/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -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) const override; - virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; Optional fill_color() const; diff --git a/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-irrelevant.txt b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-irrelevant.txt new file mode 100644 index 00000000000..f94275db528 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-irrelevant.txt @@ -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 \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-relevant.txt b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-relevant.txt new file mode 100644 index 00000000000..03ba9d91a18 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-relevant.txt @@ -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 \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-unknown.txt b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-unknown.txt new file mode 100644 index 00000000000..9ebb97a4c3a --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/svg/styling/presentation-attributes-unknown.txt @@ -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 \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-irrelevant.html b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-irrelevant.html new file mode 100644 index 00000000000..401118c16f2 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-irrelevant.html @@ -0,0 +1,21 @@ + + +SVG presentation attributes - on irrelevant elements + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-relevant.html b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-relevant.html new file mode 100644 index 00000000000..74d407d82f4 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-relevant.html @@ -0,0 +1,20 @@ + + +SVG presentation attributes - on relevant elements + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-unknown.html b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-unknown.html new file mode 100644 index 00000000000..519d26e2d51 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes-unknown.html @@ -0,0 +1,20 @@ + + +SVG presentation attributes - on unknown SVG elements + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes.js b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes.js new file mode 100644 index 00000000000..96d9568e3bd --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/svg/styling/presentation-attributes.js @@ -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; +}