LibWeb: Implement SVGLength's read-only property

An SVGLength can be read-only, e.g. all animVal values cannot be
modified. Implement this for all instantiations of SVGLength.

While we're here, add `fake_animated_length_fixme()` so we can easily
find all sites where we need to improve our animated length game.
This commit is contained in:
Jelle Raaijmakers 2025-08-26 16:51:46 +02:00 committed by Jelle Raaijmakers
commit 676f5837b3
Notes: github-actions[bot] 2025-08-27 09:51:30 +00:00
16 changed files with 175 additions and 113 deletions

View file

@ -14,16 +14,21 @@ GC_DEFINE_ALLOCATOR(SVGAnimatedLength);
GC::Ref<SVGAnimatedLength> SVGAnimatedLength::create(JS::Realm& realm, GC::Ref<SVGLength> base_val, GC::Ref<SVGLength> anim_val) GC::Ref<SVGAnimatedLength> SVGAnimatedLength::create(JS::Realm& realm, GC::Ref<SVGLength> base_val, GC::Ref<SVGLength> anim_val)
{ {
return realm.create<SVGAnimatedLength>(realm, move(base_val), move(anim_val)); return realm.create<SVGAnimatedLength>(realm, base_val, anim_val);
} }
SVGAnimatedLength::SVGAnimatedLength(JS::Realm& realm, GC::Ref<SVGLength> base_val, GC::Ref<SVGLength> anim_val) SVGAnimatedLength::SVGAnimatedLength(JS::Realm& realm, GC::Ref<SVGLength> base_val, GC::Ref<SVGLength> anim_val)
: PlatformObject(realm) : PlatformObject(realm)
, m_base_val(move(base_val)) , m_base_val(base_val)
, m_anim_val(move(anim_val)) , m_anim_val(anim_val)
{ {
// The object referenced by animVal will always be distinct from the one referenced by baseVal, even when the attribute is not animated. // The object referenced by animVal will always be distinct from the one referenced by baseVal, even when the
// attribute is not animated.
VERIFY(m_base_val.ptr() != m_anim_val.ptr()); VERIFY(m_base_val.ptr() != m_anim_val.ptr());
// https://svgwg.org/svg2-draft/types.html#InterfaceSVGLength
// SVGLength objects reflected through the animVal IDL attribute are always read only.
VERIFY(m_anim_val->read_only() == SVGLength::ReadOnly::Yes);
} }
SVGAnimatedLength::~SVGAnimatedLength() = default; SVGAnimatedLength::~SVGAnimatedLength() = default;
@ -34,7 +39,7 @@ void SVGAnimatedLength::initialize(JS::Realm& realm)
Base::initialize(realm); Base::initialize(realm);
} }
void SVGAnimatedLength::visit_edges(Cell::Visitor& visitor) void SVGAnimatedLength::visit_edges(Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_base_val); visitor.visit(m_base_val);

View file

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2023, Preston Taylor <95388976+PrestonLTaylor@users.noreply.github.com> * Copyright (c) 2023, Preston Taylor <95388976+PrestonLTaylor@users.noreply.github.com>
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -298,17 +299,28 @@ GC::Ptr<SVGElement> SVGElement::viewport_element()
return nullptr; return nullptr;
} }
GC::Ref<SVGAnimatedLength> SVGElement::fake_animated_length_fixme() const
{
// FIXME: All callers of this method must implement their animated length correctly.
auto base_length = SVGLength::create(realm(), 0, 0, SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, 0, SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), base_length, anim_length);
}
GC::Ref<SVGAnimatedLength> SVGElement::svg_animated_length_for_property(CSS::PropertyID property) const GC::Ref<SVGAnimatedLength> SVGElement::svg_animated_length_for_property(CSS::PropertyID property) const
{ {
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto make_length = [&] { auto make_length = [&](SVGLength::ReadOnly read_only) {
if (auto const computed_properties = this->computed_properties()) { if (auto const computed_properties = this->computed_properties()) {
if (auto length = computed_properties->length_percentage(property); length.has_value()) if (auto length = computed_properties->length_percentage(property); length.has_value())
return SVGLength::from_length_percentage(realm(), *length); return SVGLength::from_length_percentage(realm(), *length, read_only);
} }
return SVGLength::create(realm(), 0, 0.0f); return SVGLength::create(realm(), 0, 0, read_only);
}; };
return SVGAnimatedLength::create(realm(), make_length(), make_length()); return SVGAnimatedLength::create(
realm(),
make_length(SVGLength::ReadOnly::No),
make_length(SVGLength::ReadOnly::Yes));
} }
} }

