This fixes a plethora of rounding problems on many websites.
In the future, we may want to replace this with fixed-point arithmetic
(bug #18566) for performance (and consistency with other engines),
but in the meantime this makes the web look a bit better. :^)
There's a lot more things that could be converted to doubles, which
would reduce the amount of casting necessary in this patch.
We can do that incrementally, however.
If there are min or max size constraints in the cross axis for a flex
item that has a desired aspect ratio, we may need to adjust the main
size *after* applying the cross size constraints.
All the steps to achieving this aren't mentioned in the spec, but it
seems that all other browsers behave this way, so we should too.
These functions no longer do anything interesting and just forward to
content_width/content_height. Let's make the callers use those directly
instead and remove this indirection layer.
This isn't actually part of CSS-FLEXBOX-1, but all major engines honor
these properties in flex layout, and it's widely used on the web.
There's a bug open against the flexbox spec where fantasai says the
algorithm will be updated in CSS-FLEXBOX-2:
https://github.com/w3c/csswg-drafts/issues/2336
I've added comments to all the places where we adjust calculations for
gaps with "CSS-FLEXBOX-2" so we can find them easily. When that spec
becomes available, we can add proper spec links.
In situations where we need a width to calculate the intrinsic height of
a flex item, we use the fit-content width as a stand-in. However, we
also need to clamp it to any min-width and max-width properties present.
In `flex-direction: column` layouts, a flex item's intrinsic height may
depend on its width, but the width is calculated *after* the intrinsic
height is required.
Unfortunately, the specification doesn't tell us exactly what to do here
(missing inputs to intrinsic sizing is a common problem) so we take the
solution that flexbox applies in 9.2.3.C and apply it to all intrinsic
height calculations within FlexFormattingContext: if the used width of
an item is not yet known when its intrinsic height is requested, we
substitute the fit-content width instead.
Note that while this is technically ad-hoc, it's basically extrapolating
the spec's suggestion in one specific case and using it in all cases.
Instead of special-casing FlexFormattingContext in the intrinsic sizing
layout helpers, add FormattingContext::automatic_content_width() and let
each context subclass decide what that means.
No behavior change here, just moving this responsibility.
The draft CSS-FLEXBOX-1 spec had a more detailed description of this
algorithm, so let's use that as our basis for the implementation.
Test by Aliaksandr. :^)
"Specified" means something else in CSS, so let's not use this
overloaded word here. These helpers return the inner main/cross size of
a given box, so let's say "inner" instead.
When a flex item has a specific preferred size, that size should be its
contribution to the containers intrinsic sizes.
This fixes an issue on Patreon where the logo would cover the entire
viewport since the SVG had a large intrinsic size but the flex item
containing it had a small specified size in CSS.
This property tells us how to lay out multi-line flex containers.
I implemented all modes except `space-between` and `space-around`.
Those are left as FIXMEs.
For percentage cross min/max sizes that resolve against indefinite
available space, we now essentially ignore them instead of resolving
them to 0 at the start of layout.
When we have nested flexbox layouts within one another, and the child
context wants to call up to the parent context and ask for help with
dimensioning the child flex container, we now simply do nothing.
As far as I can tell, this works out just fine, since the child flex
container will already be dimensioned by the flex layout algorithm.
After speaking with fantasai at CSSWG about this, it turns out I had
misunderstood intrinsic heights. I originally believed all intrinsic
sizes had to be computed with no influence from the surrounding context.
As it turns out, intrinsic heights *are* influenced by the available
width, since it's needed to determine where lines break.
The APIs for calculating min-content and max-content heights now take
the available width as inputs. This instantly improves layout in many
cases where we'd previously make things way too wide.
This is a big and messy change, and here's the gist:
- AvaliableSpace is now 2x AvailableSize (width and height)
- Layout algorithms are redesigned around the idea of available space
- When doing layout across nested formatting contexts, the parent
context tells the child context how much space is available for the
child's root box in both axes.
- "Available space" replaces "containing block width" in most places.
- The width and height in a box's UsedValues are considered to be
definite after they're assigned to. Marking something as having
definite size is no longer a separate step,
This probably introduces various regressions, but the big win here is
that our layout system now works with available space, just like the
specs are written. Fixing issues will be much easier going forward,
since you don't need to do nearly as much conversion from "spec logic"
to "LibWeb logic" as you previously did.
Instead of formatting contexts flailing around to figure out from the
"inside" how much space is available on the "outside", we should
provide the amount of available space in both axes as an input to run().
This basically means that when something creates a nested formatting
context, the parent context is responsible for telling the nested context
how much space is available for layout. This information is provided
immediately when invoking run().
Note that this commit doesn't pass accurate values in all cases yet.
This first step just makes it build, and passes available values in some
cases where getting them was trivial.
This patch changes the *computed* representation of the following CSS
properties to use CSS::Size:
- width, min-width, max-width
- height, min-height, max-height
A few things had to change in order for things to keep working,
but I tried to keep the diff to a minimum.
The main trouble was that `min-width` and `max-width` can't actually be
`auto`, but they *can* be `none`. We previously treated `auto` as a
valid value (and it behaved mostly like `none`).
This function should return the automatic height of the formatting
context's root box.
Until now, we've been relying on some magical handshakes between parent
and child context, when negotiating the height of child context root
boxes. This is a step towards something more reasonable.
This is rather subtle and points to our architecture around definite
sizes not being exactly right, but...
At some points during flexbox layout, the spec tells us that the sizes
of certain flex items are considered definite from this point on.
We implement this by marking each item's associated UsedValues as
"has-definite-width/height".
However, this breaks code that tries to resolve computed "auto" sizes
by taking the corresponding size from the containing block. The end
result was that the 1st sizing pass in flexbox would find the right size
for an "auto" sized item, but the 2nd pass would override the correct
size with the containing block's content size in that axis instead.
To work around the issue, FFC now remembers when it "definitizes" an
item, and future attempts to resolve an "auto" computed size for that
value will bypass the computed-auto-is-resolved-against-containing-block
step of the algorithm. It's not perfect, and we'll need to think more
about how to really represent these intermediate states relating to
box sizes being definite..
Percentage sizes of flex items are relative to the flex container, but
even when the flex container is automatically sized, we still have to
support them.
To make this work, we first do a pass where percentage sizes are ignored
(treated as "auto", basically) in order to get a "reference" value.
Then we do a second pass where percentages can be resolved against this
reference value.
The CSS-FLEXBOX-1 spec gives us two situations in which flex item cross
sizes should be considered definite. Both of them happen *during* flex
layout, which is super finicky but it is what it is.
Previously, we considered all LengthPercentage values for used flex
basis to be definite. This is not accurate, as percentages should only
be considered definite if the reference value they resolve against is
a definite size.
Fix this by checking the flex container's main definite size flag.
If we wait until after the parent context has laid out the flex
container, abspos children are able to use the final results of the
parent sizing the flex container.
This makes `height:auto` work on abspos children of a flex container.
Previously, we had three layout modes:
- Normal:
- Everything uses the computed values from CSS.
- MinContent:
- Containing blocks act as if they have 0 width.
- All line breaking opportunities are taken.
- MaxContent:
- Containing blocks act as if they have infinite width.
- Only forced line breaks are accepted.
The above was based on a set of misunderstandings of CSS sizing.
A major problem with the above was that *all* containing blocks
behaved differently during intrinsic size layout, not just the
relevant one.
With this patch there are only two layout modes:
- Normal:
- Everything uses the computed values from CSS.
- IntrinsicSizeDetermination:
- One or more boxes have size constraints applied.
There are two size constraints per layout box, set here:
- FormattingState::NodeState::width_constraint
- FormattingState::NodeState::height_constraint
They are of type SizeConstraint and can be one of None, MinContent,
or MaxContent. The default is None.
When performing an IntrinsicSizeDetermination layout, we now assign
a size constraint to the box we're trying to determine the intrinsic
size of, which is then honored by using two new helpers to query
the dimensions of containing blocks:
- FormattingContext::containing_block_width_for(Box)
- FormattingContext::containing_block_height_for(Box)
If there's a relevant constraint in effect on the Box, the size of
its containing block is adjusted accordingly.
This is essentially an implementation of the "available space"
constraints from CSS-SIZING-3. I'm sure some things will break from
this, and we'll have to deal with that separately.
Spec: https://drafts.csswg.org/css-sizing-3/#available
In cases where flex item cross size is based on the flex line cross
size, the spec specifically says to transfer the *outer* cross size of
the line. We were ignoring the "outer" part.
This patch fixes that by subtracting the cross margins from the size.
Instead of recomputing the "remaining free space" per flex line,
remember it after calculating it during the "resolve flexible lengths"
step so we can reuse it later.
We were not applying the distributed space to the used offset of flex
items, as we were only assigning the margins to the layout state of the
box, not the internal FlexItem::margins.
This patch adds support for MinContent and MaxContent layout to FFC.
This means that an FFC can now calculate intrinsic sizes for the flex
container, to be used by the parent formatting context.
There are some FIXME's as usual, but this already works on basic things.