ladybird/Libraries/LibWeb/Layout/SVGFormattingContext.h
Aliaksandr Kalenik e190f3ec23 LibWeb: Compute SVG mask/clip transforms using layout tree hierarchy
No observable behavior change — this is a refactoring of how SVG
transforms are computed for mask and clip content.

Previously, SVGGraphicsElement::get_transform() computed accumulated
transforms by walking the DOM tree. This didn't work correctly for masks
and clips because their DOM structure differs from layout: in the DOM,
mask/clip elements are siblings or ancestors of their targets, but in
the layout tree they become children of the target element. Walking the
DOM tree caused transforms from the target's ancestors to incorrectly
leak into mask/clip content.

The fix walks the layout tree instead of the DOM tree, which means
transform computation must happen during layout (where we have access to
the layout tree structure) rather than on-demand from the DOM element.
This moves the logic to SVGFormattingContext and removes get_transform()
since it can no longer serve its purpose — the DOM element only provides
element_transform() for its own transform now.

During layout, we walk up the layout tree and stop at mask/clip
boundaries, ensuring mask/clip content stays in its own coordinate
space. The target's accumulated transform is applied separately at paint
time.
2026-02-05 09:00:56 +01:00

50 lines
1.7 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Forward.h>
#include <LibWeb/Layout/FormattingContext.h>
#include <LibWeb/Layout/SVGImageBox.h>
#include <LibWeb/Layout/SVGSVGBox.h>
#include <LibWeb/Layout/SVGTextBox.h>
#include <LibWeb/Layout/SVGTextPathBox.h>
namespace Web::Layout {
class SVGFormattingContext final : public FormattingContext {
public:
explicit SVGFormattingContext(LayoutState&, LayoutMode, Box const&, FormattingContext* parent, Gfx::AffineTransform parent_viewbox_transform = {});
~SVGFormattingContext();
virtual void run(AvailableSpace const&) override;
virtual CSSPixels automatic_content_width() const override;
virtual CSSPixels automatic_content_height() const override;
private:
void layout_svg_element(Box const&);
void layout_nested_viewport(Box const&);
void layout_container_element(SVGBox const&);
void layout_graphics_element(SVGGraphicsBox const&);
void layout_path_like_element(SVGGraphicsBox const&);
void layout_mask_or_clip(SVGBox const&);
void layout_image_element(SVGImageBox const& image_box);
[[nodiscard]] Gfx::Path compute_path_for_text(SVGTextBox const&) const;
[[nodiscard]] Gfx::Path compute_path_for_text_path(SVGTextPathBox const&) const;
[[nodiscard]] Gfx::AffineTransform get_parent_svg_transform(SVGGraphicsBox const&) const;
Gfx::AffineTransform m_parent_viewbox_transform {};
Optional<AvailableSpace> m_available_space {};
Gfx::AffineTransform m_current_viewbox_transform {};
CSSPixelSize m_viewport_size {};
CSSPixelPoint m_svg_offset {};
Gfx::FloatPoint m_current_text_position {};
};
}