LibWeb: Use qualified names for SVG attribute reflectors

This commit is contained in:
Luke Wilde 2025-10-31 12:27:49 +00:00 committed by Tim Flynn
parent d211df8118
commit 60e1a136aa
Notes: github-actions[bot] 2025-11-06 16:46:15 +00:00
16 changed files with 51 additions and 44 deletions

View file

@ -66,7 +66,7 @@ i32 SVGAElement::default_tab_index_value() const
GC::Ref<SVGAnimatedString> SVGAElement::target()
{
if (!m_target)
m_target = SVGAnimatedString::create(realm(), *this, HTML::AttributeNames::target);
m_target = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { HTML::AttributeNames::target, OptionalNone {}, OptionalNone {} });
return *m_target;
}

View file

@ -15,14 +15,14 @@ namespace Web::SVG {
GC_DEFINE_ALLOCATOR(SVGAnimatedNumber);
GC::Ref<SVGAnimatedNumber> SVGAnimatedNumber::create(JS::Realm& realm, GC::Ref<SVGElement> element,
FlyString reflected_attribute, float initial_value, SupportsSecondValue supports_second_value,
DOM::QualifiedName reflected_attribute, float initial_value, SupportsSecondValue supports_second_value,
ValueRepresented value_represented)
{
return realm.create<SVGAnimatedNumber>(realm, element, move(reflected_attribute), initial_value,
supports_second_value, value_represented);
}
SVGAnimatedNumber::SVGAnimatedNumber(JS::Realm& realm, GC::Ref<SVGElement> element, FlyString reflected_attribute,
SVGAnimatedNumber::SVGAnimatedNumber(JS::Realm& realm, GC::Ref<SVGElement> element, DOM::QualifiedName reflected_attribute,
float initial_value, SupportsSecondValue supports_second_value, ValueRepresented value_represented)
: PlatformObject(realm)
, m_element(element)
@ -55,7 +55,7 @@ void SVGAnimatedNumber::set_base_val(float new_value)
if (m_supports_second_value == SupportsSecondValue::Yes) {
// 1. Let current be the value of the reflected attribute (using the attribute's initial value if it is not
// present or invalid).
auto current = m_element->get_attribute_value(m_reflected_attribute);
auto current = m_element->get_attribute_value(m_reflected_attribute.local_name(), m_reflected_attribute.namespace_());
auto current_values = MUST(current.split(' '));
// 2. Let first be the first number in current.
@ -93,7 +93,7 @@ void SVGAnimatedNumber::set_base_val(float new_value)
// (given the implementation's supported Precisionreal number precision), joined and separated by a single U+0020
// SPACE character.
auto new_attribute_value = MUST(String::join(' ', new_));
m_element->set_attribute_value(m_reflected_attribute, new_attribute_value);
m_element->set_attribute_value(m_reflected_attribute.local_name(), new_attribute_value, m_reflected_attribute.prefix(), m_reflected_attribute.namespace_());
}
// https://svgwg.org/svg2-draft/types.html#__svg__SVGAnimatedNumber__animVal
@ -116,7 +116,7 @@ float SVGAnimatedNumber::get_base_or_anim_value() const
{
// 1. Let value be the value of the reflected attribute (using the attribute's initial value if it is not present or
// invalid).
auto value = m_element->get_attribute_value(m_reflected_attribute);
auto value = m_element->get_attribute_value(m_reflected_attribute.local_name(), m_reflected_attribute.namespace_());
// 2. If the reflected attribute is defined to take an number followed by an optional second number, then:
if (m_supports_second_value == SupportsSecondValue::Yes) {

View file

@ -28,7 +28,7 @@ public:
};
[[nodiscard]] static GC::Ref<SVGAnimatedNumber> create(JS::Realm&, GC::Ref<SVGElement>,
FlyString reflected_attribute, float initial_value, SupportsSecondValue = SupportsSecondValue::No,
DOM::QualifiedName reflected_attribute, float initial_value, SupportsSecondValue = SupportsSecondValue::No,
ValueRepresented = ValueRepresented::First);
virtual ~SVGAnimatedNumber() override;
@ -38,7 +38,7 @@ public:
float anim_val() const;
private:
SVGAnimatedNumber(JS::Realm&, GC::Ref<SVGElement>, FlyString, float, SupportsSecondValue, ValueRepresented);
SVGAnimatedNumber(JS::Realm&, GC::Ref<SVGElement>, DOM::QualifiedName, float, SupportsSecondValue, ValueRepresented);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Visitor&) override;
@ -47,7 +47,7 @@ private:
float get_base_or_anim_value() const;
GC::Ref<SVGElement> m_element;
FlyString m_reflected_attribute;
DOM::QualifiedName m_reflected_attribute;
float m_initial_value;
SupportsSecondValue m_supports_second_value;
ValueRepresented m_value_represented;

View file

@ -15,12 +15,12 @@ namespace Web::SVG {
GC_DEFINE_ALLOCATOR(SVGAnimatedString);
GC::Ref<SVGAnimatedString> SVGAnimatedString::create(JS::Realm& realm, GC::Ref<SVGElement> element, FlyString reflected_attribute, Optional<FlyString> deprecated_reflected_attribute, Optional<FlyString> initial_value)
GC::Ref<SVGAnimatedString> SVGAnimatedString::create(JS::Realm& realm, GC::Ref<SVGElement> element, DOM::QualifiedName reflected_attribute, Optional<DOM::QualifiedName> deprecated_reflected_attribute, Optional<FlyString> initial_value)
{
return realm.create<SVGAnimatedString>(realm, element, move(reflected_attribute), move(deprecated_reflected_attribute), move(initial_value));
}
SVGAnimatedString::SVGAnimatedString(JS::Realm& realm, GC::Ref<SVGElement> element, FlyString reflected_attribute, Optional<FlyString> deprecated_reflected_attribute, Optional<FlyString> initial_value)
SVGAnimatedString::SVGAnimatedString(JS::Realm& realm, GC::Ref<SVGElement> element, DOM::QualifiedName reflected_attribute, Optional<DOM::QualifiedName> deprecated_reflected_attribute, Optional<FlyString> initial_value)
: Bindings::PlatformObject(realm)
, m_element(element)
, m_reflected_attribute(move(reflected_attribute))
@ -48,11 +48,11 @@ String SVGAnimatedString::base_val() const
{
// On getting baseVal or animVal, the following steps are run:
// 1. If the reflected attribute is not present, then:
if (!m_element->has_attribute(m_reflected_attribute)) {
if (!m_element->has_attribute_ns(m_reflected_attribute.namespace_(), m_reflected_attribute.local_name())) {
// 1. If the SVGAnimatedString object is defined to additionally reflect a second, deprecated attribute,
// and that attribute is present, then return its value.
if (m_deprecated_reflected_attribute.has_value()) {
if (auto attribute = m_element->get_attribute(m_deprecated_reflected_attribute.value()); attribute.has_value())
if (auto attribute = m_element->get_attribute_ns(m_deprecated_reflected_attribute->namespace_(), m_deprecated_reflected_attribute->local_name()); attribute.has_value())
return attribute.release_value();
}
@ -65,7 +65,7 @@ String SVGAnimatedString::base_val() const
}
// 2. Otherwise, the reflected attribute is present. Return its value.
return m_element->attribute(m_reflected_attribute).value();
return m_element->get_attribute_ns(m_reflected_attribute.namespace_(), m_reflected_attribute.local_name()).value();
}
// https://svgwg.org/svg2-draft/types.html#__svg__SVGAnimatedString__baseVal
@ -74,15 +74,15 @@ void SVGAnimatedString::set_base_val(String const& base_val)
// 1. If the reflected attribute is not present, the SVGAnimatedString object is defined to additionally reflect
// a second, deprecated attribute, and that deprecated attribute is present, then set that deprecated attribute
// to the specified value.
if (!m_element->has_attribute(m_reflected_attribute)
if (!m_element->has_attribute_ns(m_reflected_attribute.namespace_(), m_reflected_attribute.local_name())
&& m_deprecated_reflected_attribute.has_value()
&& m_element->has_attribute(m_deprecated_reflected_attribute.value())) {
MUST(m_element->set_attribute(m_deprecated_reflected_attribute.value(), base_val));
&& m_element->has_attribute_ns(m_deprecated_reflected_attribute->namespace_(), m_deprecated_reflected_attribute->local_name())) {
m_element->set_attribute_value(m_deprecated_reflected_attribute->local_name(), base_val, m_deprecated_reflected_attribute->prefix(), m_deprecated_reflected_attribute->namespace_());
return;
}
// 2. Otherwise, set the reflected attribute to the specified value.
MUST(m_element->set_attribute(m_reflected_attribute, base_val));
m_element->set_attribute_value(m_reflected_attribute.local_name(), base_val, m_reflected_attribute.prefix(), m_reflected_attribute.namespace_());
}
}

View file

@ -8,6 +8,7 @@
#include <AK/FlyString.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/DOM/QualifiedName.h>
#include <LibWeb/Export.h>
namespace Web::SVG {
@ -18,21 +19,21 @@ class WEB_API SVGAnimatedString final : public Bindings::PlatformObject {
GC_DECLARE_ALLOCATOR(SVGAnimatedString);
public:
[[nodiscard]] static GC::Ref<SVGAnimatedString> create(JS::Realm&, GC::Ref<SVGElement> element, FlyString reflected_attribute, Optional<FlyString> deprecated_reflected_attribute = {}, Optional<FlyString> initial_value = {});
[[nodiscard]] static GC::Ref<SVGAnimatedString> create(JS::Realm&, GC::Ref<SVGElement> element, DOM::QualifiedName reflected_attribute, Optional<DOM::QualifiedName> deprecated_reflected_attribute = {}, Optional<FlyString> initial_value = {});
virtual ~SVGAnimatedString() override;
String base_val() const;
void set_base_val(String const& base_val);
private:
SVGAnimatedString(JS::Realm&, GC::Ref<SVGElement> element, FlyString reflected_attribute, Optional<FlyString> deprecated_reflected_attribute, Optional<FlyString> initial_value);
SVGAnimatedString(JS::Realm&, GC::Ref<SVGElement> element, DOM::QualifiedName reflected_attribute, Optional<DOM::QualifiedName> deprecated_reflected_attribute, Optional<FlyString> initial_value);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
GC::Ref<SVGElement> m_element;
FlyString m_reflected_attribute;
Optional<FlyString> m_deprecated_reflected_attribute;
DOM::QualifiedName m_reflected_attribute;
Optional<DOM::QualifiedName> m_deprecated_reflected_attribute;
Optional<FlyString> m_initial_value;
};

View file

@ -279,7 +279,7 @@ GC::Ref<SVGAnimatedString> SVGElement::class_name()
{
// The className IDL attribute reflects the class attribute.
if (!m_class_name_animated_string)
m_class_name_animated_string = SVGAnimatedString::create(realm(), *this, AttributeNames::class_);
m_class_name_animated_string = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::class_, OptionalNone {}, OptionalNone {} });
return *m_class_name_animated_string;
}

View file

@ -55,7 +55,7 @@ void SVGFEBlendElement::attribute_changed(FlyString const& name, Optional<String
GC::Ref<SVGAnimatedString> SVGFEBlendElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}
@ -63,7 +63,7 @@ GC::Ref<SVGAnimatedString> SVGFEBlendElement::in1()
GC::Ref<SVGAnimatedString> SVGFEBlendElement::in2()
{
if (!m_in2)
m_in2 = SVGAnimatedString::create(realm(), *this, AttributeNames::in2);
m_in2 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in2, OptionalNone {}, OptionalNone {} });
return *m_in2;
}

