diff --git a/Tests/LibWeb/Layout/expected/svg/objectBoundingBox-mask.txt b/Tests/LibWeb/Layout/expected/svg/objectBoundingBox-mask.txt index 2104cc1d2c..8a1eaea171 100644 --- a/Tests/LibWeb/Layout/expected/svg/objectBoundingBox-mask.txt +++ b/Tests/LibWeb/Layout/expected/svg/objectBoundingBox-mask.txt @@ -1,22 +1,31 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (0,0) content-size 800x216 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x200 children: inline - frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 200x200] baseline: 200 - SVGSVGBox at (8,8) content-size 200x200 [SVG] children: inline + BlockContainer at (0,0) content-size 800x416 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x400 children: inline + frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 400x400] baseline: 400 + SVGSVGBox at (8,8) content-size 400x400 [SVG] children: inline TextNode <#text> - SVGGraphicsBox at (8,8) content-size 1x1 children: inline - TextNode <#text> - SVGGeometryBox at (8,8) content-size 1x1 children: not-inline - TextNode <#text> - SVGGeometryBox at (8.09375,8.09375) content-size 0.8125x0.8125 children: not-inline - TextNode <#text> TextNode <#text> SVGGeometryBox at (8,8) content-size 200x200 children: not-inline + SVGMaskBox at (8,8) content-size 200x200 children: inline + TextNode <#text> + SVGGeometryBox at (8,8) content-size 200x200 children: not-inline + TextNode <#text> + SVGGeometryBox at (26.75,26.75) content-size 162.5x162.5 children: not-inline + TextNode <#text> + TextNode <#text> + SVGGeometryBox at (208,208) content-size 200x100 children: not-inline + SVGMaskBox at (208,208) content-size 200x100 children: inline + TextNode <#text> + SVGGeometryBox at (208,208) content-size 200x100 children: not-inline + TextNode <#text> + SVGGeometryBox at (226.75,217.375) content-size 162.5x81.25 children: not-inline + TextNode <#text> TextNode <#text> TextNode <#text> ViewportPaintable (Viewport<#document>) [0,0 800x600] - PaintableWithLines (BlockContainer) [0,0 800x216] - PaintableWithLines (BlockContainer) [8,8 784x200] - SVGSVGPaintable (SVGSVGBox) [8,8 200x200] + PaintableWithLines (BlockContainer) [0,0 800x416] + PaintableWithLines (BlockContainer) [8,8 784x400] + SVGSVGPaintable (SVGSVGBox) [8,8 400x400] SVGPathPaintable (SVGGeometryBox) [8,8 200x200] + SVGPathPaintable (SVGGeometryBox) [208,208 200x100] diff --git a/Tests/LibWeb/Layout/expected/svg/svg-symbol-with-viewbox.txt b/Tests/LibWeb/Layout/expected/svg/svg-symbol-with-viewbox.txt index b97eda9569..2f0eecf533 100644 --- a/Tests/LibWeb/Layout/expected/svg/svg-symbol-with-viewbox.txt +++ b/Tests/LibWeb/Layout/expected/svg/svg-symbol-with-viewbox.txt @@ -9,7 +9,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline TextNode <#text> SVGSVGBox at (8,8) content-size 300x150 [SVG] children: inline TextNode <#text> - SVGGraphicsBox at (92.375,26.75) content-size 131.25x112.15625 children: not-inline + SVGGraphicsBox at (8,8) content-size 300x150 children: not-inline SVGGraphicsBox at (92.375,26.75) content-size 131.25x112.15625 [BFC] children: inline TextNode <#text> SVGGeometryBox at (92.375,26.75) content-size 131.25x112.15625 children: inline @@ -24,6 +24,6 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer(anonymous)) [8,8 784x0] PaintableWithLines (BlockContainer
) [8,8 784x150] SVGSVGPaintable (SVGSVGBox) [8,8 300x150] - SVGGraphicsPaintable (SVGGraphicsBox) [92.375,26.75 131.25x112.15625] + SVGGraphicsPaintable (SVGGraphicsBox) [8,8 300x150] SVGGraphicsPaintable (SVGGraphicsBox#braces) [92.375,26.75 131.25x112.15625] SVGPathPaintable (SVGGeometryBox) [92.375,26.75 131.25x112.15625] diff --git a/Tests/LibWeb/Layout/input/svg/objectBoundingBox-mask.html b/Tests/LibWeb/Layout/input/svg/objectBoundingBox-mask.html index c85f681ca7..2216c7baa7 100644 --- a/Tests/LibWeb/Layout/input/svg/objectBoundingBox-mask.html +++ b/Tests/LibWeb/Layout/input/svg/objectBoundingBox-mask.html @@ -1,8 +1,9 @@ - + + diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index e42113cc83..f03058f923 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -469,6 +469,7 @@ set(SOURCES Layout/SVGGeometryBox.cpp Layout/SVGGraphicsBox.cpp Layout/SVGSVGBox.cpp + Layout/SVGMaskBox.cpp Layout/SVGTextBox.cpp Layout/SVGTextPathBox.cpp Layout/TableFormattingContext.cpp @@ -523,6 +524,7 @@ set(SOURCES Painting/RecordingPainter.cpp Painting/SVGPathPaintable.cpp Painting/SVGGraphicsPaintable.cpp + Painting/SVGMaskPaintable.cpp Painting/SVGPaintable.cpp Painting/SVGSVGPaintable.cpp Painting/ShadowPainting.cpp diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index 76dafe8dc7..29921a9951 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -289,6 +290,8 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available }(); for_each_in_subtree(box, [&](Node const& descendant) { + if (is(descendant)) + return TraversalDecision::SkipChildrenAndContinue; if (is(descendant.dom_node())) { // Layout for a nested SVG viewport. // https://svgwg.org/svg2-draft/coords.html#EstablishingANewSVGViewport. @@ -391,14 +394,19 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available // https://svgwg.org/svg2-draft/struct.html#Groups // 5.2. Grouping: the ā€˜g’ element // The ā€˜g’ element is a container element for grouping together related graphics elements. - box.for_each_in_subtree_of_type([&](Box const& descendant) { + box.for_each_in_subtree_of_type([&](SVGBox const& descendant) { if (is_container_element(descendant)) { Gfx::BoundingBox bounding_box; - descendant.for_each_in_subtree_of_type([&](Box const& child_of_svg_container) { - auto& box_state = m_state.get(child_of_svg_container); + for_each_in_subtree(descendant, [&](Node const& child_of_svg_container) { + if (!is(child_of_svg_container)) + return TraversalDecision::Continue; + // Masks do not change the bounding box of their parents. + if (is(child_of_svg_container)) + return TraversalDecision::SkipChildrenAndContinue; + auto& box_state = m_state.get(static_cast(child_of_svg_container)); bounding_box.add_point(box_state.offset); bounding_box.add_point(box_state.offset.translated(box_state.content_width(), box_state.content_height())); - return IterationDecision::Continue; + return TraversalDecision::Continue; }); auto& box_state = m_state.get_mutable(descendant); @@ -411,6 +419,28 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available } return IterationDecision::Continue; }); + + // Lay out masks last (as their parent needs to be sized first). + box.for_each_in_subtree_of_type([&](SVGMaskBox const& mask_box) { + auto& mask_state = m_state.get_mutable(static_cast(mask_box)); + auto parent_viewbox_transform = viewbox_transform; + if (mask_box.dom_node().mask_content_units() == SVG::MaskContentUnits::ObjectBoundingBox) { + auto* masked_node = mask_box.parent(); + auto& masked_node_state = m_state.get(*masked_node); + mask_state.set_content_width(masked_node_state.content_width()); + mask_state.set_content_height(masked_node_state.content_height()); + parent_viewbox_transform = Gfx::AffineTransform {}.translate(masked_node_state.offset.to_type()); + } else { + mask_state.set_content_width(viewport_width); + mask_state.set_content_height(viewport_height); + } + // Pretend masks are a viewport so we can scale the contents depending on the `maskContentUnits`. + SVGFormattingContext nested_context(m_state, static_cast(mask_box), this, parent_viewbox_transform); + mask_state.set_has_definite_width(true); + mask_state.set_has_definite_height(true); + nested_context.run(static_cast(mask_box), layout_mode, available_space); + return IterationDecision::Continue; + }); } } diff --git a/Userland/Libraries/LibWeb/Layout/SVGMaskBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.cpp new file mode 100644 index 0000000000..c76c1c57d7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::Layout { + +SVGMaskBox::SVGMaskBox(DOM::Document& document, SVG::SVGMaskElement& element, NonnullRefPtr properties) + : SVGGraphicsBox(document, element, properties) +{ +} + +JS::GCPtr SVGMaskBox::create_paintable() const +{ + return Painting::SVGMaskPaintable::create(*this); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h new file mode 100644 index 0000000000..f73fccd1cf --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Layout { + +class SVGMaskBox : public SVGGraphicsBox { + JS_CELL(SVGMaskBox, SVGBox); + +public: + SVGMaskBox(DOM::Document&, SVG::SVGMaskElement&, NonnullRefPtr); + virtual ~SVGMaskBox() override = default; + + SVG::SVGMaskElement& dom_node() { return verify_cast(SVGGraphicsBox::dom_node()); } + SVG::SVGMaskElement const& dom_node() const { return verify_cast(SVGGraphicsBox::dom_node()); } + + virtual JS::GCPtr create_paintable() const override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index f164a6e9ff..4bf10329fd 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -335,6 +336,12 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& display = CSS::Display(CSS::DisplayOutside::Inline, CSS::DisplayInside::Flow); } + if (context.layout_svg_mask && is(dom_node)) { + layout_node = document.heap().allocate_without_realm(document, static_cast(dom_node), *style); + // We're here if our parent is a use of an SVG mask, but we don't want to lay out any elements that could be a child of this mask. + context.layout_svg_mask = false; + } + if (!layout_node) return; @@ -389,6 +396,19 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& pop_parent(); } + if (is(dom_node)) { + auto& graphics_element = static_cast(dom_node); + // Create the layout tree for the SVG mask as a child of the masked element. Note: This will create + // a new subtree for each use of the mask (so there's not a 1-to-1 mapping from DOM node to mask + // layout node). Each use of a mask may be laid out differently so this duplication is necessary. + if (auto mask = graphics_element.mask()) { + TemporaryChange layout_mask(context.layout_svg_mask, true); + push_parent(verify_cast(*layout_node)); + create_layout_tree(const_cast(*mask), context); + pop_parent(); + } + } + // https://html.spec.whatwg.org/multipage/rendering.html#button-layout // If the computed value of 'inline-size' is 'auto', then the used value is the fit-content inline size. if (dom_node.is_html_button_element() && dom_node.layout_node()->computed_values().width().is_auto()) { diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h index df156c0caf..fe2841fcfa 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h @@ -23,6 +23,7 @@ public: private: struct Context { bool has_svg_root = false; + bool layout_svg_mask = false; }; i32 calculate_list_item_index(DOM::Node&); diff --git a/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.cpp index 6e1598dc6d..e04874b24b 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -23,12 +24,6 @@ SVGGraphicsPaintable::SVGGraphicsPaintable(Layout::SVGGraphicsBox const& layout_ { } -bool SVGGraphicsPaintable::forms_unconnected_subtree() const -{ - // Masks should not be painted (i.e. reachable) unless referenced by another element. - return is(dom_node()); -} - Layout::SVGGraphicsBox const& SVGGraphicsPaintable::layout_box() const { return static_cast(layout_node()); @@ -37,20 +32,10 @@ Layout::SVGGraphicsBox const& SVGGraphicsPaintable::layout_box() const Optional SVGGraphicsPaintable::get_masking_area() const { auto const& graphics_element = verify_cast(*dom_node()); - auto mask = graphics_element.mask(); - if (!mask) + auto* mask_box = graphics_element.layout_node()->first_child_of_type(); + if (!mask_box) return {}; - auto target_area = [&] { - auto mask_units = mask->mask_units(); - if (mask_units == SVG::MaskUnits::UserSpaceOnUse) { - auto const* svg_element = graphics_element.shadow_including_first_ancestor_of_type(); - return svg_element->paintable_box()->absolute_border_box_rect(); - } else { - VERIFY(mask_units == SVG::MaskUnits::ObjectBoundingBox); - return absolute_border_box_rect(); - } - }(); - return mask->resolve_masking_area(target_area); + return mask_box->dom_node().resolve_masking_area(mask_box->paintable_box()->absolute_border_box_rect()); } static Gfx::Bitmap::MaskKind mask_type_to_gfx_mask_kind(CSS::MaskType mask_type) @@ -77,30 +62,24 @@ Optional SVGGraphicsPaintable::get_mask_type() const RefPtr SVGGraphicsPaintable::calculate_mask(PaintContext& context, CSSPixelRect const& masking_area) const { auto const& graphics_element = verify_cast(*dom_node()); - auto mask = graphics_element.mask(); - VERIFY(mask); - if (mask->mask_content_units() != SVG::MaskContentUnits::UserSpaceOnUse) { - // FIXME: Implement support for maskContentUnits=objectBoundingBox - return {}; - } + auto* mask_box = graphics_element.layout_node()->first_child_of_type(); + VERIFY(mask_box); auto mask_rect = context.enclosing_device_rect(masking_area); RefPtr mask_bitmap = {}; - if (mask && mask->layout_node() && is(mask->layout_node()->paintable())) { - auto& mask_paintable = static_cast(*mask->layout_node()->paintable()); - auto mask_bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, mask_rect.size().to_type()); - if (mask_bitmap_or_error.is_error()) - return {}; - mask_bitmap = mask_bitmap_or_error.release_value(); - { - CommandList painting_commands; - RecordingPainter recording_painter(painting_commands); - recording_painter.translate(-mask_rect.location().to_type()); - auto paint_context = context.clone(recording_painter); - paint_context.set_svg_transform(graphics_element.get_transform()); - StackingContext::paint_node_as_stacking_context(mask_paintable, paint_context); - CommandExecutorCPU executor { *mask_bitmap }; - painting_commands.execute(executor); - } + auto& mask_paintable = static_cast(*mask_box->paintable()); + auto mask_bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, mask_rect.size().to_type()); + if (mask_bitmap_or_error.is_error()) + return {}; + mask_bitmap = mask_bitmap_or_error.release_value(); + { + CommandList painting_commands; + RecordingPainter recording_painter(painting_commands); + recording_painter.translate(-mask_rect.location().to_type()); + auto paint_context = context.clone(recording_painter); + paint_context.set_svg_transform(graphics_element.get_transform()); + StackingContext::paint_node_as_stacking_context(mask_paintable, paint_context); + CommandExecutorCPU executor { *mask_bitmap }; + painting_commands.execute(executor); } return mask_bitmap; } diff --git a/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.h b/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.h index d7c4e4e2b3..e4ea76db5b 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/SVGGraphicsPaintable.h @@ -49,8 +49,6 @@ public: Layout::SVGGraphicsBox const& layout_box() const; - virtual bool forms_unconnected_subtree() const override; - virtual Optional get_masking_area() const override; virtual Optional get_mask_type() const override; virtual RefPtr calculate_mask(PaintContext&, CSSPixelRect const& masking_area) const override; diff --git a/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.cpp new file mode 100644 index 0000000000..534944f165 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::Painting { + +JS::NonnullGCPtr SVGMaskPaintable::create(Layout::SVGMaskBox const& layout_box) +{ + return layout_box.heap().allocate_without_realm(layout_box); +} + +SVGMaskPaintable::SVGMaskPaintable(Layout::SVGMaskBox const& layout_box) + : SVGGraphicsPaintable(layout_box) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.h b/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.h new file mode 100644 index 0000000000..b57ab8eaee --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/SVGMaskPaintable.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::Painting { + +class SVGMaskPaintable : public SVGGraphicsPaintable { + JS_CELL(SVGMaskPaintable, SVGGraphicsPaintable); + +public: + static JS::NonnullGCPtr create(Layout::SVGMaskBox const&); + + bool forms_unconnected_subtree() const override + { + // Masks should not be painted (i.e. reachable) unless referenced by another element. + return true; + } + +protected: + SVGMaskPaintable(Layout::SVGMaskBox const&); +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGDefsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGDefsElement.cpp index 989728826e..62457ef9b9 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGDefsElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGDefsElement.cpp @@ -27,10 +27,4 @@ void SVGDefsElement::initialize(JS::Realm& realm) set_prototype(&Bindings::ensure_web_prototype(realm, "SVGDefsElement"_fly_string)); } -JS::GCPtr SVGDefsElement::create_layout_node(NonnullRefPtr style) -{ - // FIXME: We need this layout node so any s inside this element get layout computed. - return heap().allocate_without_realm(document(), *this, move(style)); -} - } diff --git a/Userland/Libraries/LibWeb/SVG/SVGDefsElement.h b/Userland/Libraries/LibWeb/SVG/SVGDefsElement.h index 6d5e63169f..ed4297b3a2 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGDefsElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGDefsElement.h @@ -17,7 +17,10 @@ class SVGDefsElement final : public SVGGraphicsElement { public: virtual ~SVGDefsElement(); - virtual JS::GCPtr create_layout_node(NonnullRefPtr) override; + virtual JS::GCPtr create_layout_node(NonnullRefPtr) override + { + return nullptr; + } private: SVGDefsElement(DOM::Document&, DOM::QualifiedName); diff --git a/Userland/Libraries/LibWeb/SVG/SVGMaskElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGMaskElement.cpp index f99f443c6e..cb90068075 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGMaskElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGMaskElement.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -28,9 +27,10 @@ void SVGMaskElement::initialize(JS::Realm& realm) set_prototype(&Bindings::ensure_web_prototype(realm, "SVGMaskElement"_fly_string)); } -JS::GCPtr SVGMaskElement::create_layout_node(NonnullRefPtr style) +JS::GCPtr SVGMaskElement::create_layout_node(NonnullRefPtr) { - return heap().allocate_without_realm(document(), *this, move(style)); + // Masks are handled as a special case in the TreeBuilder. + return nullptr; } void SVGMaskElement::attribute_changed(FlyString const& name, Optional const& value) diff --git a/Userland/Libraries/LibWeb/SVG/SVGMaskElement.h b/Userland/Libraries/LibWeb/SVG/SVGMaskElement.h index 367258039a..40a11eaa35 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGMaskElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGMaskElement.h @@ -8,16 +8,34 @@ #include #include +#include namespace Web::SVG { -class SVGMaskElement final : public SVGGraphicsElement { +class SVGMaskElement final : public SVGGraphicsElement + , public SVGViewport { + WEB_PLATFORM_OBJECT(SVGMaskElement, SVGGraphicsElement); JS_DECLARE_ALLOCATOR(SVGMaskElement); public: virtual ~SVGMaskElement() override; + virtual Optional 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". + if (mask_content_units() == MaskContentUnits::ObjectBoundingBox) + return ViewBox { 0, 0, 1, 1 }; + 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& value) override; virtual JS::GCPtr create_layout_node(NonnullRefPtr) override; diff --git a/Userland/Libraries/LibWeb/SVG/SVGStopElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGStopElement.cpp index 793183bfbf..4a8147af9d 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGStopElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGStopElement.cpp @@ -6,8 +6,7 @@ #include #include -#include -#include +#include #include #include #include