Browsers such as Chrome and Firefox apply an arbitrary scale to the
current font size if `normal` is used for `line-height`. Firefox uses
1.2 while Chrome uses 1.15. Let's go with the latter for now, it's
relatively easy to change if we ever want to go back on that decision.
This also requires updating the expectations for a lot of layout tests.
The upside of this is that it's a bit easier to compare our layout
results to other browsers', especially Chrome.
Whenever we introduce a block element in a container that at that point
has only had inline children, we create an anonymous wrapper for all the
inline elements so we can keep the invariant that each container
contains either inline or non-inline children. For some reason, we
ignore all the out-of-flow nodes since they are layed out separately and
it was thought that this shouldn't matter.
However, if we are dealing with inline blocks and floating blocks, the
order of the inline contents _including_ out-of-flow nodes becomes very
important: floating blocks need to take the order of nodes into account
when positioning themselves.
Fix this by simply hoisting the out-of-flow nodes into the anonymous
wrapper as well.
Fixes the order of blocks in #4212. The gap is still not present.
In 15103d172c we applied any remaining vertical float clearance to the
BFC's current Y offset for the next block to layout, because a `<br
style="clear: left">` could introduce clearance that would otherwise be
ignored. However, a `<div>` that floats _and_ clears `right` also
introduces this clearance and it is obvious that this should not push
down any subsequent blocks to layout in the current BFC.
Turns out, we don't need this change anymore. Some other later change
also fixed the underlying issue, and by getting rid of the original fix
we can now render https://en.wikipedia.org/wiki/SerenityOS correctly
again.
Fixes#4418.
There's a specific (and thankfully very common!) scenario where we can
actually skip calculating the automatic minimum size for flex items.
In single-line (no wrapping) flex containers, if the sum of all item
flex base sizes is <= the flex container's main size, we know that
none of the items will be shrunk by the layout algorithm.
And so for any flex item with definite main size AND automatic minimum
main size, we can treat the automatic minimum size as 0.
Before this change, we assigned the style sheet's location *after* its
content rules were parsed and added to it. This meant any `@import`s
would try to fetch their style sheet before they knew the URL they
should fetch it relative to.
When determining the content/margin box rects within their ancestor's
coordinate space, we were returning early if the passed in values
already belonged to the requested ancestor. Unfortunately, we had
already applied the used values' offset to the rect, which is the offset
to the ancestor's ancestor.
This simplifies the logic to always apply the rect offset after checking
if we've reached the ancestor. Fixes determining float intrusions inside
block elements with `margin: auto` set.
Fixes#4083.
If a block with inline children ends with a line break clearing any
floats, we not only need to take the introduced clearance into account
for the next line box, but the containing block needs to set the correct
height as well.
Since the spec calls for using the last line box' bottom as the resolved
height (if treated as auto), we now correctly apply the clearance to the
previous line box' bottom coordinate.
Fixes#4058.
We were accidentally providing it with absolute Y-coordinates, messing
up stacked floating boxes that would otherwise intrude on each other.
Fixes#4160.
Whenever we generate line boxes, we might end up with a residual
vertical float clearance by way of having a `<br>` with `clear: ..` set.
Set the Y offset of the next block level box to place by this vertical
clearance.
Relates to #4058.
We were introducing a line break and applying vertical clearance to the
inline formatting context, but that vertical clearance only applied to
new floating boxes. We should move the current block offset to the
vertical clearance to make sure the next line box starts beyond the
cleared floats.
There was a layout test for `<br>` with `clear: ..` set, but that test
did not actually do anything - removing the `clear` property would
result in the same layout. Replace that test with something that
actually tests float clearing.
Relates to #4058.
Our recent change to get rid of the "move 1px at a time" algorithm in
the float positioning logic introduced the issue that potentially
intersecting float boxes were not evaluated in order anymore. This could
result in float boxes being pushed down further than strictly necessary.
By finding the highest point we can move the floating box to and
repeating the process until we're no longer intersecting any floating
box, we also solve some edge cases like intersecting with very long
floating boxes whose edges lay outside the current box' edges.
This is by no means the most efficient solution, but it is more correct
than what we had until now.
Fixes#4110.
We used to always clear the side data after encountering a box with
`clear: ..`, but this is not the right thing to do if that same box also
has `float: ..` set. For example, with `clear: right` and `float: left`
it might be that the next box still wants to clear the right side, and
since the previous box is floating it did not push the next box down far
enough to justify clearing the side data at that point.
This changes the logic to only clear the float side if the clearing box
itself is not floating. We also no longer clear the opposite side after
placing a floating box; that doesn't seem to be necessary anymore.
Fixes#4102.
When generating line boxes, we place floats simultaneously with the
other items on the same line. The CSS text spec requires us to trim the
whitespace at the end of each line, but we only did so after laying out
all the line boxes.
This changes the way we calculate the current line box width for floats
by subtracting the amount of pixels that the current trailing whitespace
is using.
Fixes#4050.
It's safe to remove this pointer because intrinsic layout should never
look up a box's state beyond its containing block.
This change affects the expectations of two layout tests, but both
already differ slightly from other browsers, and the difference between
expectations is less than 5px.
All abspos boxes are expected to be blockified, so we are certain that
we can ignore non-box elements when collecting abspos nodes for layout.
Fixes a crash caused by an attempt to cast a BreakNode to a Box while
performing abspos layout.
Quite simply, ignore any declarations for properties we don't want,
while computing a pseudo-element's style.
I've imported a WPT test for this, which fails without this patch.
...boxes with non-auto height.
We know for sure that by the time we layout abspos boxes, their
containing block has definite height, so it's possible to resolve
non-auto heights and mark it as definite before doing inner layout.
Big step towards having reasonable performance on
https://demo.immich.app/photos because now we avoid a bunch of work
initiated by mistakenly invoked intersection observer callbacks.
Co-Authored-By: Andreas Kling <andreas@ladybird.org>
With this change we no longer stretch "width: auto" for replaced
elements and also use "width calculation rules for block-level replaced
elements", like suggested by the spec.
positioned element is a descendant of inline-block
Sets inline block offsets in InlineFormattingContext.cpp, but this is
not enough. When static position rect is calculated during layout,
not all ancestors of abspos box may have their offsets calculated yet
(more info here: https://github.com/LadybirdBrowser/ladybird/pull/2583#issuecomment-2507140272).
So now static position rect is calculated relative to static containing
block during layout and calculation relative to actual containing block
is done later in
FormattingContext::layout_absolutely_positioned_element.
Fixes wpt/css/CSS2/abspos/static-inside-inline-block.html
Instead of directly invoking `to_px()`,
`calculate_min_content_contribution()` needs to use
`calculate_inner_width()` and `calculate_inner_height()`, which are
aware of how to correctly handle `min-content` and `max-content` values.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/3469
Height resolution assumes that when available space is definite, it
matches the size of non-anonymous containing block. With this change, we
correctly maintain this assumption when box is wrapped in anonymous
node.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/3422
When setting `font-family: monospace;` in CSS, we have to interpret
the keyword font sizes (small, medium, large, etc) as slightly smaller
for historical reasons. Normally the medium font size is 16px, but
for monospace it's 13px.
The way this needs to behave is extremely strange:
When encountering `font-family: monospace`, we have to go back and
replay the CSS cascade as if the medium font size had been 13px all
along. Otherwise relative values like 2em/200%/etc could have gotten
lost in the inheritance chain.
We implement this in a fairly naive way by explicitly checking for
`font-family: monospace` (note: it has to be *exactly* like that,
it can't be `font-family: monospace, Courier` or similar.)
When encountered, we simply walk the element ancestors and re-run the
cascade for the font-size property. This is clumsy and inefficient,
but it does work for the common cases.
Other browsers do more elaborate things that we should eventually care
about as well, such as user-configurable font settings, per-language
behavior, etc. For now, this is just something that allows us to handle
more WPT tests where things fall apart due to unexpected font sizes.
To learn more about the wonders of font-size, see this blog post:
https://manishearth.github.io/blog/2017/08/10/font-size-an-unexpectedly-complex-css-property/
This essentially reverts 1b46a52cfc
and adds more tests.
The reverted change was an incorrect workaround for the real issue,
which was that we weren't creating anonymous wrapper boxes around inline
children of table-cell boxes.
Now that this has been fixed, we can go back to aligning text properly.
This fixes an issue where CSS vertical-align on a table-cell box would
incorrectly apply to both the table-cell box and any inline content it
had inside.
By the time we're measuring the height of a BFC root, we've already
collapsed all relevant margins for the root and its descendants.
Given this, we should simply use 0 (relative to the BFC root) as the
lowest block axis coordinate (i.e Y value) for the margin edges.
This fixes a long-standing issue where BFC roots were sometimes not tall
enough to contain their children due to margins.
MediaQueryList will now remember if a state change occurred when
evaluating its match state. This memory can then be used by the document
later on when it's updating all queries, to ensure that we don't forget
to fire at least one change event.
This also required plumbing the system visibility state to initial
about:blank documents, since otherwise they would be stuck in "hidden"
state indefinitely and never evaluate their media queries.
This was mainly a matter of deferring the wrapping of the button's
children until after its internal layout tree has been constructed.
That way we don't lose any pseudo elements spawned along the way.
Fixes#2397.
Fixes#2399.
BFC roots behave differently in that their height is computed twice,
before and after inside layout, since automatic height depends on the
results of inside layout. Other formatting contexts only require the
"before" pass, and so we can treat their content sizes as definite
before proceeding with inside layout.
This makes https://play.tailwind.com/ look beautiful. :^)
When positioning floats against an edge, we are taking all current
relevant floats at that side into account to determine the Y offset at
which to place the new float. However, we were using the margin box
height instead of the absolute bottom position, which disregards the
current float's Y-position within the root, and we were setting the Y
offset to that height, instead of taking the new float's Y position
inside of the root into account.
The new code determines the lowest margin bottom value within the root
of the current floats, and adds the difference between that value and
the new float's Y position to the Y offset.