View file

@ -35,7 +35,7 @@ void SVGFEColorMatrixElement::visit_edges(Cell::Visitor& visitor)
GC::Ref<SVGAnimatedString> SVGFEColorMatrixElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}
@ -62,7 +62,7 @@ GC::Ref<SVGAnimatedEnumeration> SVGFEColorMatrixElement::type() const
GC::Ref<SVGAnimatedString> SVGFEColorMatrixElement::values()
{
if (!m_values)
m_values = SVGAnimatedString::create(realm(), *this, AttributeNames::values);
m_values = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::values, OptionalNone {}, OptionalNone {} });
return *m_values;
}

View file

@ -70,7 +70,7 @@ void SVGFECompositeElement::attribute_changed(FlyString const& name, Optional<St
GC::Ref<SVGAnimatedString> SVGFECompositeElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}
@ -78,7 +78,7 @@ GC::Ref<SVGAnimatedString> SVGFECompositeElement::in1()
GC::Ref<SVGAnimatedString> SVGFECompositeElement::in2()
{
if (!m_in2)
m_in2 = SVGAnimatedString::create(realm(), *this, AttributeNames::in2);
m_in2 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in2, OptionalNone {}, OptionalNone {} });
return *m_in2;
}
@ -87,7 +87,7 @@ GC::Ref<SVGAnimatedString> SVGFECompositeElement::in2()
GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k1()
{
if (!m_k1)
m_k1 = SVGAnimatedNumber::create(realm(), *this, AttributeNames::k1, 0.f);
m_k1 = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::k1, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_k1;
}
@ -96,7 +96,7 @@ GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k1()
GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k2()
{
if (!m_k2)
m_k2 = SVGAnimatedNumber::create(realm(), *this, AttributeNames::k2, 0.f);
m_k2 = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::k2, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_k2;
}
@ -105,7 +105,7 @@ GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k2()
GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k3()
{
if (!m_k3)
m_k3 = SVGAnimatedNumber::create(realm(), *this, AttributeNames::k3, 0.f);
m_k3 = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::k3, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_k3;
}
@ -114,7 +114,7 @@ GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k3()
GC::Ref<SVGAnimatedNumber> SVGFECompositeElement::k4()
{
if (!m_k4)
m_k4 = SVGAnimatedNumber::create(realm(), *this, AttributeNames::k4, 0.f);
m_k4 = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::k4, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_k4;
}

