Commit graph

126 commits

Author SHA1 Message Date
Jelle Raaijmakers
087601832a LibWeb: Set fit-content width for buttons in used values, not computed
For button layouts, we were overriding the computed `width` value with
`fit-content` in `TreeBuilder::wrap_in_button_layout_if_needed()`. But
the spec asks us to set the _used value_ instead, so we now actually
calculate the fit-content width and set the box' content width to it.

Fixes #2516.
2025-08-18 11:04:34 +01:00
Andreas Kling
41e8211405 LibWeb: Honor "should treat as auto" cases in aspect-ratio decisions 2025-08-05 21:33:41 +02:00
Jelle Raaijmakers
af8b256832 LibWeb: Avoid floats for BFC/FFC/GFCs with a definite width
Applicable FCs with an indefinite width simply shrink in their available
space as long as floats are intruding, but as soon as we have a definite
width we must push the box down until it it has enough space again.

Fixes #4136.
2025-08-01 14:26:12 +02:00
Jelle Raaijmakers
d27395cf1f LibWeb: Add spec links and text for BFC/FFC/GFC float intrusion behavior
No functional changes.
2025-08-01 14:26:12 +02:00
Jelle Raaijmakers
b6732240c7 LibWeb: Avoid float intrusions for BFCs, FFCs and GFCs
We were only avoiding float intrusions for BFCs, but FFCs and GFCs
should also accommodate for any floats present.

Work towards #4136.
2025-07-28 12:13:06 +02:00
Jelle Raaijmakers
ab3e9799d5 LibWeb: Allow negative margins to influence inline offset after float
In 89ba00304c, the box' X position was
capped at 0 to prevent negative X positions to act as if there were
intruding floats on the left side. Instead, we need to check whether the
left side float intrusion we are going to calculate matters at all -
because if there's no matching float box, the intrusion is always going
to be 0 and we don't need to take the box' X position into account.

Fixes the floating publication images on https://lexfridman.com/.
2025-07-28 12:11:56 +02:00
Jelle Raaijmakers
115e5f42af LibWeb: Improve graphical list item marker positioning
While 788d5368a7 took care of better text
marker positioning, this improves graphical marker positioning instead.

By looking at how Firefox and Chrome render markers, it's clear that
there are three parts to positioning a graphical marker:

  * The containing space that the marker resides in;
  * The marker dimensions;
  * The distance between the marker and the start of the list item.

The space that the marker can be contained in, is the area to the left
of the list item with a height of the marker's line-height. The marker
dimensions are relative to the marker's font's pixel size: most of them
are a square at 35% of the font size, but the disclosure markers are
sized at 50% instead. Finally, the marker distance is always gauged at
50% of the font size.

So for example, a list item with `list-style-type: disc` and `font-size:
20px`, has 10px between its start and the right side of the marker, and
the marker's dimensions are 7x7.

The percentages I've chosen closely resemble how Firefox lays out its
list item markers.
2025-07-17 09:35:09 +01:00
Jelle Raaijmakers
788d5368a7 LibWeb: Improve list item marker positioning and alpha/roman text
This commit is a three-parter that is hard to separate without breaking
marker rendering:

  1. Any marker style that results in a string, except for a literal
     string (e.g. `list-style-type: "@"`), should get the string ". "
     appended. We forgot to do this for the alpha and roman types.

  2. Instead of using the "pixel size rounded up" from a font and adding
     an arbitrary 1 to that, we now use the exact pixel size for as long
     as possible to improve our vertical positioning of markers.

  3. Instead of always adding a "default marker width" to the marker
     content width, we now only do this if we did not have text metrics
     available (i.e. the marker style is not a text type). This greatly
     improves horizontal positioning of text markers.
2025-07-15 19:05:36 +01:00
Andreas Kling
11fa5fdd47 LibWeb: Honor box-sizing for block-level replaced element widths
Before this change, we were always behaving as if box-sizing were
content-box for block-level replaced element widths.

This fixes the squishy logo on https://videolan.org/
2025-07-14 11:16:13 +02:00
Tim Ledbetter
30cdacc05a LibWeb: Add flow relative values for the clear property
Currently `inline-start` and `inline-end` are always treated as `left`
and `right` respectively.
2025-06-17 09:26:26 +02:00
Tim Ledbetter
28b24b72bc LibWeb: Don't resolve flow-relative values for float too early
This allows `getComputedStyle()` to return the correct value if `float`
is set to `inline-start` or `inline-end`
2025-06-17 09:26:26 +02:00
InvalidUsernameException
1d0cfdc839 LibWeb: Consider margins during fit-content sizing in BFC
`BlockFormattingContext::compute_width()` stores the left and right
margins in the layout state at the very end of the function. However,
before doing so, it calls `FormattingContext::calculate_inner_width()`
which ends up calling `FormattingContext::calculate_stretch_fit_width()`
if the current box has `width: fit-content`.

