LibWeb: Update layout when view box properties are changed

This commit is contained in:
Tim Ledbetter 2025-08-27 22:19:12 +01:00 committed by Jelle Raaijmakers
parent ea41aba6c7
commit b23fd1f440
6 changed files with 166 additions and 1 deletions

View File

@ -129,6 +129,7 @@ enum class SetNeedsLayoutReason {
X(NodeSetTextContent) \
X(None) \
X(SVGGraphicsElementTransformChange) \
X(SVGViewBoxChange) \
X(StyleChange)
enum class SetNeedsLayoutTreeUpdateReason {

View File

@ -22,7 +22,7 @@ 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<String> const& value)
void SVGFitToViewBox::attribute_changed(DOM::Element& element, FlyString const& name, Optional<String> const& value)
{
if (name.equals_ignoring_ascii_case(SVG::AttributeNames::viewBox)) {
if (!value.has_value()) {
@ -35,8 +35,10 @@ void SVGFitToViewBox::attribute_changed(DOM::Element&, FlyString const& name, Op
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 });
}
}
element.set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::SVGViewBoxChange);
} else if (name.equals_ignoring_ascii_case(SVG::AttributeNames::preserveAspectRatio)) {
m_preserve_aspect_ratio = AttributeParser::parse_preserve_aspect_ratio(value.value_or(String {}));
element.set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::SVGViewBoxChange);
}
}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<meta charset="utf-8">
<svg height="100" width="100" viewBox="40 40 20 20">
<polyline stroke-width="10%" stroke="gold" points="20,20 80,80"></polyline>
</svg>
<svg height="100" width="100" viewBox="40 40 20 20">
<polyline stroke-width="calc(5% + 5px)" stroke="gold" points="20,20 80,80"></polyline>
</svg>
<svg height="100" width="100" viewBox="40 40 20 20">
<circle stroke-width="10%" stroke="gold" cx="50" cy="50" r="10" fill="none"></circle>
</svg>
<svg height="100" width="100" viewBox="40 40 20 20">
<rect stroke-width="10%" stroke="gold" x="40" y="40" width="20" height="10" fill="none"></rect>
</svg>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>
A percentage stroke-width is updated when the viewport (viewBox) is updated.
</title>
<link rel="help" href="http://crbug.com/384605094">
<link rel="help" href="https://www.w3.org/TR/SVG/painting.html#StrokeWidth">
<link rel="help" href="https://www.w3.org/TR/SVG/coords.html#Units">
<link rel="match" href="../../../../expected/wpt-import/svg/painting/svg-percent-stroke-width-viewbox-update-ref.html">
<script src="../../common/rendering-utils.js"></script>
<svg id="svgPolyRoot" height="100" width="100" viewBox="30 30 40 40">
<polyline stroke-width="10%" stroke="gold" points="20,20 80,80"></polyline>
</svg>
<svg id="svgPolyWithCalcStrokeWidthRoot" height="100" width="100" viewBox="30 30 40 40">
<polyline stroke-width="calc(5% + 5px)" stroke="gold" points="20,20 80,80"></polyline>
</svg>
<svg id="svgCircleRoot" height="100" width="100" viewBox="30 30 40 40">
<circle stroke-width="10%" stroke="gold" cx="50" cy="50" r="10" fill="none"></circle>
</svg>
<svg id="svgRectRoot" height="100" width="100" viewBox="30 30 40 40">
<rect stroke-width="10%" stroke="gold" x="40" y="40" width="20" height="10" fill="none"></rect>
</svg>
<script>
waitForAtLeastOneFrame().then(() => {
document.getElementById("svgPolyRoot").setAttribute("viewBox", "40 40 20 20");
document.getElementById("svgPolyWithCalcStrokeWidthRoot").setAttribute("viewBox", "40 40 20 20");
document.getElementById("svgCircleRoot").setAttribute("viewBox", "40 40 20 20");
document.getElementById("svgRectRoot").setAttribute("viewBox", "40 40 20 20");
});
</script>

View File

@ -0,0 +1,20 @@
Harness status: OK
Found 15 tests
15 Pass
Pass no sizing attributes set
Pass specified width
Pass modified specified width
Pass specified width and height
Pass specified width and modified specified height
Pass specified height
Pass modified specified height
Pass no specified sizing attrs (after setting & removing them)
Pass set a 10x8 viewBox
Pass modified viewBox to 50x20
Pass adding specified width, in presence of specified viewBox
Pass modifiying specified viewBox, in presence of specified width
Pass removing specified width, in presence of specified viewBox
Pass adding specified height, in presence of specified viewBox
Pass modifiying specified viewBox, in presence of specified height

View File

@ -0,0 +1,95 @@
<!doctype HTML>
<head>
<link rel="help" href="https://www.w3.org/TR/SVG/coords.html#SizingSVGInCSS">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#wrapper {
width: 500px;
}
</style>
</head>
<body>
<div id="wrapper">
<svg id="mySVG"></svg>
</div>
<script>
let svgElem = document.getElementById("mySVG");
function expect_svg_width_and_height(width, height) {
let rect = svgElem.getBoundingClientRect();
assert_equals(rect.width, width, "checking width.");
assert_equals(rect.height, height, "checking height.");
}
test(function() {
expect_svg_width_and_height(300, 150);
}, "no sizing attributes set");
// Just setting width and/or height:
test(function() {
svgElem.setAttribute("width", "100");
expect_svg_width_and_height(100, 150);
}, "specified width");
test(function() {
svgElem.setAttribute("width", "400");
expect_svg_width_and_height(400, 150);
}, "modified specified width");
test(function() {
// (set height, leaving width still set)
svgElem.setAttribute("height", "100");
expect_svg_width_and_height(400, 100);
}, "specified width and height");
test(function() {
svgElem.setAttribute("height", "200");
expect_svg_width_and_height(400, 200);
}, "specified width and modified specified height");
test(function() {
svgElem.removeAttribute("width"); // leaving only 'height':
expect_svg_width_and_height(300, 200);
}, "specified height");
test(function() {
svgElem.setAttribute("height", "250");
expect_svg_width_and_height(300, 250);
}, "modified specified height");
test(function() {
// clean up (go back to having no sizing attrs set)
svgElem.removeAttribute("height");
expect_svg_width_and_height(300, 150);
}, "no specified sizing attrs (after setting & removing them)");
// Just setting viewBox:
test(function() {
svgElem.setAttribute("viewBox", "0 0 10 8");
expect_svg_width_and_height(500, 400);
}, "set a 10x8 viewBox");
test(function() {
// Adjusting already-set viewBox:
svgElem.setAttribute("viewBox", "0 0 50 10");
expect_svg_width_and_height(500, 100);
}, "modified viewBox to 50x20");
test(function() {
svgElem.setAttribute("width", "100");
expect_svg_width_and_height(100, 20);
}, "adding specified width, in presence of specified viewBox");
test(function() {
svgElem.setAttribute("viewBox", "0 0 40 30");
expect_svg_width_and_height(100, 75);
}, "modifiying specified viewBox, in presence of specified width");
test(function() {
svgElem.removeAttribute("width");
expect_svg_width_and_height(500, 375);
}, "removing specified width, in presence of specified viewBox");
test(function() {
svgElem.setAttribute("height", "60");
expect_svg_width_and_height(80, 60);
}, "adding specified height, in presence of specified viewBox");
test(function() {
svgElem.setAttribute("viewBox", "0 0 100 120");
expect_svg_width_and_height(50, 60);
}, "modifiying specified viewBox, in presence of specified height");
</script>
</body>