This change adds support for rendering static SVG images using the
`resvg` crate, allowing svg sources in the `img` tag and in CSS
`background` and `content` properties. There are some limitations in
using resvg:
1. There is no support for animations or interactivity as these would
require implementing the full DOM layer of SVG specification.
2. Only system fonts can be used for text rendering. There is some
mechanism to provide a custom font resolver to usvg, but that is not
explored in this change.
3. resvg's handling of certain edge cases involving lack of explicit
`width` and `height` on the root svg element deviates from what the
specification expects from browsers. For example, resvg uses the values
in `viewBox` to derive the missing width or height dimension, but
without scaling that dimension to preserve the aspect ratio. It also
doesn't allow overriding this behavior.
Demo screenshot:

<details>
<summary>Source</summary>
```
<style>
#svg1 {
border: 1px solid red;
}
#svg2 {
border: 1px solid red;
width: 300px;
}
#svg3 {
border: 1px solid red;
width: 300px;
height: 200px;
object-fit: contain;
}
#svg4 {
border: 1px solid red;
width: 300px;
height: 200px;
object-fit: cover;
}
#svg5 {
border: 1px solid red;
width: 300px;
height: 200px;
object-fit: fill;
}
#svg6 {
border: 1px solid red;
width: 300px;
height: 200px;
object-fit: none;
}
</style>
</head>
<body>
<div>
<img id="svg1" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
</div>
<div>
<img id="svg2" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
<img id="svg3" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
<img id="svg4" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
</div>
<div>
<img id="svg5" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
<img id="svg6" src="https://raw.githubusercontent.com/servo/servo/refs/heads/main/resources/servo.svg" alt="Servo logo">
</div>
</body>
```
</details>
---------
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Testing: No tests, if `test-wpt` broke again then we would notice pretty
quickly.
Fixes: https://github.com/servo/servo/issues/37124
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Spec: https://drafts.csswg.org/css-sizing-3/#funcdef-width-fit-content
It's similar to the `fit-content` keyword but, instead of clamping the
stretch size between `min-content` and `max-content`, it clamps the
provided argument.
So now that we support `fit-content`, it's quite straightforward to add.
It's just not completely clear what should happen when the argument has
a cyclic percentage, so this may need some further adjustments depending
on the outcome of https://github.com/w3c/csswg-drafts/issues/11805
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
The static position rect is calculated assuming that the containing
block would be established by the content box of some ancestor, but the
actual containing block is established by the padding box.
So we need to add the padding of that ancestor.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Adds support for min-content, max-content, fit-content and stretch on
the min and max main size properties of a flex item.
I'm removing `automatic_min_size()` and `flex_base_size()` because they
would need to share so much code among themselves and their one caller
that it's simpler to just inline the code.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`block_size_is_zero_or_intrinsic()` was always returning true for
`stretch`. This function is used for the margin collapse heuristics
in block layout, so we were considering that an empty element with
`height: stretch` would self-collapse.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
In #35630 I treated an indefinite `stretch` as 0px on min sizing
properties, and as `none` on max sizing properties. However, this was
only for final layout sizes, I forgot about intrinsic contributions.
Blink already modified the relevant test, I'm just reordering it a bit
since we are no longer treating `stretch` as `fit-content`, so it seems
better to test it at the end.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
The CSSWG resolved that `block-size: stretch` on a block-level box
stretches the margin box to fill the parent. However, if the parent
doesn't have padding nor border, and doesn't establish an independent
formatting context, then we assume that the margins will collapse.
Therefore, we treat the margins as zero when resolving the stretch size,
regardless of whether they will actually end up collapsing.
https://github.com/w3c/csswg-drafts/issues/11044#issuecomment-2599101601https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Adds support for min-content, max-content, fit-content and stretch on
the min and max cross size properties of a flex item.
With one exception: when resolving the main sizes, transferred cross
minimums and maximums will still ignore keywords.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Two floats with `width: 50%` each should fit on the same line,
and four floats with `width: 25%` each should fit on the same line.
That's even if the containing block width, expressed in layout units,
cannot be divided by 2 or 4.
This test checks cases that are tricky for browsers where 1 pixel
are either 60, 64 or 100 layout units.
It passes on other browsers but fails on Servo because of #34665.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This changes `FlexItem::content_cross_size` into `Size<Au>` to preserve
keyword sizes. The calculation of the hypothetical cross size still
ignores them though, that will be addressed in a follow-up.
Also, browsers don't follow the spec and treat a stretch size different
than a stretch alignment: the former stretches to the containing block,
while the latter stretches to the line. This aligns Servo with that
behavior (following the spec would require bigger refactorings), so
`stretches()` is renamed to `stretches_to_line()` for clarity.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
An intrinsic flex base size depends on the contents, which may depend on
the cross size through an aspect ratio. We were only taking this into
account if the preferred cross size was numeric, but `auto` or `stretch`
can also be definite.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We were always treating an indefinite `stretch` as the automatic size.
This instead treats it as `0px` on min sizing properties, and as `none`
on max sizing properties, aligning with Blink and this recent CSSWG
resolution: https://github.com/w3c/csswg-drafts/issues/11006
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* Add tests for sizing keywords on flex items
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* layout: Partial support for sizing keywords on flex items
When a flex item has `flex-basis: auto`, the used `flex-basis` is the
value of the main size property. In that case, if the main size property
was set to keyword, we were always assuming it was `auto`. Now we handle
non-`auto` keywords correctly.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
---------
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
layout: Basic implementation of size keywords on `flex-basis`
This splits the logic to resolve the used value of `flex-basis` into its
own method, which preserves size keywords.
And then it changes `flex_base_size()` to resolve the provided keywords
properly. However, it doesn't handle size keywords in the cross axis.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`width` and `max-width` typically treat expressions with percentages as
their initial value, but for the min-content contribution of replaced
elements, they should instead be treated as zero.
https://drafts.csswg.org/css-sizing-3/#replaced-percentage-min-contribution
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Block layout uses some heuristics to guess whether margins are separated
by clearance and then don't collapse. These heuristics now take the
min-content, max-content, fit-content and stretch sizing keywords into
account.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Consider:
```html
<div style="position: relative; width: 50px; height: 50px; border: solid; margin: 5px">
<div style="position: absolute; top: 0; bottom: 0; height: max-content">
<canvas width="25" height="25" style="background: cyan; height: 100%"></canvas>
</div>
</div>
```
In order to determine the inline min/max-content sizes, we need a
tentative block size as the input, which only takes extrinsic values
into account.
In this case `height: max-content` is intrinsic, so we were treating it
as `height: initial`, which would behave as a definite `height: stretch`.
Therefore, the canvas was able to resolve its percentage.
However, it seems weird to treat an explicitly intrinsic keyword in an
extrinsic way, and Blink doesn't do it. So now we treat the tentative
block size as indefinite, therefore the percentage behaves as auto.
This adds a new test, we were previously failing 6 subtests, now only 3.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Adds support for min-content, max-content, fit-content and stretch,
for block-level elements that don't establish an independent formatting
context, and for block-level elements when there is no float.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We were sizing absolutely positioned replaced elements within their
actual containing block instead of the inset-modified containing block.
Then the `stretch` keyword would result in a wrong size.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We were using the preferred aspect ratio provided by the `aspect-ratio`
property instead of the natural aspect ratio. However, the preferred
aspect ratio should only be used to size the replaced element. To paint
the replaced contents into that element we need the natural ratio.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
To compute the min-content and max-content inline sizes of a replaced
element, we were only using the aspect ratio to transfer definite block
sizes resulting from clamping the preferred block size between the min
and max block sizes.
However, if the preferred block size is indefinite, then we weren't
transfering the min and max through the aspect ratio.
This patch adds a `SizeConstraint` enum that can represent these cases,
and a `ConstraintSpace` struct analogous to `IndefiniteContainingBlock`
but with no inline size, and a `SizeConstraint` block size.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
A `<video>` element with no source won't have a natural aspect ratio,
but `aspect-ratio: auto` should still fall back to a ratio of 300/150.
`used_size_as_if_inline_element_from_content_box_sizes()` was already
handling this, but other consumers of `preferred_aspect_ratio()` were
wrong. In particular, this resulted in a 0px wide inline-block:
```html
<div style="display: inline-block; border: solid">
<video style="height: 100px; background: cyan"></video>
</div>
```
So this patch moves the fallback into `preferred_aspect_ratio()`.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We were following CSS2, which didn't handle `aspect-ratio`.
This patch simplifies the logic and handles it correctly.
Unfortunately this makes 2 tests fail, but I'm pretty sure they aren't
spec-compliant. I'm leaving them as-is for now since they are part of
interop-2021, and Gecko, Blink and WebKit pass them (because of some
non-interoperable incorrect behaviors).
I'm adding a new test that is fully passed by Servo and WebKit.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* feat: patch for video layout sizes
added rebase from main 2024/10/05
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Signed-off-by: eri <epazos@igalia.com>
* feat: take width and height parameters if provided
Signed-off-by: eri <epazos@igalia.com>
* chore: tidy the code and update test expectations
Signed-off-by: eri <epazos@igalia.com>
* feat: handle removing poster
Signed-off-by: eri <epazos@igalia.com>
* chore: update test expectations and remove debug code
Signed-off-by: eri <epazos@igalia.com>
* fix: issues after rebasing to main
Signed-off-by: eri <epazos@igalia.com>
* feat: pass src remove test and tidy
Signed-off-by: eri <epazos@igalia.com>
* chore: clippy fixes
Signed-off-by: eri <epazos@igalia.com>
* chore: update passing test expectations
Signed-off-by: eri <epazos@igalia.com>
* fix object-position-svg test
Signed-off-by: eri <epazos@igalia.com>
* fix unintentional override of video size and resize events
Signed-off-by: eri <epazos@igalia.com>
* change how resize events are sent to better match the spec
Signed-off-by: eri <epazos@igalia.com>
* simplify poster mutation handling
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: eri <eri@inventati.org>
* improved handling of intrinsic sizes
- differentiate between natural size and css size
- presentational attributes
- fallback ratio for video element
- handle more cases where the src/poster are added/removed
- aspect ratio hints
Signed-off-by: eri <epazos@igalia.com>
* update test expectations
Signed-off-by: eri <epazos@igalia.com>
* fix cleaning current frame
Signed-off-by: eri <epazos@igalia.com>
* update test expectations
Signed-off-by: eri <epazos@igalia.com>
* Apply suggestions from code review
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: eri <eri@inventati.org>
* More code review suggestions
Signed-off-by: eri <epazos@igalia.com>
* Prevent aspect-ratio:auto from pulling the ratio from the default object size
As resolved in https://github.com/w3c/csswg-drafts/issues/7524#issuecomment-1204462924
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
---------
Signed-off-by: eri <epazos@igalia.com>
Signed-off-by: eri <eri@inventati.org>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>