Due to this, `calculate_stretch_fit_width()` would always see the
margins from the layout state as zero and therefore not take them into
account. Subsequently, the calculated width ended up being wrong.

Saving margins on the layout state earlier, before calling
`calculate_inner_width()`, makes sure that the width is calculated
correctly.
2025-06-05 17:56:19 +02:00
Ruben
3263b629c6 LibWeb: Correct the calculation for right float intrusion
We used to subtract the maximum right margin of any containing box,
but we want to subtract the entire right margin instead. This yielded
incorrect intrusions for right floats not placed in the root.
2025-05-27 17:31:47 +02:00
Ruben
09f5ce42f6 LibWeb: Ensure floats are correctly placed under the preceding float
Previously floats would be placed next to the highest float that
fitted the new float on its line. However, this violates the rule
that floats should be placed under the preceding float if it does
not fit next to it.
2025-05-23 10:58:15 +02:00
Jelle Raaijmakers
170e599bd9 LibWeb: Always set the Y offset of the current block in BFC
Originally part of a fix in 15103d172c, it
appears that this is no longer necessary and received a better fix in a
more recent commit. Resolves a visual regression with the ACID3 test.
2025-05-16 20:33:27 +02:00
Jelle Raaijmakers
123abe0e79 LibWeb: Overlap float space and left margins for all shared ancestors
We would only correct for the left margin of the containing block of the
child box that we are laying out, but we actually need to correct for
all left margins up until the containing block that contains both the
float and the child box.

Fixes #4639.
2025-05-09 14:13:02 +02:00
Jelle Raaijmakers
79352ad725 LibWeb: Remove application of vertical float clearance to BFC Y offset
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.
2025-04-23 13:24:58 +02:00
Jelle Raaijmakers
1731389bfe LibWeb: Return early in clear_floating_boxes()
Get rid of an indentation level. No functional changes.
2025-04-23 13:24:58 +02:00
Jelle Raaijmakers
47ed39a790 LibWeb: Reuse floating box' margin box rect for clear: ...
We already store the resulting margin box rect for floating boxes
relative to their root coordinate space, so don't calculate them again.
No functional changes.
2025-04-23 13:24:58 +02:00
Andreas Kling
28d564197c LibWeb: Compute containing block boxes once at start of layout
We now cache the containing block (box) once at the start of layout,
which allows Layout::Node::containing_block() to return instantly
instead of doing tree traversal.

Removes a 0.7% profile item on Speedometer 3.
2025-04-19 01:14:02 +02:00
Jelle Raaijmakers
76105d6a02 LibWeb: Use LayoutState::set_content_x/y() where possible
No functional changes.
2025-04-11 02:34:28 +01:00
Jelle Raaijmakers
4a6998497f LibWeb: Don't recalculate margin box rect for preceding floats
We already stored that rect while building up the side data during
floating box layout. No functional changes.
2025-04-11 02:34:28 +01:00
Jelle Raaijmakers
23f0fddeab LibWeb: Let LineBuilder decide on Y-coordinate for float insertion
Instead of reaching into the IFC of the LineBuilder from the BFC, we
should let LineBuilder determine how to deal with the running vertical
float clearance. No functional changes.
2025-04-01 16:02:53 +02:00
Jelle Raaijmakers
86b831750d LibWeb: Layout children a bit earlier in BlockFormattingContext
We don't need to duplicate this piece of code. No functional changes.
2025-03-31 08:11:10 +01:00
Aliaksandr Kalenik
732a5cdc12 LibWeb: Respect min/max-content available size in auto width calculation
...for block level boxes. Otherwise we end up resolving auto width as
zero during intrinsic layout, which leads to incorrectly applied
max-width constraint.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/4123
2025-03-28 02:13:41 +00:00
Oliver Medhurst
60fd406903 LibWeb: Fix inside list style overriding content offset
Previously, if a horizontal margin was set on a list-item and
it was list-style-position: inside, it would effectively remove
that margin.
2025-03-27 23:10:09 +00:00
Jelle Raaijmakers
15103d172c LibWeb: Apply remaining vertical float clearance to next block level box
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.
2025-03-27 17:21:56 +00:00
Jelle Raaijmakers
f340f8682b LibWeb: Do not clear float sides for floating boxes with clear: ..
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.
2025-03-27 00:56:56 +00:00
stasoid
a6935299eb LibWeb: Correctly calculate static position rect when absolutely
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
2025-03-17 15:55:06 +01:00
Aliaksandr Kalenik
227b4c38b7 LibWeb: Use parent's available space for anonymous blocks in BFC
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
2025-03-15 13:31:07 +01:00
Andreas Kling
591f06f887 LibWeb: Leave right margin along when BFC solves for width
We were incorrectly shrinking the used right margin to make it fit
within the available space, even though this was not necessary and the
margin is allowed to stretch beyond the containing block.

