LibWeb: Split SVGTextContentElement into spec defined subclasses

As part of this move properties/methods to the correct subclass
(position related properties go under SVGTextPositioningElement).

SVG text element hierarchy:

  SVGTextContentElement
           ^- SVGTextPositioningElement
                     ^- SVGTextElement
                     ^- SVGTSpanElement
           ^- SVGTextPathElement (TODO)
           ^- SVGTRefElement (TODO)
This commit is contained in:
MacDue 2023-07-22 19:24:55 +01:00 committed by Andreas Kling
parent 77e6dbab33
commit 0e12503586
14 changed files with 195 additions and 45 deletions

View file

@ -550,6 +550,8 @@ set(SOURCES
SVG/SVGStyleElement.cpp
SVG/SVGSymbolElement.cpp
SVG/SVGTextContentElement.cpp
SVG/SVGTextElement.cpp
SVG/SVGTextPositioningElement.cpp
SVG/SVGTitleElement.cpp
SVG/SVGUseElement.cpp
SVG/TagNames.cpp

View file

@ -99,7 +99,7 @@
#include <LibWeb/SVG/SVGStopElement.h>
#include <LibWeb/SVG/SVGStyleElement.h>
#include <LibWeb/SVG/SVGSymbolElement.h>
#include <LibWeb/SVG/SVGTextContentElement.h>
#include <LibWeb/SVG/SVGTextElement.h>
#include <LibWeb/SVG/SVGTitleElement.h>
#include <LibWeb/SVG/SVGUseElement.h>
#include <LibWeb/SVG/TagNames.h>
@ -462,7 +462,7 @@ static WebIDL::ExceptionOr<JS::GCPtr<SVG::SVGElement>> create_svg_element(JS::Re
if (local_name == SVG::TagNames::symbol)
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGSymbolElement>(realm, document, move(qualified_name)));
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::SVGTextElement>(realm, document, move(qualified_name)));
if (local_name == SVG::TagNames::title)
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGTitleElement>(realm, document, move(qualified_name)));
if (local_name == SVG::TagNames::use)

View file

@ -10,7 +10,7 @@
namespace Web::Layout {
SVGTextBox::SVGTextBox(DOM::Document& document, SVG::SVGTextContentElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
SVGTextBox::SVGTextBox(DOM::Document& document, SVG::SVGTextPositioningElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
: SVGGraphicsBox(document, element, properties)
{
}

View file

@ -8,7 +8,7 @@
#include <AK/Optional.h>
#include <LibWeb/Layout/SVGGraphicsBox.h>
#include <LibWeb/SVG/SVGTextContentElement.h>
#include <LibWeb/SVG/SVGTextPositioningElement.h>
namespace Web::Layout {
@ -16,11 +16,11 @@ class SVGTextBox final : public SVGGraphicsBox {
JS_CELL(SVGTextBox, SVGGraphicsBox);
public:
SVGTextBox(DOM::Document&, SVG::SVGTextContentElement&, NonnullRefPtr<CSS::StyleProperties>);
SVGTextBox(DOM::Document&, SVG::SVGTextPositioningElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~SVGTextBox() override = default;
SVG::SVGTextContentElement& dom_node() { return static_cast<SVG::SVGTextContentElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGTextContentElement const& dom_node() const { return static_cast<SVG::SVGTextContentElement const&>(SVGGraphicsBox::dom_node()); }
SVG::SVGTextPositioningElement& dom_node() { return static_cast<SVG::SVGTextPositioningElement&>(SVGGraphicsBox::dom_node()); }
SVG::SVGTextPositioningElement const& dom_node() const { return static_cast<SVG::SVGTextPositioningElement const&>(SVGGraphicsBox::dom_node()); }
Optional<Gfx::AffineTransform> layout_transform() const;

View file

@ -47,26 +47,6 @@ Optional<TextAnchor> SVGTextContentElement::text_anchor() const
}
}
void SVGTextContentElement::attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value)
{
SVGGraphicsElement::attribute_changed(name, value);
if (name == SVG::AttributeNames::x) {
m_x = AttributeParser::parse_coordinate(value).value_or(m_x);
} else if (name == SVG::AttributeNames::y) {
m_y = AttributeParser::parse_coordinate(value).value_or(m_y);
} else if (name == SVG::AttributeNames::dx) {
m_dx = AttributeParser::parse_coordinate(value).value_or(m_dx);
} else if (name == SVG::AttributeNames::dy) {
m_dy = AttributeParser::parse_coordinate(value).value_or(m_dy);
}
}
JS::GCPtr<Layout::Node> SVGTextContentElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return heap().allocate_without_realm<Layout::SVGTextBox>(document(), *this, move(style));
}
// https://svgwg.org/svg2-draft/text.html#__svg__SVGTextContentElement__getNumberOfChars
WebIDL::ExceptionOr<int> SVGTextContentElement::get_number_of_chars() const
{
@ -74,9 +54,4 @@ WebIDL::ExceptionOr<int> SVGTextContentElement::get_number_of_chars() const
return static_cast<int>(chars.size());
}
Gfx::FloatPoint SVGTextContentElement::get_offset() const
{
return { m_x + m_dx, m_y + m_dy };
}
}

View file

@ -17,26 +17,14 @@ class SVGTextContentElement : public SVGGraphicsElement {
WEB_PLATFORM_OBJECT(SVGTextContentElement, SVGGraphicsElement);
public:
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
virtual void attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value) override;
WebIDL::ExceptionOr<int> get_number_of_chars() const;
Gfx::FloatPoint get_offset() const;
Optional<TextAnchor> text_anchor() const;
protected:
SVGTextContentElement(DOM::Document&, DOM::QualifiedName);
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
private:
float m_x { 0 };
float m_y { 0 };
float m_dx { 0 };
float m_dy { 0 };
};
}

View file

