From ea41aba6c771cbe8e48fca07d856d3bcbf49b8a0 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Wed, 27 Aug 2025 22:17:33 +0100 Subject: [PATCH] LibWeb: Move common SVGViewport code into an SVGFitToViewBox class Special handling for SVGClipPathElement and SVGMaskElement, which use a a ViewBox and PreserveAspectRatio value internally, has been moved to `SVGFormattingContext`. --- Libraries/LibWeb/CMakeLists.txt | 1 + Libraries/LibWeb/Forward.h | 1 + .../LibWeb/Layout/SVGFormattingContext.cpp | 43 +++++++++++-------- Libraries/LibWeb/SVG/SVGCircleElement.cpp | 8 +++- Libraries/LibWeb/SVG/SVGClipPathElement.h | 11 +---- Libraries/LibWeb/SVG/SVGFitToViewBox.cpp | 43 +++++++++++++++++++ Libraries/LibWeb/SVG/SVGFitToViewBox.h | 35 +++++++++++++++ Libraries/LibWeb/SVG/SVGGraphicsElement.h | 8 ++++ Libraries/LibWeb/SVG/SVGMaskElement.h | 12 +----- Libraries/LibWeb/SVG/SVGSVGElement.cpp | 25 +++-------- Libraries/LibWeb/SVG/SVGSVGElement.h | 15 ++----- Libraries/LibWeb/SVG/SVGSymbolElement.cpp | 14 ++---- Libraries/LibWeb/SVG/SVGSymbolElement.h | 17 +------- Libraries/LibWeb/SVG/SVGViewElement.cpp | 20 ++------- Libraries/LibWeb/SVG/SVGViewElement.h | 14 +----- Libraries/LibWeb/SVG/SVGViewport.h | 29 ------------- 16 files changed, 144 insertions(+), 152 deletions(-) create mode 100644 Libraries/LibWeb/SVG/SVGFitToViewBox.cpp create mode 100644 Libraries/LibWeb/SVG/SVGFitToViewBox.h delete mode 100644 Libraries/LibWeb/SVG/SVGViewport.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 4075aa5359e..382c83c4f68 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -878,6 +878,7 @@ set(SOURCES SVG/SVGFEMergeNodeElement.cpp SVG/SVGFEOffsetElement.cpp SVG/SVGFilterElement.cpp + SVG/SVGFitToViewBox.cpp SVG/SVGForeignObjectElement.cpp SVG/SVGGElement.cpp SVG/SVGGeometryElement.cpp diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 26564ffa110..d83eb2ba4ad 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -1052,6 +1052,7 @@ class SVGFEBlendElement; class SVGFEFloodElement; class SVGFEGaussianBlurElement; class SVGFilterElement; +class SVGFitToViewBox; class SVGForeignObjectElement; class SVGGeometryElement; class SVGGraphicsElement; diff --git a/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index 55962f65150..d92841cde63 100644 --- a/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -177,7 +177,6 @@ void SVGFormattingContext::run(AvailableSpace const& available_space) // NOTE: SVG doesn't have a "formatting context" in the spec, but this is the most // obvious way to drive SVG layout in our engine at the moment. - auto const& svg_viewport = as(*context_box().dom_node()); auto& svg_box_state = m_state.get_mutable(context_box()); if (!this->context_box().root().document().is_decoded_svg()) { @@ -197,38 +196,48 @@ void SVGFormattingContext::run(AvailableSpace const& available_space) svg_box_state.set_has_definite_width(true); svg_box_state.set_has_definite_height(true); - auto viewbox = svg_viewport.view_box(); + auto* dom_node = context_box().dom_node(); + VERIFY(dom_node); + auto& svg_graphics_element = as(*dom_node); + auto active_view_box = svg_graphics_element.active_view_box(); // https://svgwg.org/svg2-draft/coords.html#ViewBoxAttribute - if (viewbox.has_value()) { - if (viewbox->width < 0 || viewbox->height < 0) { + if (active_view_box.has_value()) { + if (active_view_box->width < 0 || active_view_box->height < 0) { // A negative value for or is an error and invalidates the ‘viewBox’ attribute. - viewbox = {}; - } else if (viewbox->width == 0 || viewbox->height == 0) { + active_view_box = {}; + } else if (active_view_box->width == 0 || active_view_box->height == 0) { // A value of zero disables rendering of the element. return; } } m_current_viewbox_transform = m_parent_viewbox_transform; - if (viewbox.has_value()) { + if (active_view_box.has_value()) { // FIXME: This should allow just one of width or height to be specified. // E.g. We should be able to layout where height is unspecified/auto. if (!svg_box_state.has_definite_width() || !svg_box_state.has_definite_height()) { dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Attempting to layout indefinitely sized SVG with a viewbox -- this likely won't work!"); } - auto scale_width = svg_box_state.has_definite_width() ? svg_box_state.content_width() / viewbox->width : 1; - auto scale_height = svg_box_state.has_definite_height() ? svg_box_state.content_height() / viewbox->height : 1; + auto scale_width = svg_box_state.has_definite_width() ? svg_box_state.content_width() / active_view_box->width : 1; + auto scale_height = svg_box_state.has_definite_height() ? svg_box_state.content_height() / active_view_box->height : 1; // The initial value for preserveAspectRatio is xMidYMid meet. - auto preserve_aspect_ratio = svg_viewport.preserve_aspect_ratio().value_or(SVG::PreserveAspectRatio {}); - auto viewbox_offset_and_scale = scale_and_align_viewbox_content(preserve_aspect_ratio, *viewbox, { scale_width, scale_height }, svg_box_state); + SVG::PreserveAspectRatio preserve_aspect_ratio {}; + if (auto* view_box = as_if(*dom_node)) { + preserve_aspect_ratio = view_box->preserve_aspect_ratio().value_or(SVG::PreserveAspectRatio {}); + } else if (is(*dom_node) || is(*dom_node)) { + // This allows mask and clipPath elements to be scaled in the x and y directions independently to match the target size. + preserve_aspect_ratio = SVG::PreserveAspectRatio { SVG::PreserveAspectRatio::Align::None, {} }; + } + + auto viewbox_offset_and_scale = scale_and_align_viewbox_content(preserve_aspect_ratio, *active_view_box, { scale_width, scale_height }, svg_box_state); CSSPixelPoint offset = viewbox_offset_and_scale.offset; m_current_viewbox_transform = Gfx::AffineTransform { m_current_viewbox_transform }.multiply(Gfx::AffineTransform {} .translate(offset.to_type()) .scale(viewbox_offset_and_scale.scale_factor_x, viewbox_offset_and_scale.scale_factor_y) - .translate({ -viewbox->min_x, -viewbox->min_y })); + .translate({ -active_view_box->min_x, -active_view_box->min_y })); } if (svg_box_state.has_definite_width() && svg_box_state.has_definite_height()) { @@ -246,8 +255,8 @@ void SVGFormattingContext::run(AvailableSpace const& available_space) } auto viewport_width = [&] { - if (viewbox.has_value()) - return CSSPixels::nearest_value_for(viewbox->width); + if (active_view_box.has_value()) + return CSSPixels::nearest_value_for(active_view_box->width); if (svg_box_state.has_definite_width()) return svg_box_state.content_width(); dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve width of SVG viewport!"); @@ -255,8 +264,8 @@ void SVGFormattingContext::run(AvailableSpace const& available_space) }(); auto viewport_height = [&] { - if (viewbox.has_value()) - return CSSPixels::nearest_value_for(viewbox->height); + if (active_view_box.has_value()) + return CSSPixels::nearest_value_for(active_view_box->height); if (svg_box_state.has_definite_height()) return svg_box_state.content_height(); dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve height of SVG viewport!"); @@ -275,7 +284,7 @@ void SVGFormattingContext::run(AvailableSpace const& available_space) void SVGFormattingContext::layout_svg_element(Box const& child) { - if (is(child.dom_node())) { + if (is(child.dom_node())) { layout_nested_viewport(child); } else if (is(child.dom_node()) && is(child)) { Layout::BlockFormattingContext bfc(m_state, m_layout_mode, static_cast(child), this); diff --git a/Libraries/LibWeb/SVG/SVGCircleElement.cpp b/Libraries/LibWeb/SVG/SVGCircleElement.cpp index e31a3efc2be..4203f553eea 100644 --- a/Libraries/LibWeb/SVG/SVGCircleElement.cpp +++ b/Libraries/LibWeb/SVG/SVGCircleElement.cpp @@ -12,7 +12,6 @@ #include #include #include -#include namespace Web::SVG { @@ -58,6 +57,13 @@ void SVGCircleElement::apply_presentational_hints(GC::Refset_property_from_presentational_hint(CSS::PropertyID::R, r_value.release_nonnull()); } +static CSSPixels normalized_diagonal_length(CSSPixelSize viewport_size) +{ + if (viewport_size.width() == viewport_size.height()) + return viewport_size.width(); + return sqrt((viewport_size.width() * viewport_size.width()) + (viewport_size.height() * viewport_size.height())) / CSSPixels::nearest_value_for(AK::Sqrt2); +} + Gfx::Path SVGCircleElement::get_path(CSSPixelSize viewport_size) { auto node = layout_node(); diff --git a/Libraries/LibWeb/SVG/SVGClipPathElement.h b/Libraries/LibWeb/SVG/SVGClipPathElement.h index a6b44266d60..9c72c202c01 100644 --- a/Libraries/LibWeb/SVG/SVGClipPathElement.h +++ b/Libraries/LibWeb/SVG/SVGClipPathElement.h @@ -9,19 +9,17 @@ #include #include #include -#include namespace Web::SVG { -class SVGClipPathElement final : public SVGGraphicsElement - , public SVGViewport { +class SVGClipPathElement final : public SVGGraphicsElement { WEB_PLATFORM_OBJECT(SVGClipPathElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGClipPathElement); public: virtual ~SVGClipPathElement(); - virtual Optional view_box() const override + virtual Optional active_view_box() const override { // Same trick as SVGMaskElement. if (clip_path_units() == MaskContentUnits::ObjectBoundingBox) @@ -29,11 +27,6 @@ public: return {}; } - virtual Optional preserve_aspect_ratio() const override - { - return PreserveAspectRatio { PreserveAspectRatio::Align::None, {} }; - } - virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; ClipPathUnits clip_path_units() const diff --git a/Libraries/LibWeb/SVG/SVGFitToViewBox.cpp b/Libraries/LibWeb/SVG/SVGFitToViewBox.cpp new file mode 100644 index 00000000000..00914639c58 --- /dev/null +++ b/Libraries/LibWeb/SVG/SVGFitToViewBox.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, Tim Ledbetter + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Web::SVG { + +void SVGFitToViewBox::initialize(JS::Realm& realm) +{ + m_view_box_for_bindings = realm.create(realm); +} + +void SVGFitToViewBox::visit_edges(JS::Cell::Visitor& visitor) +{ + visitor.visit(m_view_box_for_bindings); +} + +void SVGFitToViewBox::attribute_changed(DOM::Element&, FlyString const& name, Optional const& value) +{ + if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) { + if (!value.has_value()) { + m_view_box_for_bindings->set_nulled(true); + } else { + m_view_box = try_parse_view_box(value.value_or(String {})); + m_view_box_for_bindings->set_nulled(!m_view_box.has_value()); + if (m_view_box.has_value()) { + m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); + m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); + } + } + } else if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio)) { + m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {})); + } +} + +} diff --git a/Libraries/LibWeb/SVG/SVGFitToViewBox.h b/Libraries/LibWeb/SVG/SVGFitToViewBox.h new file mode 100644 index 00000000000..bd9dd324b43 --- /dev/null +++ b/Libraries/LibWeb/SVG/SVGFitToViewBox.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025, Tim Ledbetter + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::SVG { + +class SVGFitToViewBox { +public: + virtual ~SVGFitToViewBox() = default; + + GC::Ref view_box_for_bindings() const { return *m_view_box_for_bindings; } + Optional view_box() const { return m_view_box; } + Optional preserve_aspect_ratio() const { return m_preserve_aspect_ratio; } + +protected: + void initialize(JS::Realm&); + void visit_edges(JS::Cell::Visitor&); + void attribute_changed(DOM::Element& element, FlyString const& name, Optional const& value); + +private: + Optional m_view_box; + GC::Ptr m_view_box_for_bindings; + Optional m_preserve_aspect_ratio; +}; + +} diff --git a/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Libraries/LibWeb/SVG/SVGGraphicsElement.h index 51b9051a2cd..de7b17198bc 100644 --- a/Libraries/LibWeb/SVG/SVGGraphicsElement.h +++ b/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,13 @@ public: Optional fill_rule() const; Optional clip_rule() const; + virtual Optional active_view_box() const + { + if (auto* svg_fit_to_view_box = as_if(*this)) + return svg_fit_to_view_box->view_box(); + return {}; + } + float visible_stroke_width() const { if (auto color = stroke_color(); color.has_value() && color->alpha() > 0) diff --git a/Libraries/LibWeb/SVG/SVGMaskElement.h b/Libraries/LibWeb/SVG/SVGMaskElement.h index a3158c6e9df..624129c2346 100644 --- a/Libraries/LibWeb/SVG/SVGMaskElement.h +++ b/Libraries/LibWeb/SVG/SVGMaskElement.h @@ -8,12 +8,10 @@ #include #include -#include namespace Web::SVG { -class SVGMaskElement final : public SVGGraphicsElement - , public SVGViewport { +class SVGMaskElement final : public SVGGraphicsElement { WEB_PLATFORM_OBJECT(SVGMaskElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGMaskElement); @@ -21,7 +19,7 @@ class SVGMaskElement final : public SVGGraphicsElement public: virtual ~SVGMaskElement() override; - virtual Optional view_box() const override + virtual Optional active_view_box() const override { // maskContentUnits = objectBoundingBox acts like the mask is sized to the bounding box // of the target element, with a viewBox of "0 0 1 1". @@ -30,12 +28,6 @@ public: return {}; } - virtual Optional preserve_aspect_ratio() const override - { - // preserveAspectRatio = none (allow mask to be scaled in both x and y to match target size) - return PreserveAspectRatio { PreserveAspectRatio::Align::None, {} }; - } - virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; virtual GC::Ptr create_layout_node(GC::Ref) override; diff --git a/Libraries/LibWeb/SVG/SVGSVGElement.cpp b/Libraries/LibWeb/SVG/SVGSVGElement.cpp index a541e79ea59..05a71cceb23 100644 --- a/Libraries/LibWeb/SVG/SVGSVGElement.cpp +++ b/Libraries/LibWeb/SVG/SVGSVGElement.cpp @@ -34,14 +34,14 @@ void SVGSVGElement::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGSVGElement); Base::initialize(realm); - m_view_box_for_bindings = realm.create(realm); + SVGFitToViewBox::initialize(realm); } void SVGSVGElement::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); + SVGFitToViewBox::visit_edges(visitor); visitor.visit(m_active_view_element); - visitor.visit(m_view_box_for_bindings); } GC::Ptr SVGSVGElement::create_layout_node(GC::Ref style) @@ -120,21 +120,8 @@ void SVGSVGElement::apply_presentational_hints(GC::Ref void SVGSVGElement::attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) { Base::attribute_changed(name, old_value, value, namespace_); + SVGFitToViewBox::attribute_changed(*this, name, value); - if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) { - if (!value.has_value()) { - m_view_box_for_bindings->set_nulled(true); - } else { - m_view_box = try_parse_view_box(value.value_or(String {})); - m_view_box_for_bindings->set_nulled(!m_view_box.has_value()); - if (m_view_box.has_value()) { - m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - } - } - } - if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio)) - m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {})); if (name.equals_ignoring_ascii_case(SVG::AttributeNames::width) || name.equals_ignoring_ascii_case(SVG::AttributeNames::height)) update_fallback_view_box_for_svg_as_image(); } @@ -188,13 +175,13 @@ void SVGSVGElement::set_fallback_view_box_for_svg_as_image(Optional vie m_fallback_view_box_for_svg_as_image = view_box; } -Optional SVGSVGElement::view_box() const +Optional SVGSVGElement::active_view_box() const { if (m_active_view_element && m_active_view_element->view_box().has_value()) return m_active_view_element->view_box().value(); - if (m_view_box.has_value()) - return m_view_box; + if (auto view_box = SVGFitToViewBox::view_box(); view_box.has_value()) + return view_box; // NOTE: If the parent is a document, we're an element used as an image. if (parent() && parent()->is_document() && m_fallback_view_box_for_svg_as_image.has_value()) diff --git a/Libraries/LibWeb/SVG/SVGSVGElement.h b/Libraries/LibWeb/SVG/SVGSVGElement.h index 8e65d9345f0..b4ac4fcc4c0 100644 --- a/Libraries/LibWeb/SVG/SVGSVGElement.h +++ b/Libraries/LibWeb/SVG/SVGSVGElement.h @@ -11,17 +11,17 @@ #include #include #include +#include #include #include #include -#include #include #include namespace Web::SVG { class SVGSVGElement final : public SVGGraphicsElement - , public SVGViewport { + , public SVGFitToViewBox { WEB_PLATFORM_OBJECT(SVGSVGElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGSVGElement); @@ -34,16 +34,12 @@ public: virtual bool requires_svg_container() const override { return false; } virtual bool is_svg_container() const override { return true; } - virtual Optional view_box() const override; + virtual Optional active_view_box() const override; void set_active_view_element(GC::Ptr view_element) { m_active_view_element = view_element; } - virtual Optional preserve_aspect_ratio() const override { return m_preserve_aspect_ratio; } - void set_fallback_view_box_for_svg_as_image(Optional); - GC::Ref view_box_for_bindings() { return *m_view_box_for_bindings; } - GC::Ref x() const; GC::Ref y() const; GC::Ref width() const; @@ -107,13 +103,8 @@ private: void update_fallback_view_box_for_svg_as_image(); - Optional m_view_box; - Optional m_preserve_aspect_ratio; - Optional m_fallback_view_box_for_svg_as_image; - GC::Ptr m_view_box_for_bindings; - GC::Ptr m_active_view_element; }; diff --git a/Libraries/LibWeb/SVG/SVGSymbolElement.cpp b/Libraries/LibWeb/SVG/SVGSymbolElement.cpp index b66c98b5fb9..c1bdfeb84a6 100644 --- a/Libraries/LibWeb/SVG/SVGSymbolElement.cpp +++ b/Libraries/LibWeb/SVG/SVGSymbolElement.cpp @@ -30,13 +30,13 @@ void SVGSymbolElement::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGSymbolElement); Base::initialize(realm); - m_view_box_for_bindings = realm.create(realm); + SVGFitToViewBox::initialize(realm); } void SVGSymbolElement::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); - visitor.visit(m_view_box_for_bindings); + SVGFitToViewBox::visit_edges(visitor); } bool SVGSymbolElement::is_presentational_hint(FlyString const& name) const @@ -66,15 +66,7 @@ void SVGSymbolElement::apply_presentational_hints(GC::Ref const& old_value, Optional const& value, Optional const& namespace_) { Base::attribute_changed(name, old_value, value, namespace_); - - if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) { - m_view_box = try_parse_view_box(value.value_or(String {})); - m_view_box_for_bindings->set_nulled(!m_view_box.has_value()); - if (m_view_box.has_value()) { - m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - } - } + SVGFitToViewBox::attribute_changed(*this, name, value); } bool SVGSymbolElement::is_direct_child_of_use_shadow_tree() const diff --git a/Libraries/LibWeb/SVG/SVGSymbolElement.h b/Libraries/LibWeb/SVG/SVGSymbolElement.h index e78321c612b..5d09f161817 100644 --- a/Libraries/LibWeb/SVG/SVGSymbolElement.h +++ b/Libraries/LibWeb/SVG/SVGSymbolElement.h @@ -6,13 +6,13 @@ #pragma once +#include #include -#include namespace Web::SVG { class SVGSymbolElement final : public SVGGraphicsElement - , public SVGViewport { + , public SVGFitToViewBox { WEB_PLATFORM_OBJECT(SVGSymbolElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGSymbolElement); @@ -22,15 +22,6 @@ public: virtual bool is_presentational_hint(FlyString const&) const override; virtual void apply_presentational_hints(GC::Ref) const override; - virtual Optional view_box() const override { return m_view_box; } - virtual Optional preserve_aspect_ratio() const override - { - // FIXME: Support the `preserveAspectRatio` attribute on . - return {}; - } - - GC::Ref view_box_for_bindings() { return *m_view_box_for_bindings; } - private: SVGSymbolElement(DOM::Document&, DOM::QualifiedName); @@ -42,10 +33,6 @@ private: bool is_direct_child_of_use_shadow_tree() const; virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; - - Optional m_view_box; - - GC::Ptr m_view_box_for_bindings; }; } diff --git a/Libraries/LibWeb/SVG/SVGViewElement.cpp b/Libraries/LibWeb/SVG/SVGViewElement.cpp index b1ab75e8de6..5078fd43242 100644 --- a/Libraries/LibWeb/SVG/SVGViewElement.cpp +++ b/Libraries/LibWeb/SVG/SVGViewElement.cpp @@ -24,13 +24,13 @@ void SVGViewElement::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGViewElement); Base::initialize(realm); - m_view_box_for_bindings = realm.create(realm); + SVGFitToViewBox::initialize(realm); } void SVGViewElement::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); - visitor.visit(m_view_box_for_bindings); + SVGFitToViewBox::visit_edges(visitor); } bool SVGViewElement::is_presentational_hint(FlyString const& name) const @@ -52,21 +52,7 @@ void SVGViewElement::apply_presentational_hints(GC::Ref void SVGViewElement::attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) { Base::attribute_changed(name, old_value, value, namespace_); - - if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) { - if (!value.has_value()) { - m_view_box_for_bindings->set_nulled(true); - } else { - m_view_box = try_parse_view_box(value.value_or(String {})); - m_view_box_for_bindings->set_nulled(!m_view_box.has_value()); - if (m_view_box.has_value()) { - m_view_box_for_bindings->set_base_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - m_view_box_for_bindings->set_anim_val(Gfx::DoubleRect { m_view_box->min_x, m_view_box->min_y, m_view_box->width, m_view_box->height }); - } - } - } - if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio)) - m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {})); + SVGFitToViewBox::attribute_changed(*this, name, value); } } diff --git a/Libraries/LibWeb/SVG/SVGViewElement.h b/Libraries/LibWeb/SVG/SVGViewElement.h index d1f29f50d67..5e205e05c28 100644 --- a/Libraries/LibWeb/SVG/SVGViewElement.h +++ b/Libraries/LibWeb/SVG/SVGViewElement.h @@ -6,14 +6,13 @@ #pragma once +#include #include -#include -#include namespace Web::SVG { class SVGViewElement final : public SVGGraphicsElement - , public SVGViewport { + , public SVGFitToViewBox { WEB_PLATFORM_OBJECT(SVGViewElement, SVGGraphicsElement); GC_DECLARE_ALLOCATOR(SVGViewElement); @@ -21,11 +20,6 @@ public: virtual bool is_presentational_hint(FlyString const&) const override; virtual void apply_presentational_hints(GC::Ref) const override; - virtual Optional view_box() const override { return m_view_box; } - virtual Optional preserve_aspect_ratio() const override { return m_preserve_aspect_ratio; } - - GC::Ref view_box_for_bindings() { return *m_view_box_for_bindings; } - private: SVGViewElement(DOM::Document&, DOM::QualifiedName); @@ -35,10 +29,6 @@ private: virtual bool is_svg_view_element() const override { return true; } virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; - - Optional m_view_box; - Optional m_preserve_aspect_ratio; - GC::Ptr m_view_box_for_bindings; }; } diff --git a/Libraries/LibWeb/SVG/SVGViewport.h b/Libraries/LibWeb/SVG/SVGViewport.h deleted file mode 100644 index c41f1a09f07..00000000000 --- a/Libraries/LibWeb/SVG/SVGViewport.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2024, MacDue - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Web::SVG { - -class SVGViewport { -public: - virtual Optional view_box() const = 0; - virtual Optional preserve_aspect_ratio() const = 0; - virtual ~SVGViewport() = default; -}; - -inline CSSPixels normalized_diagonal_length(CSSPixelSize viewport_size) -{ - if (viewport_size.width() == viewport_size.height()) - return viewport_size.width(); - return sqrt((viewport_size.width() * viewport_size.width()) + (viewport_size.height() * viewport_size.height())) / CSSPixels::nearest_value_for(AK::Sqrt2); -} - -}