mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 03:37:43 +00:00
LibWeb: Implement the <use>
SVG element
The SVG <use> element is used to be able to reuse other SVG graphics without having to re-write the svg element. We now support this feature! :^)
This commit is contained in:
parent
b322abd8d0
commit
c7c3043aa2
10 changed files with 323 additions and 1 deletions
|
@ -523,6 +523,7 @@ set(SOURCES
|
||||||
SVG/SVGSVGElement.cpp
|
SVG/SVGSVGElement.cpp
|
||||||
SVG/SVGStopElement.cpp
|
SVG/SVGStopElement.cpp
|
||||||
SVG/SVGTextContentElement.cpp
|
SVG/SVGTextContentElement.cpp
|
||||||
|
SVG/SVGUseElement.cpp
|
||||||
SVG/TagNames.cpp
|
SVG/TagNames.cpp
|
||||||
SVG/ViewBox.cpp
|
SVG/ViewBox.cpp
|
||||||
Selection/Selection.cpp
|
Selection/Selection.cpp
|
||||||
|
|
|
@ -98,6 +98,7 @@
|
||||||
#include <LibWeb/SVG/SVGSVGElement.h>
|
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||||
#include <LibWeb/SVG/SVGStopElement.h>
|
#include <LibWeb/SVG/SVGStopElement.h>
|
||||||
#include <LibWeb/SVG/SVGTextContentElement.h>
|
#include <LibWeb/SVG/SVGTextContentElement.h>
|
||||||
|
#include <LibWeb/SVG/SVGUseElement.h>
|
||||||
#include <LibWeb/SVG/TagNames.h>
|
#include <LibWeb/SVG/TagNames.h>
|
||||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||||
|
|
||||||
|
@ -455,6 +456,8 @@ static WebIDL::ExceptionOr<JS::GCPtr<SVG::SVGElement>> create_svg_element(JS::Re
|
||||||
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGStopElement>(realm, document, move(qualified_name)));
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGStopElement>(realm, document, move(qualified_name)));
|
||||||
if (local_name == SVG::TagNames::text)
|
if (local_name == SVG::TagNames::text)
|
||||||
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGTextContentElement>(realm, document, move(qualified_name)));
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGTextContentElement>(realm, document, move(qualified_name)));
|
||||||
|
if (local_name == SVG::TagNames::use)
|
||||||
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGUseElement>(realm, document, move(qualified_name)));
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace Web::SVG::AttributeNames {
|
||||||
E(gradientTransform) \
|
E(gradientTransform) \
|
||||||
E(gradientUnits) \
|
E(gradientUnits) \
|
||||||
E(height) \
|
E(height) \
|
||||||
|
E(href) \
|
||||||
E(kernelMatrix) \
|
E(kernelMatrix) \
|
||||||
E(kernelUnitLength) \
|
E(kernelUnitLength) \
|
||||||
E(keyPoints) \
|
E(keyPoints) \
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
/*
|
/*
|
||||||
* 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>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/HTML/DOMStringMap.h>
|
#include <LibWeb/HTML/DOMStringMap.h>
|
||||||
#include <LibWeb/SVG/SVGElement.h>
|
#include <LibWeb/SVG/SVGElement.h>
|
||||||
|
#include <LibWeb/SVG/SVGUseElement.h>
|
||||||
|
|
||||||
namespace Web::SVG {
|
namespace Web::SVG {
|
||||||
|
|
||||||
|
@ -34,4 +37,56 @@ void SVGElement::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_dataset);
|
visitor.visit(m_dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SVGElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
|
||||||
|
{
|
||||||
|
Base::parse_attribute(name, value);
|
||||||
|
|
||||||
|
update_use_elements_that_reference_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGElement::inserted()
|
||||||
|
{
|
||||||
|
Base::inserted();
|
||||||
|
|
||||||
|
update_use_elements_that_reference_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGElement::children_changed()
|
||||||
|
{
|
||||||
|
Base::children_changed();
|
||||||
|
|
||||||
|
update_use_elements_that_reference_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGElement::update_use_elements_that_reference_this()
|
||||||
|
{
|
||||||
|
if (is<SVGUseElement>(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document().for_each_in_subtree_of_type<SVGUseElement>([this](SVGUseElement& use_element) {
|
||||||
|
use_element.svg_element_changed(*this);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGElement::removed_from(Node* parent)
|
||||||
|
{
|
||||||
|
Base::removed_from(parent);
|
||||||
|
|
||||||
|
remove_from_use_element_that_reference_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGElement::remove_from_use_element_that_reference_this()
|
||||||
|
{
|
||||||
|
if (is<SVGUseElement>(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document().for_each_in_subtree_of_type<SVGUseElement>([this](SVGUseElement& use_element) {
|
||||||
|
use_element.svg_element_removed(*this);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@ class SVGElement : public DOM::Element {
|
||||||
public:
|
public:
|
||||||
virtual bool requires_svg_container() const override { return true; }
|
virtual bool requires_svg_container() const override { return true; }
|
||||||
|
|
||||||
|
virtual void parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) override;
|
||||||
|
|
||||||
|
virtual void children_changed() override;
|
||||||
|
virtual void inserted() override;
|
||||||
|
virtual void removed_from(Node*) override;
|
||||||
|
|
||||||
HTML::DOMStringMap* dataset() { return m_dataset.ptr(); }
|
HTML::DOMStringMap* dataset() { return m_dataset.ptr(); }
|
||||||
HTML::DOMStringMap const* dataset() const { return m_dataset.ptr(); }
|
HTML::DOMStringMap const* dataset() const { return m_dataset.ptr(); }
|
||||||
|
|
||||||
|
@ -25,6 +31,9 @@ protected:
|
||||||
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
void update_use_elements_that_reference_this();
|
||||||
|
void remove_from_use_element_that_reference_this();
|
||||||
|
|
||||||
JS::GCPtr<HTML::DOMStringMap> m_dataset;
|
JS::GCPtr<HTML::DOMStringMap> m_dataset;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
177
Userland/Libraries/LibWeb/SVG/SVGUseElement.cpp
Normal file
177
Userland/Libraries/LibWeb/SVG/SVGUseElement.cpp
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Preston Taylor <95388976+PrestonLTaylor@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
|
#include <LibWeb/DOM/Document.h>
|
||||||
|
#include <LibWeb/DOM/ElementFactory.h>
|
||||||
|
#include <LibWeb/DOM/Event.h>
|
||||||
|
#include <LibWeb/DOM/ShadowRoot.h>
|
||||||
|
#include <LibWeb/Namespace.h>
|
||||||
|
#include <LibWeb/SVG/AttributeNames.h>
|
||||||
|
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||||
|
#include <LibWeb/SVG/SVGUseElement.h>
|
||||||
|
|
||||||
|
namespace Web::SVG {
|
||||||
|
|
||||||
|
SVGUseElement::SVGUseElement(DOM::Document& document, DOM::QualifiedName qualified_name)
|
||||||
|
: SVGGraphicsElement(document, qualified_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::ThrowCompletionOr<void> SVGUseElement::initialize(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
MUST_OR_THROW_OOM(Base::initialize(realm));
|
||||||
|
set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGUseElementPrototype>(realm, "SVGUseElement"));
|
||||||
|
|
||||||
|
// The shadow tree is open (inspectable by script), but read-only.
|
||||||
|
auto shadow_root = TRY(heap().allocate<DOM::ShadowRoot>(realm, document(), *this, Bindings::ShadowRootMode::Open));
|
||||||
|
|
||||||
|
// The user agent must create a use-element shadow tree whose host is the ‘use’ element itself
|
||||||
|
set_shadow_root(shadow_root);
|
||||||
|
|
||||||
|
m_document_observer = TRY(realm.heap().allocate<DOM::DocumentObserver>(realm, realm, document()));
|
||||||
|
m_document_observer->document_fully_loaded = [this]() {
|
||||||
|
clone_element_tree_as_our_shadow_tree(referenced_element());
|
||||||
|
};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGUseElement::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_document_observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGUseElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
|
||||||
|
{
|
||||||
|
Base::parse_attribute(name, value);
|
||||||
|
|
||||||
|
// https://svgwg.org/svg2-draft/struct.html#UseLayout
|
||||||
|
if (name == SVG::AttributeNames::x) {
|
||||||
|
m_x = AttributeParser::parse_coordinate(value);
|
||||||
|
} else if (name == SVG::AttributeNames::y) {
|
||||||
|
m_y = AttributeParser::parse_coordinate(value);
|
||||||
|
} else if (name == SVG::AttributeNames::href) {
|
||||||
|
// FIXME: Support the xlink:href attribute as a fallback
|
||||||
|
m_referenced_id = parse_id_from_href(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<StringView> SVGUseElement::parse_id_from_href(DeprecatedString const& href)
|
||||||
|
{
|
||||||
|
auto id_seperator = href.find('#');
|
||||||
|
if (!id_seperator.has_value()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return href.substring_view(id_seperator.value() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGUseElement::inserted()
|
||||||
|
{
|
||||||
|
Base::inserted();
|
||||||
|
|
||||||
|
// The x and y properties define an additional transformation (translate(x,y), where x and y represent the computed value of the corresponding property)
|
||||||
|
// to be applied to the ‘use’ element, after any transformations specified with other properties
|
||||||
|
m_transform.translate(m_x.value_or(0), m_y.value_or(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGUseElement::svg_element_changed(SVGElement& svg_element)
|
||||||
|
{
|
||||||
|
auto to_clone = referenced_element();
|
||||||
|
if (!to_clone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: We need to check the ancestor because parse_attribute of a child doesn't call children_changed on the parent(s)
|
||||||
|
if (to_clone == &svg_element || to_clone->is_ancestor_of(svg_element)) {
|
||||||
|
clone_element_tree_as_our_shadow_tree(to_clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGUseElement::svg_element_removed(SVGElement& svg_element)
|
||||||
|
{
|
||||||
|
if (!m_referenced_id.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svg_element.attribute("id"sv).matches(m_referenced_id.value())) {
|
||||||
|
shadow_root()->remove_all_children();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::GCPtr<DOM::Element> SVGUseElement::referenced_element()
|
||||||
|
{
|
||||||
|
if (!m_referenced_id.has_value()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Support loading of external svg documents
|
||||||
|
return document().get_element_by_id(m_referenced_id.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://svgwg.org/svg2-draft/struct.html#UseShadowTree
|
||||||
|
void SVGUseElement::clone_element_tree_as_our_shadow_tree(Element* to_clone) const
|
||||||
|
{
|
||||||
|
shadow_root()->remove_all_children();
|
||||||
|
|
||||||
|
if (to_clone && is_valid_reference_element(to_clone)) {
|
||||||
|
// The ‘use’ element references another element, a copy of which is rendered in place of the ‘use’ in the document.
|
||||||
|
auto cloned_reference_node = to_clone->clone_node(nullptr, true);
|
||||||
|
shadow_root()->append_child(cloned_reference_node).release_value_but_fixme_should_propagate_errors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGUseElement::is_valid_reference_element(Element* reference_element) const
|
||||||
|
{
|
||||||
|
// If the referenced element that results from resolving the URL is not an SVG element, then the reference is invalid and the ‘use’ element is in error.
|
||||||
|
// If the referenced element is a (shadow-including) ancestor of the ‘use’ element, then this is an invalid circular reference and the ‘use’ element is in error.
|
||||||
|
return reference_element->is_svg_element() && !reference_element->is_ancestor_of(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/shapes.html#RectElementXAttribute
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGUseElement::x() const
|
||||||
|
{
|
||||||
|
// FIXME: Populate the unit type when it is parsed (0 here is "unknown").
|
||||||
|
// FIXME: Create a proper animated value when animations are supported.
|
||||||
|
auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGUseElement::y() const
|
||||||
|
{
|
||||||
|
// FIXME: Populate the unit type when it is parsed (0 here is "unknown").
|
||||||
|
// FIXME: Create a proper animated value when animations are supported.
|
||||||
|
auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGUseElement::width() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGUseElement::height() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://svgwg.org/svg2-draft/struct.html#TermInstanceRoot
|
||||||
|
JS::GCPtr<SVGElement> SVGUseElement::instance_root() const
|
||||||
|
{
|
||||||
|
return shadow_root()->first_child_of_type<SVGElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::GCPtr<SVGElement> SVGUseElement::animated_instance_root() const
|
||||||
|
{
|
||||||
|
return instance_root();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
58
Userland/Libraries/LibWeb/SVG/SVGUseElement.h
Normal file
58
Userland/Libraries/LibWeb/SVG/SVGUseElement.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Preston Taylor <95388976+PrestonLTaylor@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGfx/Path.h>
|
||||||
|
#include <LibWeb/DOM/DocumentObserver.h>
|
||||||
|
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||||
|
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||||
|
|
||||||
|
namespace Web::SVG {
|
||||||
|
|
||||||
|
class SVGUseElement final : public SVGGraphicsElement {
|
||||||
|
WEB_PLATFORM_OBJECT(SVGUseElement, SVGGraphicsElement);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~SVGUseElement() override = default;
|
||||||
|
|
||||||
|
virtual void parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) override;
|
||||||
|
|
||||||
|
virtual void inserted() override;
|
||||||
|
|
||||||
|
void svg_element_changed(SVGElement&);
|
||||||
|
void svg_element_removed(SVGElement&);
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> x() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> y() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> width() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> height() const;
|
||||||
|
|
||||||
|
JS::GCPtr<SVGElement> instance_root() const;
|
||||||
|
JS::GCPtr<SVGElement> animated_instance_root() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGUseElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
||||||
|
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
Optional<StringView> parse_id_from_href(DeprecatedString const& href);
|
||||||
|
|
||||||
|
JS::GCPtr<DOM::Element> referenced_element();
|
||||||
|
|
||||||
|
void clone_element_tree_as_our_shadow_tree(Element* to_clone) const;
|
||||||
|
bool is_valid_reference_element(Element* reference_element) const;
|
||||||
|
|
||||||
|
Optional<float> m_x;
|
||||||
|
Optional<float> m_y;
|
||||||
|
|
||||||
|
Optional<StringView> m_referenced_id;
|
||||||
|
|
||||||
|
JS::GCPtr<DOM::DocumentObserver> m_document_observer;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
16
Userland/Libraries/LibWeb/SVG/SVGUseElement.idl
Normal file
16
Userland/Libraries/LibWeb/SVG/SVGUseElement.idl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#import <SVG/SVGAnimatedLength.idl>
|
||||||
|
#import <SVG/SVGElement.idl>
|
||||||
|
#import <SVG/SVGGraphicsElement.idl>
|
||||||
|
|
||||||
|
// https://svgwg.org/svg2-draft/struct.html#InterfaceSVGUseElement
|
||||||
|
[Exposed=Window]
|
||||||
|
interface SVGUseElement : SVGGraphicsElement {
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength x;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength y;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength width;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength height;
|
||||||
|
[SameObject] readonly attribute SVGElement? instanceRoot;
|
||||||
|
[SameObject] readonly attribute SVGElement? animatedInstanceRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: SVGUseElement includes SVGURIReference;
|
|
@ -33,7 +33,8 @@ namespace Web::SVG::TagNames {
|
||||||
__ENUMERATE_SVG_TAG(radialGradient) \
|
__ENUMERATE_SVG_TAG(radialGradient) \
|
||||||
__ENUMERATE_SVG_TAG(script) \
|
__ENUMERATE_SVG_TAG(script) \
|
||||||
__ENUMERATE_SVG_TAG(stop) \
|
__ENUMERATE_SVG_TAG(stop) \
|
||||||
__ENUMERATE_SVG_TAG(title)
|
__ENUMERATE_SVG_TAG(title) \
|
||||||
|
__ENUMERATE_SVG_TAG(use)
|
||||||
|
|
||||||
#define __ENUMERATE_SVG_TAG(name) extern DeprecatedFlyString name;
|
#define __ENUMERATE_SVG_TAG(name) extern DeprecatedFlyString name;
|
||||||
ENUMERATE_SVG_TAGS
|
ENUMERATE_SVG_TAGS
|
||||||
|
|
|
@ -212,6 +212,7 @@ libweb_js_bindings(SVG/SVGRectElement)
|
||||||
libweb_js_bindings(SVG/SVGSVGElement)
|
libweb_js_bindings(SVG/SVGSVGElement)
|
||||||
libweb_js_bindings(SVG/SVGStopElement)
|
libweb_js_bindings(SVG/SVGStopElement)
|
||||||
libweb_js_bindings(SVG/SVGTextContentElement)
|
libweb_js_bindings(SVG/SVGTextContentElement)
|
||||||
|
libweb_js_bindings(SVG/SVGUseElement)
|
||||||
libweb_js_bindings(Selection/Selection)
|
libweb_js_bindings(Selection/Selection)
|
||||||
libweb_js_bindings(UIEvents/FocusEvent)
|
libweb_js_bindings(UIEvents/FocusEvent)
|
||||||
libweb_js_bindings(UIEvents/KeyboardEvent)
|
libweb_js_bindings(UIEvents/KeyboardEvent)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue