mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-07 00:29:15 +00:00
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:
parent
fe6d8131ae
commit
10cff06a95
Notes:
github-actions[bot]
2025-04-25 08:19:32 +00:00
Author: https://github.com/awesomekling
Commit: 10cff06a95
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4465
Reviewed-by: https://github.com/gmta ✅
11 changed files with 700 additions and 69 deletions
|
@ -30,6 +30,91 @@ void SVGElement::initialize(JS::Realm& realm)
|
||||||
Base::initialize(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 SVGElement::should_include_in_accessibility_tree() const
|
||||||
{
|
{
|
||||||
bool has_title_or_desc = false;
|
bool has_title_or_desc = false;
|
||||||
|
|
|
@ -28,6 +28,9 @@ public:
|
||||||
bool should_include_in_accessibility_tree() const;
|
bool should_include_in_accessibility_tree() const;
|
||||||
virtual Optional<ARIA::Role> default_role() const override;
|
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:
|
protected:
|
||||||
SVGElement(DOM::Document&, DOM::QualifiedName);
|
SVGElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
||||||
|
|
|
@ -131,72 +131,6 @@ Gfx::AffineTransform SVGGraphicsElement::get_transform() const
|
||||||
return transform;
|
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)
|
static FillRule to_svg_fill_rule(CSS::FillRule fill_rule)
|
||||||
{
|
{
|
||||||
switch (fill_rule) {
|
switch (fill_rule) {
|
||||||
|
|
|
@ -29,9 +29,6 @@ class SVGGraphicsElement : public SVGElement {
|
||||||
WEB_PLATFORM_OBJECT(SVGGraphicsElement, SVGElement);
|
WEB_PLATFORM_OBJECT(SVGGraphicsElement, SVGElement);
|
||||||
|
|
||||||
public:
|
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;
|
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;
|
Optional<Gfx::Color> fill_color() const;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue