We were previously crashing when invoking 'scroll to the fragment' on
such documents as it was unable to find the active document. This is
as a result of our AD-HOC implementation not setting up the document
fully to mark it is active before running the parser.
Fixes a crash on https://tweakers.net.
Tests with different combinations of missing width, height
and viewBox.
All tests confirmed to work on Ladybird:
- exactly the same as Chromium (131.0.6778.85)
- almost the same as Firefox (129.0.2)
- only difference: standalone-w.svg: same size, different alignment
This condition was included to implement flex containers with auto
height, but it actually can reset the definitive height to 0 for inline
blocks with only replaced elements such as an SVG. Removing the
condition does not break any in-tree test, so let's improve the
situation on the SVG side of things for now.
InlinePaintable was an ad-hoc paintable type required to support the
fragmentation of inline nodes across multiple lines. It existed because
there was no way to associate multiple paintables with a single layout
node. This resulted in a lot of duplicated code between PaintableBox and
InlinePaintable. For example, most of the CSS properties like
background, border, shadows, etc. and hit-testing are almost identical
for both of them. However, the code had to be duplicated to account for
the fact that InlinePaintable creates a box for each line. And we had
quite many places that operate on paintables with a code like:
```
if (box.is_paintable_box()) {
// do something
} else (box.is_inline_paintable()) {
// do exactly the same as for paintable box but using InlinePaintable
}
```
This change replaces the usage of `InlinePaintable` with
`PaintableWithLines` created for each line, which is now possible
because we support having multiple paintables per layout node. By doing
that, we remove lots of duplicated code and bring our implementation
closer to the spec.
This is what the spec tells us to do:
The root element’s display type is always blockified,
and its principal box always establishes an independent
formatting context.
Additionally, a display of contents computes to block
on the root element.
Spec link: https://drafts.csswg.org/css-display/#rootFixes#1562
We were incorrectly looking at the CSS computed values for width and
height to determine the natural size of <svg> root elements.
This meant that elements where the attribute and computed value were
different values would end up with incorrect natural size.
Previously, `SVGSVGBox` would have a natural aspect ratio of 0 if it
had a viewbox with zero width. This led to a division by zero, causing
a crash.
Found by Domato.
Doing multiple `for_each_in_subtree()` passes was kind of a hack. We
can resolve everything in a single pass with a little more control over
the layout process. This also fixes a few minor issues like the sizing
of nested `<g>` elements.
More work is needed here though as this is still fairly ad-hoc.
Note: This does regress `css-namespace-tag-name-selector.html`,
previously SVG text within `<a>` elements would appear. However, this
was only because `for_each_in_subtree()` would blindly look through the
InlineNodes from the unimplemented `SVGAElement`s.
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.
With this the `<circle>` element now correctly parses percentage sizes,
and resolves them relative to the viewport.
The rest of the geometry elements are still left TODO.
Previously, the check for `.html` meant that `.svg` tests were excluded.
This led to a few `.svg` with missing or bit-rotted expectations, which
have now been added/updated.
This allows positioning a child SVG relative to its parent SVG.
Note: These have been implemented as CSS properties as in SVG 2, these
are geometry properties that can be used in CSS (see
https://www.w3.org/TR/SVG/geometry.html), but there is not much browser
support for this. It is nicer to implement than the ad-hoc SVG
attribute parsing though, so I feel it may make sense to port the rest
of the attributes specified here (which should fix some issues with
viewport relative sizes).
Previously, we were handling viewBoxes/viewports in a slightly hacky
way, asking graphics elements to figure out what viewBox to use during
layout. This does not work in all cases, and can't allow for more
complex SVGs where it is possible to have nested viewports.
This commit makes the SVGFormattingContext keep track of the
viewport/boxes, and it now lays out each viewport recursively, where
each nested `<svg>` or `<symbol>` can establish a new viewport.
This fixes some previous edge cases, and starts to allow nested
viewports (there's still some issues to resolve there).
Fixes#22931
This is a part of refactoring towards making the paintable tree
independent of the layout tree. Now, instead of transferring text
fragments from the layout tree to the paintable tree during the layout
commit phase, we allocate separate PaintableFragments that contain only
the information necessary for painting. Doing this also allows us to
get rid LineBoxes, as they are used only during layout.
This patch makes a few changes to the way we calculate line-height:
- `line-height: normal` is now resolved using metrics from the used
font (specifically, round(A + D + lineGap)).
- `line-height: calc(...)` is now resolved at style compute time.
- `line-height` values are now absolutized at style compute time.
As a consequence of the above, we no longer need to walk the DOM
ancestor chain looking for line-heights during style computation.
Instead, values are inherited, resolved and absolutized locally.
This is not only much faster, but also makes our line-height metrics
match those of other engines like Gecko and Blink.
The elements this hack was being used for were grouping elements, and
can be properly sized: https://svgwg.org/svg2-draft/struct.html#Groups.
Note: Other than one test change the elements here are already covered
by layout tests.
This makes use of the new Gfx::Path::text() to handle SVG text elements,
with this text is just a regular path, and can be manipulated like any
other graphics element.
This removes the SVGTextPaintable and makes both <text> and geometry
elements use a new (shared) SVGPathPaintable. This is identical to the
old SVGGeometryPaintable. This simplifies painting as once something is
resolved to a Gfx::Path, the painting logic is the same.
Before this change, we were doing it after every layout, which meant
that already-propagated overflow could be propagated again, which led to
incorrect scrolling behavior.
Previously, all SVG <text> elements were zero-sized boxes, that were
only actually positioned and sized during painting. This led to a number
of problems, the most visible of which being that text could not be
scaled based on the viewBox.
Which this patch, <text> elements get a correctly sized layout box,
that can be hit-tested and respects the SVG viewBox.
To share code with SVGGeometryElement's the PathData (from the prior
commit) has been split into a computed path and computed transforms.
The computed path is specific to geometry elements, but the computed
transforms are shared between all SVG graphics elements.
For now, part of this is commented-out. Our current implementations of
`<mask>` and `<symbol>` rely on creating layout nodes, so they can't be
`display: none`.
This allows SVG mask elements to have layout computed, but not connected
to the main paint tree. They should only be reachable if (and painted)
if referenced by the "mask" attribute of another element.
This is controlled by the forms_unconnected_subtree() function on the
paintable, which (if it returns true) prevents the paintable from being
added as a child to what would be its parent.
I'm about to make StackingContext traverse the paintable tree instead of
actually traversing the layout tree, and it turns out we were not
creating paintables for these SVG elements.
Also switch them to Layout::Box instead of the default InlineNode to
make the trees look a bit less weird. Ultimately, we should do something
specialized for these subtrees, but for now this'll do.
This patch just adds the new root paintable and updates the tests
expectations. The next patch will move painting logic from the layout
viewport to the paint viewport.
This doesn't seem to actually have fixed any bugs, as having
FillOpacity instead of StrokeOpacity in the call to parse_css_value
doesn't seem to have actually been causing bugs. But, I still think it's
worthwhile correcting.
The reason that it wasn't causing bugs is that having FillOpacity
instead of StrokeOpacity in the call to parse_css_value means that when
parsing the value is compared to the acceptable values for that property
(for example the value can only be a percentage, or a number, etc.). In
this case both FillOpacity and StrokeOpacity seem to accept the same
values.
The SVG G container should have the same size as its children. This
fixes a bug when there was an opacity value on the G element, as in
StackingContext it would try and get a bitmap of the element which would
be empty due to it having no size.
Using fixed-point saturated arithmetics for CSSPixels allows to avoid
accumulating floating-point errors.
This implementation is not complete yet: currently saturated
arithmetics implemented only for addition. But it is enough to not
regress any of layout tests we have :)
See https://github.com/SerenityOS/serenity/issues/18566
Return error when input svg is not valid and SVGSVGElement is not
present in the tree instead of doing svg_root nullptr dereference.
Fixes crash on https://apps.kde.org/en-gb/
The fix here has two parts:
1. Don't use the fallback viewBox at all if we're not in SVG-as-image.
2. Don't make a fallback viewBox with zero width and/or height.
This fixes a crash on Bandcamp pages. Thanks Tim Flynn for reporting!
When embedding an SVG in an img element, if the external SVG's root
element has both width and height attributes, but no viewBox attribute,
we now create a fallback viewBox with "0 0 width height".
This appears to match the behavior of other browsers. Inspired by
discussion on Mozilla's bug tracker:
https://bugzilla.mozilla.org/show_bug.cgi?id=614649
If we don't paint, SVG-as-image documents don't get laid out, and so
have 0x0 size throughout.
This change is also generally nice, as it makes the painting code run
on all the layout tests, increasing coverage. :^)
The spec says the result of this algorithm is undefined in such cases,
and it appears that other engines yield a zero size.
More importantly, this prevents us from leaking a non-finite value into
the layout tree.