This is not observable yet, but will be once we start exposing used
margin values in a subsequent change.
2025-02-22 20:02:16 +01:00
Andreas Kling
0705efda13 LibWeb: Use cached UsedValues pointer for floating boxes in more places 2025-02-12 00:39:36 +01:00
Sam Atkins
b987d53926 LibWeb: Migrate ListItemMarkerBox's text from ByteString to String 2025-02-11 10:39:27 +01:00
Jelle Raaijmakers
e8bc6e9e8e LibWeb: Set float Y offset using margin box bottom instead of height
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.
2025-01-28 01:12:23 +01:00
Gingeh
f3f1db7725 LibWeb: Cache the margin box rect for floating boxes 2025-01-21 21:06:55 +01:00
Timothy Flynn
85b424464a AK+Everywhere: Rename verify_cast to as
Follow-up to fc20e61e72.
2025-01-21 11:34:06 -05:00
Sam Atkins
ebc91686b5 LibWeb/Layout: Add FIXMEs where we're using the wrong font
It's not clear how to address these right now, so add a FIXME to make
them easier to find and address later.
2024-12-06 02:57:34 +01:00
Kostya Farber
81f8866606 LibWeb: Lay out the fieldset's rendered legend 2024-11-29 12:36:52 +00:00
Jeremia Dominguez
eafa70331d LibWeb/Layout: Simplified margin collapsing
Rather than accumulating margins into a vector, and then looping through
them when resolving the margin, it's much simpler to just update two
fields, and sum them when resolving.
2024-11-24 14:45:28 +01:00
Andreas Kling
fbe9395928 LibWeb: Stop treating intrinsic size keywords as auto in CSS heights
This commit introduces proper handling of three intrinsic size keywords
when used for CSS heights:

- min-content
- max-content
- fit-content

This necessitated a few plumbing changes, since we can't resolve these
values without having access to containing block widths.

This fixes some visual glitches on https://www.supabase.com/ as well
as a number of WPT tests. It also improves the appearance of dialogs.
2024-11-21 19:21:51 +01:00
Aliaksandr Kalenik
a8c1d12e84 LibWeb: Fix percentage insets resolution for grid items
compute_inset() was incorrectly retrieving the containing block size
because containing_block() is unaware of grid areas that form a
containing block for grid items but do not exist in the layout tree.
With this change, we explicitly pass the containing block into
compute_inset(), allowing it to correctly provide the containing block
sizes for grid items.
2024-11-11 20:20:39 +01:00
Aliaksandr Kalenik
07d8ddb5fa LibWeb: Reduce usage of Node::containing_block() in BFC
Explicitly pass containing block width in
resolve_vertical_box_model_metrics() instead of doing containing block
box lookup.

This is a part of refactoring towards removing containing_block() usage
that will allow us introduce partial layout.
2024-11-11 20:20:39 +01:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Andreas Kling
13d7c09125 Libraries: Move to Userland/Libraries/ 2021-01-12 12:17:46 +01:00
Andreas Kling
0ecefbff57 LibWeb: Move absolute positioning up to FormattingContext
It seems like both BFC and IFC can have absolutely positioned children.
It's a bit strange, but consider the following HTML:

<html><body>foobar<img style="position: absolute"></body></html>

In such a document, the <img> element is an absolutely positioned child
of a block-level element (<body>) with no block-level children.
An IFC is established for <body>, and needs to handle layout for <img>.
2021-01-06 19:20:49 +01:00
Andreas Kling
5721b2a3da LibWeb: Rename LayoutStyle => CSS::ComputedValues
This object represents the CSS "computed values" so let's call it that.
2021-01-06 14:58:48 +01:00
Andreas Kling
bf3772362a LibWeb: When collapsing margins, consider border box heights
Empty boxes should be fully collapsed, but a box with border and/or
padding is not empty.