View file

@ -30,6 +30,7 @@ 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;
GC::Ref<SVGAnimatedLength> fake_animated_length_fixme() const;
GC::Ref<SVGAnimatedLength> svg_animated_length_for_property(CSS::PropertyID) const; GC::Ref<SVGAnimatedLength> svg_animated_length_for_property(CSS::PropertyID) const;
virtual bool is_presentational_hint(FlyString const&) const override; virtual bool is_presentational_hint(FlyString const&) const override;

View file

@ -81,9 +81,9 @@ GC::Ref<SVGAnimatedLength> SVGEllipseElement::cx() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_center_x.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_center_x.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_center_x.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_center_x.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementCYAttribute // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementCYAttribute
@ -91,9 +91,9 @@ GC::Ref<SVGAnimatedLength> SVGEllipseElement::cy() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_center_y.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_center_y.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_center_y.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_center_y.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
@ -101,9 +101,9 @@ GC::Ref<SVGAnimatedLength> SVGEllipseElement::rx() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
@ -111,9 +111,9 @@ GC::Ref<SVGAnimatedLength> SVGEllipseElement::ry() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
} }

View file

@ -32,10 +32,10 @@ void SVGForeignObjectElement::initialize(JS::Realm& realm)
Base::initialize(realm); Base::initialize(realm);
// FIXME: These never actually get updated! // FIXME: These never actually get updated!
m_x = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0)); m_x = fake_animated_length_fixme();
m_y = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0)); m_y = fake_animated_length_fixme();
m_width = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0)); m_width = fake_animated_length_fixme();
m_height = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0)); m_height = fake_animated_length_fixme();
} }
void SVGForeignObjectElement::visit_edges(Cell::Visitor& visitor) void SVGForeignObjectElement::visit_edges(Cell::Visitor& visitor)

View file