View file

@ -37,7 +37,7 @@ void SVGFEGaussianBlurElement::visit_edges(Cell::Visitor& visitor)
GC::Ref<SVGAnimatedString> SVGFEGaussianBlurElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}
@ -46,7 +46,7 @@ GC::Ref<SVGAnimatedString> SVGFEGaussianBlurElement::in1()
GC::Ref<SVGAnimatedNumber> SVGFEGaussianBlurElement::std_deviation_x()
{
if (!m_std_deviation_x) {
m_std_deviation_x = SVGAnimatedNumber::create(realm(), *this, AttributeNames::stdDeviation, 0.f,
m_std_deviation_x = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::stdDeviation, OptionalNone {}, OptionalNone {} }, 0.f,
SVGAnimatedNumber::SupportsSecondValue::Yes, SVGAnimatedNumber::ValueRepresented::First);
}
return *m_std_deviation_x;
@ -56,7 +56,7 @@ GC::Ref<SVGAnimatedNumber> SVGFEGaussianBlurElement::std_deviation_x()
GC::Ref<SVGAnimatedNumber> SVGFEGaussianBlurElement::std_deviation_y()
{
if (!m_std_deviation_y) {
m_std_deviation_y = SVGAnimatedNumber::create(realm(), *this, AttributeNames::stdDeviation, 0.f,
m_std_deviation_y = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::stdDeviation, OptionalNone {}, OptionalNone {} }, 0.f,
SVGAnimatedNumber::SupportsSecondValue::Yes, SVGAnimatedNumber::ValueRepresented::Second);
}
return *m_std_deviation_y;