This fixes an issue where <hr> elements were getting weirdly collapsed
since they have zero content height (but some border height.)
2021-01-02 03:48:35 +01:00
Andreas Kling
07dd73c351 LibWeb: Remove hand-rolled is_foo() helpers in Layout::Node classes 2021-01-01 18:56:49 +01:00
Andreas Kling
b322452ef4 LibWeb: Silence BFC spam about not knowing how to place boxes
This gets way too noisy on some pages, and isn't even interesting.
2020-12-18 10:25:50 +01:00
Andreas Kling
2a5877b02c LibWeb: Fix shrink-to-fit layout for position:absolute
We were following the spec incorrectly. The comment was right, but the
code was wrong.
2020-12-17 01:47:42 +01:00
Andreas Kling
a5422a210f LibWeb: Use the correct containing block for position:absolute width 2020-12-17 01:46:51 +01:00
Andreas Kling
f35f605a24 LibWeb: Make sure the ICB is at least as tall as the viewport
This is a hack until we implement a proper overflow mechanism. For now,
this allows us to right-click below the lowest content on the page.
2020-12-17 00:58:23 +01:00
Andreas Kling
17c529e6c5 LibWeb: Generate the CSS::ValueID enum and its helper functions 2020-12-15 20:40:10 +01:00
Andreas Kling
b861de0a13 LibWeb: Use final box model metrics for absolute 'right' and 'bottom'
We've already converted these to floats, so no need to do it again.
2020-12-14 12:49:35 +01:00
Andreas Kling
d1479aef56 LibWeb: Layout absolutely positioned children *after* computing height
This is required for CSS "bottom" to work correctly on absolutely
positioned elements.
2020-12-14 11:33:11 +01:00
Andreas Kling
9d442ba606 LibWeb: Store layout box model metrics as floats
Instead of storing them as CSS::Lengths, we now store the resolved
values for margin/padding/border/offset top/right/bottom/left with
each Layout::NodeWithStyleAndBoxModelMetrics.

This simplifies a lot of code since it's no longer necessary to
resolve values before using them.
2020-12-12 21:28:29 +01:00
Andreas Kling
66e9dde86f LibWeb: Don't place floating boxes before everything else
Instead, just handle them as we go about laying out block-level boxes.
2020-12-12 19:31:46 +01:00
Andreas Kling
552ba1b0a3 LibWeb: Remove some unnecessary is_replaced() checks in BFC
BFC::compute_width() has a short-circuit path for replaced elements.
2020-12-11 22:59:46 +01:00
Andreas Kling
67732df034 LibWeb: Move replaced element layout out of Layout::ReplacedBox
Replaced elements are now laid out by the current formatting context.
Since the logic is almost identical in BFC and IFC, it's implemented
by static helpers in FormattingContext.
2020-12-11 22:59:46 +01:00
Andreas Kling
e8d6691470 LibWeb: Fix inline-block width computation with no specified width
Undefined width should be treated the same as width:auto;
2020-12-11 22:59:46 +01:00
Andreas Kling
be18ac36b2 LibWeb: Use CSS::Length::resolved_or_zero() in a few places 2020-12-07 20:46:01 +01:00
Andreas Kling
cb04a5c52c LibWeb: Forget floating boxes once we've gone past them
Once we've generated enough lines to make it past all the floating
boxes on either side, just forget those boxes. This simplifies the
available space computation since we don't have to consider boxes
that can't vertically intersect the current line anyway.
2020-12-06 21:11:28 +01:00
Andreas Kling
9470169317 LibWeb: Floating elements should not stack horizontally after clear
After we've cleared past some floating elements, we should not keep
stacking new floats horizontally.

Instead, new floats after the clear should once again start at the
left or right edge of their containing block.
2020-12-06 21:00:04 +01:00
Andreas Kling
59de4adb60 LibWeb: Pass current target box to BFC::run()
The BFC "context box" is now the outer box of the block formatting
context. Previously the context box was always the current target box,
which made it hard to reason about who was really the containing block
of whom in various places.