@ -81,10 +81,8 @@ void SVGImageElement::attribute_changed(FlyString const& name, Optional<String>
// https://svgwg.org/svg2-draft/embedded.html#__svg__SVGImageElement__x // https://svgwg.org/svg2-draft/embedded.html#__svg__SVGImageElement__x
GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::x() GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::x()
{ {
if (!m_x) { if (!m_x)
auto& realm = this->realm(); m_x = fake_animated_length_fixme();
m_x = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0));
}
return *m_x; return *m_x;
} }
@ -92,10 +90,8 @@ GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::x()
// https://svgwg.org/svg2-draft/embedded.html#__svg__SVGImageElement__y // https://svgwg.org/svg2-draft/embedded.html#__svg__SVGImageElement__y
GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::y() GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::y()
{ {
if (!m_y) { if (!m_y)
auto& realm = this->realm(); m_y = fake_animated_length_fixme();
m_y = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, 0), SVGLength::create(realm, 0, 0));
}
return *m_y; return *m_y;
} }
@ -105,7 +101,10 @@ GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::width()
{ {
if (!m_width) { if (!m_width) {
auto& realm = this->realm(); auto& realm = this->realm();
m_width = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, intrinsic_width().value_or(0).to_double()), SVGLength::create(realm, 0, 0)); m_width = SVGAnimatedLength::create(
realm,
SVGLength::create(realm, 0, intrinsic_width().value_or(0).to_double(), SVGLength::ReadOnly::No),
SVGLength::create(realm, 0, 0, SVGLength::ReadOnly::Yes));
} }
return *m_width; return *m_width;
@ -116,7 +115,10 @@ GC::Ref<SVG::SVGAnimatedLength> SVGImageElement::height()
{ {
if (!m_height) { if (!m_height) {
auto& realm = this->realm(); auto& realm = this->realm();
m_height = SVGAnimatedLength::create(realm, SVGLength::create(realm, 0, intrinsic_height().value_or(0).to_double()), SVGLength::create(realm, 0, 0)); m_height = SVGAnimatedLength::create(
realm,
SVGLength::create(realm, 0, intrinsic_height().value_or(0).to_double(), SVGLength::ReadOnly::No),
SVGLength::create(realm, 0, 0, SVGLength::ReadOnly::Yes));
} }
return *m_height; return *m_height;

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -13,19 +14,20 @@ namespace Web::SVG {
GC_DEFINE_ALLOCATOR(SVGLength); GC_DEFINE_ALLOCATOR(SVGLength);
GC::Ref<SVGLength> SVGLength::create(JS::Realm& realm, u8 unit_type, float value) GC::Ref<SVGLength> SVGLength::create(JS::Realm& realm, u8 unit_type, float value, ReadOnly read_only)
{ {
return realm.create<SVGLength>(realm, unit_type, value); return realm.create<SVGLength>(realm, unit_type, value, read_only);
} }
GC::Ref<SVGLength> SVGLength::from_length_percentage(JS::Realm& realm, CSS::LengthPercentage const& length_percentage) GC::Ref<SVGLength> SVGLength::from_length_percentage(JS::Realm& realm, CSS::LengthPercentage const& length_percentage,
ReadOnly read_only)
{ {
// FIXME: We can't tell if a CSS::LengthPercentage was a unitless length. // FIXME: We can't tell if a CSS::LengthPercentage was a unitless length.
(void)SVG_LENGTHTYPE_NUMBER; (void)SVG_LENGTHTYPE_NUMBER;
if (length_percentage.is_percentage()) if (length_percentage.is_percentage())
return SVGLength::create(realm, SVG_LENGTHTYPE_PERCENTAGE, length_percentage.percentage().value()); return create(realm, SVG_LENGTHTYPE_PERCENTAGE, length_percentage.percentage().value(), read_only);
if (length_percentage.is_length()) if (length_percentage.is_length())
return SVGLength::create( return create(
realm, [&] { realm, [&] {
switch (length_percentage.length().type()) { switch (length_percentage.length().type()) {
case CSS::Length::Type::Em: case CSS::Length::Type::Em:
@ -48,14 +50,15 @@ GC::Ref<SVGLength> SVGLength::from_length_percentage(JS::Realm& realm, CSS::Leng
return SVG_LENGTHTYPE_UNKNOWN; return SVG_LENGTHTYPE_UNKNOWN;
} }
}(), }(),
length_percentage.length().raw_value()); length_percentage.length().raw_value(), read_only);
return SVGLength::create(realm, SVG_LENGTHTYPE_UNKNOWN, 0); return create(realm, SVG_LENGTHTYPE_UNKNOWN, 0, read_only);
} }
SVGLength::SVGLength(JS::Realm& realm, u8 unit_type, float value) SVGLength::SVGLength(JS::Realm& realm, u8 unit_type, float value, ReadOnly read_only)
: PlatformObject(realm) : PlatformObject(realm)
, m_unit_type(unit_type)
, m_value(value) , m_value(value)
, m_unit_type(unit_type)
, m_read_only(read_only)
{ {
} }
@ -67,11 +70,21 @@ void SVGLength::initialize(JS::Realm& realm)
SVGLength::~SVGLength() = default; SVGLength::~SVGLength() = default;
// https://www.w3.org/TR/SVG11/types.html#__svg__SVGLength__value // https://svgwg.org/svg2-draft/types.html#__svg__SVGLength__value
WebIDL::ExceptionOr<void> SVGLength::set_value(float value) WebIDL::ExceptionOr<void> SVGLength::set_value(float value)
{ {
// FIXME: Raise an exception if this <length> is read-only. // 1. If the SVGLength object is read only, then throw a NoModificationAllowedError.
if (m_read_only == ReadOnly::Yes)
return WebIDL::NoModificationAllowedError::create(realm(), "Cannot modify value of read-only SVGLength"_utf16);
// 2. Let value be the value being assigned to value.
// 3. Set the SVGLength's value to a <number> whose value is value.
m_value = value; m_value = value;
m_unit_type = SVG_LENGTHTYPE_NUMBER;
// FIXME: 4. If the SVGLength reflects the base value of a reflected attribute, reflects a presentation attribute, or
// reflects an element of the base value of a reflected attribute, then reserialize the reflected attribute.
return {}; return {};
} }

View file

@ -30,23 +30,32 @@ public:
static constexpr unsigned short SVG_LENGTHTYPE_PT = 9; static constexpr unsigned short SVG_LENGTHTYPE_PT = 9;
static constexpr unsigned short SVG_LENGTHTYPE_PC = 10; static constexpr unsigned short SVG_LENGTHTYPE_PC = 10;
[[nodiscard]] static GC::Ref<SVGLength> create(JS::Realm&, u8 unit_type, float value); enum class ReadOnly : u8 {
virtual ~SVGLength() override; Yes,
No,
};
u8 unit_type() const { return m_unit_type; } [[nodiscard]] static GC::Ref<SVGLength> create(JS::Realm&, u8 unit_type, float value, ReadOnly);
virtual ~SVGLength() override;
float value() const { return m_value; } float value() const { return m_value; }
WebIDL::ExceptionOr<void> set_value(float value); WebIDL::ExceptionOr<void> set_value(float value);
[[nodiscard]] static GC::Ref<SVGLength> from_length_percentage(JS::Realm&, CSS::LengthPercentage const&); u8 unit_type() const { return m_unit_type; }
ReadOnly read_only() const { return m_read_only; }
[[nodiscard]] static GC::Ref<SVGLength> from_length_percentage(JS::Realm&, CSS::LengthPercentage const&, ReadOnly);
private: private:
SVGLength(JS::Realm&, u8 unit_type, float value); SVGLength(JS::Realm&, u8 unit_type, float value, ReadOnly);
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
u8 m_unit_type { 0 };
float m_value { 0 }; float m_value { 0 };
u8 m_unit_type { 0 };
// https://svgwg.org/svg2-draft/types.html#ReadOnlyLength
ReadOnly m_read_only;
}; };
} }

