Commit graph

62 commits

Author SHA1 Message Date
=
a3e973970a LibWeb/CSS: Check overflow value before determining box baseline
The CSS spec says the baseline of an inline-block should be the bottom
margin when either the overflow property is not 'visible' or there are
no in-flow line boxes. Previously, only the latter case was checked.

This fixes 1 WPT test:
https://wpt.live/css/css-align/baseline-of-scrollable-1a.html
2025-10-13 09:47:32 +01:00
Andreas Kling
f00f975cf4 LibWeb: Treat percentage max-width as none during min-content sizing
...but only for non-replaced boxes. This is what CSS-SIZING-3 tells us
to do, and we were already doing it for width, but neglecting max-width.
2025-09-28 19:25:18 +02:00
Andreas Kling
4b281fa880 LibWeb: Make Length ctor/dtor/factory inline
These functions are trivial, and we were actually bleeding a lot of time
in profiles to just function entry/exit.

By marking Length::make_px() as [[nodiscard]], we also exposed some
places that were creating a Length and not using it for anything.
2025-09-24 23:59:41 +02:00
zac
191b026c9f LibWeb: Rename is_generated to is_generated_for_pseudo_element
While much longer, this better conveys the intent. Since this method
isn't used too often, I think the extra length is worth it.
2025-09-24 12:33:17 +01:00
Callum Law
405c5ffa60 LibWeb: Propagate border-box dimensions when getting max content width
This means that we now calculate the inner width correctly for `display:
inline-block` nodes when we have `box-sizing: border-box` and
`min-width`, as we would previously assume these dimensions were all `0`
2025-09-10 11:41:30 +02:00
Sam Atkins
dd122e2f74 LibWeb: Make LengthBox hold LengthPercentageOrAuto
Not every user of this requires an `auto` state, but most do.

This has quite a big diff but most of that is mechanical:
LengthPercentageOrAuto has `resolved_or_auto()` instead of `resolved()`,
and `to_px_or_zero()` instead of `to_px()`, to make their output
clearer.
2025-09-04 13:31:24 +01:00
Sam Atkins
80310b5ebf LibWeb/Layout: Use LengthOrAuto in abspos non-replaced width computation 2025-09-04 13:31:24 +01:00
Luke Wilde
b17783bb10 Everywhere: Change west consts caught by clang-format-21 to east consts 2025-08-29 18:18:55 +01:00
Tim Ledbetter
cb1a1a5cb5 LibWeb: Replace is<T>() with as_if<T>() where possible 2025-08-25 18:45:00 +02:00
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
Aliaksandr Kalenik
4d223462a5 LibWeb: Fix cyclic percentage resolution in calculate_min_content_width
Brings back some code removed in 652a457, but this time with explanation
from https://www.w3.org/TR/css-sizing-3 spec.
2025-08-13 10:14:37 +01:00
Aliaksandr Kalenik
652a457f52 LibWeb: Fix grid layout for replaced items with percentage max-width
CSS grid specification states that for grid items with a replaced
element and a percentage preferred size or maximum size, the percentage
should be resolved against 0 during content-based minimum size
calculation. This makes sense, as it prevents replaced items from
overshooting their grid track while intrinsic track sizes are
calculated, and allows later track size resolution steps to scale
replaced items to fit their grid track.
2025-08-10 11:07:02 +02:00
Andreas Kling
41e8211405 LibWeb: Honor "should treat as auto" cases in aspect-ratio decisions 2025-08-05 21:33:41 +02:00
Timothy Flynn
8b6e3cb735 LibWeb+LibUnicode+WebContent: Port DOM:CharacterData to UTF-16
This replaces the underlying storage of CharacterData with Utf16String
and deals with the fallout.
2025-07-24 19:00:20 +02:00
Andreas Kling
8d02f28cc2 LibWeb: Don't treat non-replaced sizes as 0 for min-content contrib
This behavior is part of the cyclic percentage contribution logic from
CSS-SIZING-3 which explicitly only applies to non-replaced boxes.

This fixes an issue on Discord where buttons in the settings UI were
cropped to a narrower width than intended.

Fixes #3572
2025-07-23 19:52:59 +02:00
Michael Manganiello
89036dd70c LibWeb: Fix size computation for elements with max-width and max-height
The specification [1] indicates that the tentative used width and height
should be computed first, and if they exceed the `max-width` or
`max-height`, the rules should be applied again using the computed
values of `max-width` and `max-height`.

The only required change to follow the spec is to remove the early
`return` statements, in both `compute_width_for_replaced_element`
and `compute_height_for_replaced_element`.

Fixes #5100.

[1] https://www.w3.org/TR/CSS22/visudet.html#min-max-widths
2025-07-20 17:34:19 +02:00
Tim Ledbetter
5478361ba0 LibWeb: Avoid division by zero with small aspect ratios 2025-07-13 05:41:48 +02:00
Jelle Raaijmakers
1e0013a3bc LibWeb: Use margin box height for inline-block vertical alignment
For `vertical-align: middle` and `vertical-align: text-bottom`, we used
just the content height of the inline box to determine its alignment
position. This caused incorrect positioning when padding is applied.

This fixes the button alignment on our GitHub page.

Fixes #290.
2025-07-12 18:03:14 +02:00
Andreas Kling
07838016c8 LibWeb: Make layout code aware that we create BFC for foreignObject
We were already always doing this, but through an unusual mechanism:
SVG layout creates a BFC on the stack when laying out foreignObject
subtrees.

This change makes the rest of the layout system aware of this, and
also allows it to be reflected in layout dumps.
2025-07-09 14:36:08 +02:00
Aliaksandr Kalenik
8d9920af16 LibWeb: Account for natural aspect ratio in calculate_min_content_height
By the time we calculate the min-content height, the width is already
known, so we can use it to calculate the height based on the natural
aspect ratio.
2025-07-08 22:35:04 +02:00
stelar7
7f2362643c LibWeb: Use a BFC for MathML
While this is not correct, it makes MathML text render atleast
2025-06-05 12:37:52 +01:00
Psychpsyo
a0be82b2cb LibWeb: Move containment checks to Layout::Node
It fits better there and avoids having to reach into the Element
all the time.
2025-05-13 15:30:14 +03:00
Andreas Kling
fbb3b06462 LibWeb: Don't assume table caption box wants to create a BFC
Instead, use the generic create_independent_formatting_context_if_needed
so that unusual situations like image-as-table-caption don't crash.

This logic clearly needs more work, but let's at least do better than
crashing. This gives us 26 new subtest passes on WPT.
2025-04-24 18:26:54 +02:00
Andreas Kling
f7a7cd9b2b LibWeb: Prevent infinite recursion on abspos w/ intrinsic max-size
We were incorrectly deciding that abspos elements shouldn't treat many
max-width and max-height values as `none`. My best understanding is that
this was a hack in 2023 for an issue that has been solved since then.

By removing the incorrect short-circuit, we stop at least one WPT test
from crashing due to infinite recursion and get ourselves +34 passes.
2025-04-24 18:26:54 +02:00
Jelle Raaijmakers
8257788a20 LibWeb: Ignore negative margins for margin box rect
Negative margins are processed through the `offset` in layout state, and
should not contribute to the margin box' rect's size or position.

Fixes #4249.
2025-04-11 02:34:28 +01:00
Jelle Raaijmakers
99df80f81e LibWeb: Only apply box offset if the box is not already the ancestor
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.
2025-04-04 15:35:26 +02:00
Aliaksandr Kalenik
fc45121b70 LibWeb: Size box as normal if it has aspect ratio but auto sizes
Otherwise we apply `calculate_stretch_fit_width()` instead of
calculating width based on the content inside the box, like other
browsers do.
2025-03-27 23:13:57 +00:00
Aliaksandr Kalenik
d8ff71fbb5 LibWeb: Delete parent state pointer in LayoutState
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.
2025-03-26 12:08:09 +00:00
Aliaksandr Kalenik
c49dd2036b LibWeb: Mark height as definite before doing inner layout of abspos
...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>
2025-03-21 14:46:31 -05: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
08c155cd3f LibWeb: Resolve "height: auto" using preferred aspect ratio if possible
Fixes https://github.com/LadybirdBrowser/ladybird/issues/2276
2025-03-15 13:51:16 +01:00
Aliaksandr Kalenik
a4463c45b9 LibWeb: Bring back cache of intrinsic sizes across layout runs
12c6ac78e2 with fixed mistake when cache
slot is copied instead of being referenced:
```cpp
auto cache =
    box.cached_intrinsic_sizes().min_content_height.ensure(width);
```
while it should've been:
```cpp
auto& cache =
    box.cached_intrinsic_sizes().min_content_height.ensure(width);
```
2025-03-08 14:52:33 +01:00
Andreas Kling
73a4b176cf Revert "LibWeb: Cache intrinsic sizes across layout runs"
This reverts commit 12c6ac78e2.

Very large performance regression when viewing GitHub repository pages.
2025-03-08 12:08:51 +01:00
Aliaksandr Kalenik
12c6ac78e2 LibWeb: Cache intrinsic sizes across layout runs
This change moves intrinsic sizes cache from
LayoutState, which is local to current layout run,
to layout nodes, so it could be reused between
layout runs. This optimization is possible because
we can guarantee that these measurements will
remain unchanged unless the style of the element
or any of its descendants changes.

For now, invalidation is implemented simply by
resetting cache on whole ancestors chain once we
figured that element needs layout update.
The case when layout is invalidated by DOM's
structural changes is covered by layout tree
invalidation that drops intrinsic sizes cache
along with layout nodes.

I measured improvement on couple websites:
- Mail list on GMail 28ms -> 6ms
- GitHub large code page 47ms -> 36ms
- Discord chat history 15ms -> 8ms
(Time does not include `commit()`)
2025-03-08 11:45:36 +01:00
Andreas Kling
16662ab230 LibWeb: Account for collapsed top margins in BFC root height calculation
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.
2025-02-22 18:41:42 +01:00
Psychpsyo
9b8120d8e8 Meta: Disallow links to single-page HTML spec 2025-02-05 16:04:50 -07:00
Andreas Kling
4f855286d7 LibWeb: Clamp layout content sizes to a max value instead of crashing
We've historically asserted that no "saturated" size values end up as
final metrics for boxes in layout. This always had a chance of producing
false positives, since you can trivially create extremely large boxes
with CSS.

The reason we had those assertions was to catch bugs in our own engine
code where we'd incorrectly end up with non-finite values in layout
algorithms. At this point, we've found and fixed all known bugs of that
nature, and what remains are a bunch of false positives on pages that
create very large scrollable areas, iframes etc.

So, let's change it! We now clamp content width and height of boxes to
17895700 pixels, apparently the same cap as Firefox uses.

There's also the issue of calc() being able to produce non-finite
values. Note that we don't clamp the result of calc() directly, but
instead just clamp values when assigning them to content sizes.

Fixes #645.
Fixes #1236.
Fixes #1249.
Fixes #1908.
Fixes #3057.
2025-02-05 18:28:55 +01:00
Andreas Kling
ad9d9bb684 LibWeb: Ensure size constraints apply to non-BFC-root abspos boxes
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. :^)
2025-01-30 19:31:07 +01:00
Psychpsyo
67ed676831 LibWeb: Implement CSS 'contain' property 2025-01-28 11:24:40 +00:00
Timothy Flynn
85b424464a AK+Everywhere: Rename verify_cast to as
Follow-up to fc20e61e72.
2025-01-21 11:34:06 -05:00
Aliaksandr Kalenik
bfdf52701f LibWeb: Treat min{max}-height as none if available is intrinsic [GFC]
Fixes infinite recursion in following cases:
- max-height: fit-content
- max-height: min-content
- max-height: max-content
2024-12-03 23:10:08 +01:00
Andreas Kling
a74954e145 LibWeb: Don't treat auto insets as definite for child of abspos element
This fixes an issue where the welcome screen wasn't showing up the first
time you visit https://puter.com/ :^)
2024-11-27 22:55:14 +01:00
Andreas Kling
d3ee49b092 LibWeb: Avoid an infinite recursion in replaced element sizing
In the case where we had a preferred aspect ratio and a natural height
but no natural width, we'd get into ping-ponging infinite recursion by
trying to find the width to resolve the height to resolve the width to
resolve the height...
2024-11-23 16:39:45 +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
Kostya Farber
a820308a02 LibWeb: Add layout objects for fieldset and legend
Add the boilerplate code for the layout objects that represent the
`<fieldset>` and `<legend>` elements. Using these, we can make progress
towards laying out these two elements per the spec at
https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements.
2024-11-19 14:31:03 +01:00
Aliaksandr Kalenik
a073e35562 LibWeb: Delete unused functions in FormattingContext 2024-11-11 20:20:39 +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
dd11d48a1d LibWeb: Use available space to resolve sizes in FFC
If available space is definite it should always match the size of the
containing block. Therefore, there is no need to do containing block
node lookup.
2024-11-10 19:14:54 +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