Note that IFC still has the containing block as its context box, this
change only affects BFC. However, to clarify the situation in IFC,
I've added a containing_block() getter than returns the context_box().
2020-12-06 20:05:04 +01:00
Andreas Kling
b638e74b68 LibWeb: Move box floatation out of normal flow layout
Layout of floating children now places the child in the normal flow and
then floats it left or right afterwards.
2020-12-06 20:05:04 +01:00
Andreas Kling
d582828040 LibWeb: Layout floating children per block instead of whole BFC at once
Instead of plowing through all the floating boxes within a BFC and
doing all the layout up front, do the children of each block as we go.
This will allow us to know the vertical position of the containing
block when placing floats.
2020-12-06 20:05:04 +01:00
Andreas Kling
6b4281c3aa LibWeb: Do floating box placement together with other boxes
I realized that we're supposed to float the boxes sideways, but not
always to y=0, so that makes it logical to share the placement logic
with other normal non-replaced blocks.

This is still pretty buggy but we're getting closer. :^)
2020-12-06 01:59:35 +01:00
Andreas Kling
af757a1659 LibWeb: Naively implement the CSS clear property
This is definitely not fully-featured, but basically we now handle
the clear property by forcing the cleared box below the bottom-most
floated box on the relevant side.
2020-12-06 01:45:51 +01:00
Andreas Kling
26a9ab7cd5 LibWeb: Floating boxes with width:auto should be shrink-to-fit 2020-12-06 01:16:45 +01:00
Andreas Kling
615a4d4f71 LibWeb: First slightly naive implementation of CSS floats :^)
Boxes can now be floated left or right, which makes text within the
same block formatting context flow around them.

We were creating way too many block formatting contexts. As it turns
out, we don't need one for every new block, but rather there's a set
of rules that determines whether a given block creates a new block
formatting context.

Each BFC keeps track of the floating boxes within it, and IFC's can
then query it to find the available space for line boxes.

There's a huge hack in here where we assume all lines are the exact
line-height. Making this work with vertically non-uniform lines will
require some architectural changes.
2020-12-05 22:51:03 +01:00
Andreas Kling
157896cc0b LibWeb: Block layout should account for vertical border space
We were not accounting for space occupied by borders when computing
the vertical (y) position of blocks. This meant that blocks with wide
top/bottom borders could bleed into each other incorrectly.

Fix this by using the combined padding+border geometry instead of just
the padding when placing blocks on the y axis.
2020-12-04 21:24:35 +01:00
Andreas Kling
c39e29d186 LibWeb: Block layout should resolve relative lengths against each box
We were incorrectly resolving relative length units (ex, em, etc.)
against the containing block in many cases. Fix this to resolve them
against the descendant box we're currently processing.
2020-12-04 21:22:49 +01:00
Andreas Kling
169a9150cb LibWeb: Rename LayoutNode::is_root() => is_initial_containing_block()
Let's use spec language for this. :^)
2020-12-04 16:27:07 +01:00
Andreas Kling
b1e75437c9 LibWeb: Keep track of the parent of each formatting context
This will allow us to find the containing block formatting context
when needed later on.
2020-11-25 21:26:58 +01:00
Andreas Kling
5aeab9878e LibWeb: Rename LayoutNode classes and move them into Layout namespace
Bring the names of various boxes closer to spec language. This should
hopefully make things easier to understand and hack on. :^)

Some notable changes:

- LayoutNode -> Layout::Node
- LayoutBox -> Layout::Box
- LayoutBlock -> Layout::BlockBox
- LayoutReplaced -> Layout::ReplacedBox
- LayoutDocument -> Layout::InitialContainingBlockBox
- LayoutText -> Layout::TextNode
- LayoutInline -> Layout::InlineNode

Note that this is not strictly a "box tree" as we also hang inline/text
nodes in the same tree, and they don't generate boxes. (Instead, they
contribute line box fragments to their containing block!)
2020-11-22 15:56:27 +01:00
Andreas Kling
e1a24edfa9 LibWeb: Reorganize layout system in terms of formatting contexts
This is a first (huge) step towards modernizing the layout architecture
and bringing it closer to spec language.

Layout is now performed by a stack of formatting contexts, operating on
the box tree (or layout tree, if you will.)

There are currently three types of formatting context:

- BlockFormattingContext (BFC)
- InlineFormattingContext (IFC)
- TableFormattingContext (TFC)

Document::layout() creates the initial BlockFormattingContext (BFC)
which lays out the initial containing block (ICB), and then we recurse
through the tree, creating BFC, IFC or TFC as appropriate and handing
over control at the context boundaries.

The majority of this patch is just refactoring the old logic spread out
in LayoutBlock and LayoutTableRowGroup, and turning into these context
classes instead. A lot more cleanup will be needed.

There are many architectural wins here, the main one being that layout
is no longer performed by boxes themselves, which gives us much greater
flexibility in the outer/inner layout of a given box.
2020-11-22 14:36:56 +01:00