1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 05:27:43 +00:00

LibWeb: Special case SVG masks during layout

Rather than try to lay out masks normally, this updates the TreeBuilder
to create layout nodes for masks as a child of their user (i.e. the
masked element). This allows each use of a mask to be laid out
differently, which makes supporting `maskContentUnits=objectBoundingBox`
fairly easy.

The `SVGFormattingContext` is then updated to lay out masks last (as
their sizing may depend on their parent), and treats them like
viewports.

This is pretty ad-hoc, but the SVG specification does not give any
guidance on how to actually implement this.
This commit is contained in:
MacDue 2024-03-11 18:26:58 +00:00 committed by Andreas Kling
parent 15e3b0ebde
commit 163b6bb401
18 changed files with 232 additions and 76 deletions

View file

@ -26,6 +26,7 @@
#include <LibWeb/Layout/ListItemBox.h>
#include <LibWeb/Layout/ListItemMarkerBox.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Layout/SVGMaskBox.h>
#include <LibWeb/Layout/TableGrid.h>
#include <LibWeb/Layout/TableWrapper.h>
#include <LibWeb/Layout/TextNode.h>
@ -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<SVG::SVGMaskElement>(dom_node)) {
layout_node = document.heap().allocate_without_realm<Layout::SVGMaskBox>(document, static_cast<SVG::SVGMaskElement&>(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 <mask> 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<SVG::SVGGraphicsElement>(dom_node)) {
auto& graphics_element = static_cast<SVG::SVGGraphicsElement&>(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<bool> layout_mask(context.layout_svg_mask, true);
push_parent(verify_cast<NodeWithStyle>(*layout_node));
create_layout_tree(const_cast<SVG::SVGMaskElement&>(*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()) {