@ -1,8 +1,24 @@
#import <SVG/SVGGraphicsElement.idl>
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextContentElement
[Exposed=Window]
interface SVGTextContentElement : SVGGraphicsElement {
long getNumberOfChars();
// lengthAdjust Types
const unsigned short LENGTHADJUST_UNKNOWN = 0;
const unsigned short LENGTHADJUST_SPACING = 1;
const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
// FIXME: [SameObject] readonly attribute SVGAnimatedLength textLength;
// FIXME:[SameObject] readonly attribute SVGAnimatedEnumeration lengthAdjust;
long getNumberOfChars();
// FIXME: float getComputedTextLength();
// FIXME: float getSubStringLength(unsigned long charnum, unsigned long nchars);
// FIXME: DOMPoint getStartPositionOfChar(unsigned long charnum);
// FIXME: DOMPoint getEndPositionOfChar(unsigned long charnum);
// FIXME: DOMRect getExtentOfChar(unsigned long charnum);
// FIXME: float getRotationOfChar(unsigned long charnum);
// FIXME: long getCharNumAtPosition(optional DOMPointInit point = {});
// FIXME: undefined selectSubString(unsigned long charnum, unsigned long nchars);
};

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Layout/SVGTextBox.h>
#include <LibWeb/SVG/SVGTextElement.h>
namespace Web::SVG {
SVGTextElement::SVGTextElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: SVGTextPositioningElement(document, move(qualified_name))
{
}
JS::ThrowCompletionOr<void> SVGTextElement::initialize(JS::Realm& realm)
{
MUST_OR_THROW_OOM(Base::initialize(realm));
set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGTextElementPrototype>(realm, "SVGTextElement"));
return {};
}
JS::GCPtr<Layout::Node> SVGTextElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
{
return heap().allocate_without_realm<Layout::SVGTextBox>(document(), *this, move(style));
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/SVG/SVGTextPositioningElement.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::SVG {
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextElement
class SVGTextElement : public SVGTextPositioningElement {
WEB_PLATFORM_OBJECT(SVGTextElement, SVGTextPositioningElement);
public:
virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
protected:
SVGTextElement(DOM::Document&, DOM::QualifiedName);
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
};
}

View file

@ -0,0 +1,6 @@
#import <SVG/SVGTextPositioningElement.idl>
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextElement
[Exposed=Window]
interface SVGTextElement : SVGTextPositioningElement {
};

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Utf16View.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/AttributeParser.h>
#include <LibWeb/SVG/SVGGeometryElement.h>
#include <LibWeb/SVG/SVGTextPositioningElement.h>
namespace Web::SVG {
SVGTextPositioningElement::SVGTextPositioningElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: SVGTextContentElement(document, move(qualified_name))
{
}
JS::ThrowCompletionOr<void> SVGTextPositioningElement::initialize(JS::Realm& realm)
{
MUST_OR_THROW_OOM(Base::initialize(realm));
set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGTextPositioningElementPrototype>(realm, "SVGTextPositioningElement"));
return {};
}
void SVGTextPositioningElement::attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value)
{
SVGGraphicsElement::attribute_changed(name, value);
if (name == SVG::AttributeNames::x) {
m_x = AttributeParser::parse_coordinate(value).value_or(m_x);
} else if (name == SVG::AttributeNames::y) {
m_y = AttributeParser::parse_coordinate(value).value_or(m_y);
} else if (name == SVG::AttributeNames::dx) {
m_dx = AttributeParser::parse_coordinate(value).value_or(m_dx);
} else if (name == SVG::AttributeNames::dy) {
m_dy = AttributeParser::parse_coordinate(value).value_or(m_dy);
}
}
Gfx::FloatPoint SVGTextPositioningElement::get_offset() const
{
return { m_x + m_dx, m_y + m_dy };
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/SVG/SVGTextContentElement.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::SVG {
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextPositioningElement
class SVGTextPositioningElement : public SVGTextContentElement {
WEB_PLATFORM_OBJECT(SVGTextPositioningElement, SVGTextContentElement);
public:
virtual void attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value) override;
Gfx::FloatPoint get_offset() const;
JS::NonnullGCPtr<SVGAnimatedLength> x() const;
JS::NonnullGCPtr<SVGAnimatedLength> y() const;
JS::NonnullGCPtr<SVGAnimatedLength> dx() const;
JS::NonnullGCPtr<SVGAnimatedLength> dy() const;
protected:
SVGTextPositioningElement(DOM::Document&, DOM::QualifiedName);
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
private:
float m_x { 0 };
float m_y { 0 };
float m_dx { 0 };
float m_dy { 0 };
};
}

View file

@ -0,0 +1,11 @@
#import <SVG/SVGTextContentElement.idl>
// https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextPositioningElement
[Exposed=Window]
interface SVGTextPositioningElement : SVGTextContentElement {
// FIXME: [SameObject] readonly attribute SVGAnimatedLengthList x;
// FIXME: [SameObject] readonly attribute SVGAnimatedLengthList y;
// FIXME: [SameObject] readonly attribute SVGAnimatedLengthList dx;
// FIXME: [SameObject] readonly attribute SVGAnimatedLengthList dy;
// FIXME: [SameObject] readonly attribute SVGAnimatedNumberList rotate;
};

View file

@ -225,6 +225,8 @@ libweb_js_bindings(SVG/SVGStopElement)
libweb_js_bindings(SVG/SVGStyleElement)
libweb_js_bindings(SVG/SVGSymbolElement)
libweb_js_bindings(SVG/SVGTextContentElement)
libweb_js_bindings(SVG/SVGTextElement)
libweb_js_bindings(SVG/SVGTextPositioningElement)
libweb_js_bindings(SVG/SVGTitleElement)
libweb_js_bindings(SVG/SVGUseElement)
libweb_js_bindings(Selection/Selection)