View file

@ -66,9 +66,9 @@ GC::Ref<SVGAnimatedLength> SVGLineElement::x1() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_x1.value_or({ 0, false }).value()); auto base_length = SVGLength::create(realm(), 0, m_x1.value_or({ 0, false }).value(), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_x1.value_or({ 0, false }).value()); auto anim_length = SVGLength::create(realm(), 0, m_x1.value_or({ 0, false }).value(), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#LineElementY1Attribute // https://www.w3.org/TR/SVG11/shapes.html#LineElementY1Attribute
@ -76,9 +76,9 @@ GC::Ref<SVGAnimatedLength> SVGLineElement::y1() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_y1.value_or({ 0, false }).value()); auto base_length = SVGLength::create(realm(), 0, m_y1.value_or({ 0, false }).value(), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_y1.value_or({ 0, false }).value()); auto anim_length = SVGLength::create(realm(), 0, m_y1.value_or({ 0, false }).value(), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#LineElementX2Attribute // https://www.w3.org/TR/SVG11/shapes.html#LineElementX2Attribute
@ -86,9 +86,9 @@ GC::Ref<SVGAnimatedLength> SVGLineElement::x2() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_x2.value_or({ 0, false }).value()); auto base_length = SVGLength::create(realm(), 0, m_x2.value_or({ 0, false }).value(), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_x2.value_or({ 0, false }).value()); auto anim_length = SVGLength::create(realm(), 0, m_x2.value_or({ 0, false }).value(), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#LineElementY2Attribute // https://www.w3.org/TR/SVG11/shapes.html#LineElementY2Attribute
@ -96,9 +96,9 @@ GC::Ref<SVGAnimatedLength> SVGLineElement::y2() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_y2.value_or({ 0, false }).value()); auto base_length = SVGLength::create(realm(), 0, m_y2.value_or({ 0, false }).value(), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_y2.value_or({ 0, false }).value()); auto anim_length = SVGLength::create(realm(), 0, m_y2.value_or({ 0, false }).value(), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
} }

View file

@ -162,26 +162,22 @@ Optional<Painting::PaintStyle> SVGLinearGradientElement::to_gfx_paint_style(SVGP
GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::x1() const GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::x1() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::y1() const GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::y1() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::x2() const GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::x2() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::y2() const GC::Ref<SVGAnimatedLength> SVGLinearGradientElement::y2() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
} }

