The parent's viewBox transform includes both scaling and a centering
offset. For nested <svg> elements, this transform was applied once to
position the nested SVG's box, then again to position children inside
it, double-counting the centering offset.
We now apply the parent's transform to the nested SVG box upfront to
ensure the transform is only applied once.
Move the inline dom_node() method to Viewport.cpp so the header no
longer needs the full Document definition. Add explicit includes to
files that relied on the transitive dependency.
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.
When userSpaceOnUse clips are nested inside objectBoundingBox masks,
the viewport_size was compounding incorrectly because it was calculated
after content scaling by m_parent_viewbox_transform. For userSpaceOnUse
clips (which have no viewBox), the fallback to content_width() returned
the already-scaled value, causing sizes to explode with each nesting
level.
Fix by calculating viewport dimensions before the scaling block. This
ensures m_viewport_size represents the coordinate system dimensions,
not the final pixel dimensions.
Add optional tracing that prints a tree visualization of formatting
context `run()` invocations. This is useful for debugging layout issues
where you need to understand the nesting and order of layout passes,
or why a box receives unexpected available space.
Example output:
```
├─ BFC <Viewport<#document>> run(definite(800) x definite(600))
│ ├─ BFC <BlockContainer<HTML>> run(definite(800) x indefinite)
│ │ ├─ IFC <BlockContainer(anonymous)> run(definite(800) x indefinite)
│ │ ├─ GFC <Box<DIV.grid>> run(definite(800) x indefinite)
│ │ │ ├─ BFC <BlockContainer<DIV.item>> run(definite(400) x indefinite)
```
Previously, we only supported very basic numbers and a single level of
text positioning support in the `x`, `y`, `dx` and `dy` attributes in
`<text>` and `<tspan>` SVG elements.
This improves our support for them in the following ways:
* Any `length-percentage` or `number` type value is accepted;
* Nested `<text>` and `<tspan>` use the 'current text position'
concept to determine where the next text run should go;
* We expose the attributes' values through the API.
Though we still do not support:
* Applying the `rotate` attribute;
* Applying transformations on a per-character basis.
* Proper horizontal and vertical glyph advancing (we just use the path
bounding box for now).
We don't need to construct a new AffineTransform and multiply that by
yet another AffineTransform; we can simply translate and scale the
transform that's already in place. No functional changes.
When displaying SVGs inside a navigable directly, we want to display
them in the size specified by their width and height attributes instead
of stretching them to the viewport as layout normally would.
However, when doing so, we need to actually check that the SVG we are
laying out is indeed directly inside the navigable. Otherwise we just
blindly overwrite whatever content sizes have been calculated by layout
code for e.g. SVG elements inlined somewhere in an HTML document.
Due to the way this was written originally, the bug was not very
noticable. The code overwrote the content width/height with the computed
width/height, which was often still correct in the sense that those two
had the same value. However, content size may also be the result of
{min,max}-{width,height} constraints, which can make it differ from the
computed values.
This bug was likely a regression introduced in
f5e01192cc.
Special handling for SVGClipPathElement and SVGMaskElement, which use a
a ViewBox and PreserveAspectRatio value internally, has been moved to
`SVGFormattingContext`.
This ensures that percentages resolve against the foreignObject's size
instead of the size of its containing block.
This makes user profile pictures clip correctly in the "Friends" view
of the Discord app.
This is according to the default user-agent style from the SVG2 spec.
In order for this to work correctly, we also have to assign width and
height to foreignObject boxes during SVG layout, since they are handled
manually by SVGFormattingContext.