View file

@ -32,7 +32,7 @@ void SVGFEMergeNodeElement::visit_edges(Cell::Visitor& visitor)
GC::Ref<SVGAnimatedString> SVGFEMergeNodeElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}

View file

@ -35,7 +35,7 @@ void SVGFEOffsetElement::visit_edges(Cell::Visitor& visitor)
GC::Ref<SVGAnimatedString> SVGFEOffsetElement::in1()
{
if (!m_in1)
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
m_in1 = SVGAnimatedString::create(realm(), *this, DOM::QualifiedName { AttributeNames::in, OptionalNone {}, OptionalNone {} });
return *m_in1;
}
@ -44,7 +44,7 @@ GC::Ref<SVGAnimatedString> SVGFEOffsetElement::in1()
GC::Ref<SVGAnimatedNumber> SVGFEOffsetElement::dx()
{
if (!m_dx) {
m_dx = SVGAnimatedNumber::create(realm(), *this, AttributeNames::dx, 0.f,
m_dx = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::dx, OptionalNone {}, OptionalNone {} }, 0.f,
SVGAnimatedNumber::SupportsSecondValue::Yes, SVGAnimatedNumber::ValueRepresented::First);
}
return *m_dx;
@ -54,7 +54,7 @@ GC::Ref<SVGAnimatedNumber> SVGFEOffsetElement::dx()
GC::Ref<SVGAnimatedNumber> SVGFEOffsetElement::dy()
{
if (!m_dy) {
m_dy = SVGAnimatedNumber::create(realm(), *this, AttributeNames::dy, 0.f,
m_dy = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::dy, OptionalNone {}, OptionalNone {} }, 0.f,
SVGAnimatedNumber::SupportsSecondValue::Yes, SVGAnimatedNumber::ValueRepresented::Second);
}
return *m_dy;