View file

@ -218,38 +218,32 @@ Optional<Painting::PaintStyle> SVGRadialGradientElement::to_gfx_paint_style(SVGP
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::cx() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::cx() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::cy() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::cy() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fx() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fx() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fy() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fy() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fr() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::fr() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::r() const GC::Ref<SVGAnimatedLength> SVGRadialGradientElement::r() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
} }

View file

@ -158,9 +158,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::x() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute
@ -168,9 +168,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::y() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
@ -178,9 +178,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::width() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_width.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_width.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_width.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_width.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
@ -188,9 +188,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::height() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_height.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_height.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_height.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_height.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute
@ -198,9 +198,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::rx() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementRYAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementRYAttribute
@ -208,9 +208,9 @@ GC::Ref<SVGAnimatedLength> SVGRectElement::ry() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
} }

View file

@ -274,7 +274,7 @@ void SVGSVGElement::deselect_all() const
GC::Ref<SVGLength> SVGSVGElement::create_svg_length() const GC::Ref<SVGLength> SVGSVGElement::create_svg_length() const
{ {
// A new, detached SVGLength object whose value is the unitless <number> 0. // A new, detached SVGLength object whose value is the unitless <number> 0.
return SVGLength::create(realm(), SVGLength::SVG_LENGTHTYPE_NUMBER, 0); return SVGLength::create(realm(), SVGLength::SVG_LENGTHTYPE_NUMBER, 0, SVGLength::ReadOnly::No);
} }
GC::Ref<Geometry::DOMPoint> SVGSVGElement::create_svg_point() const GC::Ref<Geometry::DOMPoint> SVGSVGElement::create_svg_point() const

View file

@ -215,9 +215,9 @@ GC::Ref<SVGAnimatedLength> SVGUseElement::x() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
// https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute // https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute
@ -225,21 +225,19 @@ GC::Ref<SVGAnimatedLength> SVGUseElement::y() const
{ {
// FIXME: Populate the unit type when it is parsed (0 here is "unknown"). // FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported. // FIXME: Create a proper animated value when animations are supported.
auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0)); auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0), SVGLength::ReadOnly::No);
auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0)); auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0), SVGLength::ReadOnly::Yes);
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); return SVGAnimatedLength::create(realm(), base_length, anim_length);
} }
GC::Ref<SVGAnimatedLength> SVGUseElement::width() const GC::Ref<SVGAnimatedLength> SVGUseElement::width() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
GC::Ref<SVGAnimatedLength> SVGUseElement::height() const GC::Ref<SVGAnimatedLength> SVGUseElement::height() const
{ {
// FIXME: Implement this properly. return fake_animated_length_fixme();
return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
} }
// https://svgwg.org/svg2-draft/struct.html#TermInstanceRoot // https://svgwg.org/svg2-draft/struct.html#TermInstanceRoot

View file

@ -0,0 +1,9 @@
rect.x.baseVal: 1
rect.x.animVal: 1
rect.y.baseVal: 2
rect.y.animVal: 2
rect.width.baseVal: 3
rect.width.animVal: 3
rect.height.baseVal: 4
rect.height.animVal: 4
Expected exception: NoModificationAllowedError: Cannot modify value of read-only SVGLength

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<svg xmlns="http://www.w3.org/2000/svg">
<rect id="rect" x="1" y="2" width="3" height="4" />
</svg>
<script>
test(() => {
println(`rect.x.baseVal: ${rect.x.baseVal.value}`);
println(`rect.x.animVal: ${rect.x.animVal.value}`);
println(`rect.y.baseVal: ${rect.y.baseVal.value}`);
println(`rect.y.animVal: ${rect.y.animVal.value}`);
println(`rect.width.baseVal: ${rect.width.baseVal.value}`);
println(`rect.width.animVal: ${rect.width.animVal.value}`);
println(`rect.height.baseVal: ${rect.height.baseVal.value}`);
println(`rect.height.animVal: ${rect.height.animVal.value}`);
try {
rect.x.animVal.value = 2;
} catch (e) {
println(`Expected exception: ${e}`);
}
});
</script>