diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/SVG/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/SVG/BUILD.gn index b68775eded..9446fbe52e 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/SVG/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/SVG/BUILD.gn @@ -6,6 +6,7 @@ source_set("SVG") { "AttributeParser.cpp", "SVGAnimatedLength.cpp", "SVGAnimatedNumber.cpp", + "SVGAnimatedString.cpp", "SVGCircleElement.cpp", "SVGClipPathElement.cpp", "SVGDecodedImageData.cpp", diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni index 4c19d81532..44930902e7 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni @@ -243,6 +243,7 @@ standard_idl_files = [ "//Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl", "//Userland/Libraries/LibWeb/SVG/SVGAnimatedLength.idl", "//Userland/Libraries/LibWeb/SVG/SVGAnimatedNumber.idl", + "//Userland/Libraries/LibWeb/SVG/SVGAnimatedString.idl", "//Userland/Libraries/LibWeb/SVG/SVGCircleElement.idl", "//Userland/Libraries/LibWeb/SVG/SVGClipPathElement.idl", "//Userland/Libraries/LibWeb/SVG/SVGDefsElement.idl", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 32fd5a534c..61fd1306c2 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -557,6 +557,7 @@ set(SOURCES SVG/AttributeParser.cpp SVG/SVGAnimatedLength.cpp SVG/SVGAnimatedNumber.cpp + SVG/SVGAnimatedString.cpp SVG/SVGClipPathElement.cpp SVG/SVGDecodedImageData.cpp SVG/SVGDefsElement.cpp diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.cpp b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.cpp new file mode 100644 index 0000000000..83d1c4a131 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Web::SVG { + +JS_DEFINE_ALLOCATOR(SVGAnimatedString); + +JS::NonnullGCPtr SVGAnimatedString::create(JS::Realm& realm, JS::NonnullGCPtr element, FlyString reflected_attribute, Optional deprecated_reflected_attribute, Optional initial_value) +{ + return realm.heap().allocate(realm, realm, element, move(reflected_attribute), move(deprecated_reflected_attribute), move(initial_value)); +} + +SVGAnimatedString::SVGAnimatedString(JS::Realm& realm, JS::NonnullGCPtr element, FlyString reflected_attribute, Optional deprecated_reflected_attribute, Optional initial_value) + : Bindings::PlatformObject(realm) + , m_element(element) + , m_reflected_attribute(move(reflected_attribute)) + , m_deprecated_reflected_attribute(move(deprecated_reflected_attribute)) + , m_initial_value(move(initial_value)) +{ +} + +SVGAnimatedString::~SVGAnimatedString() = default; + +void SVGAnimatedString::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + set_prototype(&Bindings::ensure_web_prototype(realm, "SVGAnimatedString"_fly_string)); +} + +void SVGAnimatedString::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_element); +} + +// https://svgwg.org/svg2-draft/types.html#__svg__SVGAnimatedString__baseVal +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)) { + // 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()) + return attribute.release_value(); + } + + // 2. Otherwise, if the reflected attribute has an initial value, then return it. + if (m_initial_value.has_value()) + return m_initial_value.value().to_string(); + + // 3. Otherwise, return the empty string. + return {}; + } + + // 2. Otherwise, the reflected attribute is present. Return its value. + return m_element->attribute(m_reflected_attribute).value(); +} + +// https://svgwg.org/svg2-draft/types.html#__svg__SVGAnimatedString__baseVal +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) + && 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)); + return; + } + + // 2. Otherwise, set the reflected attribute to the specified value. + MUST(m_element->set_attribute(m_reflected_attribute, base_val)); +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.h b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.h new file mode 100644 index 0000000000..b64777d27b --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::SVG { + +// https://svgwg.org/svg2-draft/types.html#InterfaceSVGAnimatedString +class SVGAnimatedString final : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(SVGAnimatedString, Bindings::PlatformObject); + JS_DECLARE_ALLOCATOR(SVGAnimatedString); + +public: + [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, JS::NonnullGCPtr element, FlyString reflected_attribute, Optional deprecated_reflected_attribute = {}, Optional initial_value = {}); + virtual ~SVGAnimatedString() override; + + String base_val() const; + void set_base_val(String const& base_val); + +private: + SVGAnimatedString(JS::Realm&, JS::NonnullGCPtr element, FlyString reflected_attribute, Optional deprecated_reflected_attribute, Optional initial_value); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + + JS::NonnullGCPtr m_element; + FlyString m_reflected_attribute; + Optional m_deprecated_reflected_attribute; + Optional m_initial_value; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.idl b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.idl new file mode 100644 index 0000000000..cae93f2ce5 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedString.idl @@ -0,0 +1,6 @@ +// https://svgwg.org/svg2-draft/types.html#InterfaceSVGAnimatedString +[Exposed=Window] +interface SVGAnimatedString { + attribute DOMString baseVal; + [ImplementedAs=base_val] readonly attribute DOMString animVal; +}; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index c19f950135..453e6a73e9 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -228,6 +228,7 @@ libweb_js_bindings(Streams/WritableStreamDefaultController) libweb_js_bindings(Streams/WritableStreamDefaultWriter) libweb_js_bindings(SVG/SVGAnimatedLength) libweb_js_bindings(SVG/SVGAnimatedNumber) +libweb_js_bindings(SVG/SVGAnimatedString) libweb_js_bindings(SVG/SVGClipPathElement) libweb_js_bindings(SVG/SVGDefsElement) libweb_js_bindings(SVG/SVGElement)