View file

@ -46,7 +46,7 @@ template<typename IncludingClass>
GC::Ref<SVGAnimatedString> SVGFilterPrimitiveStandardAttributes<IncludingClass>::result()
{
if (!m_result_animated_string)
m_result_animated_string = SVGAnimatedString::create(this_svg_element()->realm(), *this_svg_element(), AttributeNames::result);
m_result_animated_string = SVGAnimatedString::create(this_svg_element()->realm(), *this_svg_element(), DOM::QualifiedName { AttributeNames::result, OptionalNone {}, OptionalNone {} });
return *m_result_animated_string;
}

View file

@ -47,7 +47,7 @@ GC::Ref<Geometry::DOMPoint> SVGGeometryElement::get_point_at_length(float distan
GC::Ref<SVGAnimatedNumber> SVGGeometryElement::path_length()
{
if (!m_path_length)
m_path_length = SVGAnimatedNumber::create(realm(), *this, AttributeNames::pathLength, 0.f);
m_path_length = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::pathLength, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_path_length;
}

View file

@ -63,7 +63,7 @@ float SVGStopElement::stop_opacity() const
GC::Ref<SVGAnimatedNumber> SVGStopElement::offset()
{
if (!m_stop_offset)
m_stop_offset = SVGAnimatedNumber::create(realm(), *this, AttributeNames::offset, 0.f);
m_stop_offset = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::offset, OptionalNone {}, OptionalNone {} }, 0.f);
return *m_stop_offset;
}

View file

@ -6,6 +6,7 @@
#pragma once
#include <LibWeb/Namespace.h>
#include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/SVGAnimatedString.h>
@ -31,7 +32,12 @@ public:
// deprecated attribute.
if (!m_href_animated_string) {
auto& this_svg_element = as<SVGElement>(*this);
m_href_animated_string = SVGAnimatedString::create(this_svg_element.realm(), this_svg_element, AttributeNames::href, supports_xlink_href == SupportsXLinkHref::Yes ? Optional<FlyString> { AttributeNames::xlink_href } : OptionalNone {});
m_href_animated_string = SVGAnimatedString::create(this_svg_element.realm(),
this_svg_element,
DOM::QualifiedName { AttributeNames::href, OptionalNone {}, OptionalNone {} },
supports_xlink_href == SupportsXLinkHref::Yes
? Optional<DOM::QualifiedName> { DOM::QualifiedName { AttributeNames::href, "xlink"_fly_string, Namespace::XLink } }
: OptionalNone {});
}
return *m_href_animated_string;
}