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.
This builds on the work done by implementing the flex order CSS
property and implements flex reverse layouts by just reversing
the order and the items within each order bucket.
Before if an element didn't have a main min size we would clamp
it to a literal zero. If that element also had a flex-basis 0
it's width would end up being 0.
This patch adds a determine_min_main_size_of_child function that
will calculate the minimum main size for the box based on the
content of the box.
We use the result of that function now instead of clamping
the element main min size to 0.
This also adds one more box to the flex.html test page, which is
the same flex: 0 0 0 box but with flex-direction: column.
Before this the flex layout didn't take into account the applied
borders or padding while laying out the items.
The child's top and left borders would get painted over the
parent's borders, also due to it not taking borders into account,
children with borders would overlap each other.
Due to it not taking padding into account, the children would get
drawn outside the parent element.