Commit graph

884 commits

Author SHA1 Message Date
Aliaksandr Kalenik
d1fbb7b51e LibWeb: Invalidate less elements affected by CSS custom properties
Before this change, whenever element's attributes changed, we would add
a flag to "pending invalidation", indicating that all descendants whose
style uses CSS custom properties needed to be recomputed. This resulted
in severe overinvalidation, because we would run invalidation regardless
of whether any custom property on affected element actually changed.

This change takes another approach, and now we decide whether
descendant's style needs to be recomputed based on whether ancestor's
style recomputation results in a change of custom properties, though
this approach adds a little overhead to style computation as now we have
to compare old vs new hashmap of custom properties.

This brings substantial improvement on discord and x.com where, before
this change, advantage of using invalidation sets was lost and we had
to recompute all descendants, because almost all of them use custom
properties.
2025-07-30 11:06:05 +02:00
Aliaksandr Kalenik
4cbf47dcd2 LibWeb: Unregister ResizeObserver from Document when it has no targets
According to the spec, `ResizeObserver` needs to live for as long as
it's referenced from script or has observation targets. With this change
we make sure that `ResizeObserver` is unregistered from the `Document`
when it has no target.

Fixes GC leak that caused us to keep all resize observers alive until
document they belong to is destroyed.
2025-07-30 00:54:57 +02:00
Aliaksandr Kalenik
40fd2643cc LibWeb: Don't visit DocumentShadowRootList from Document
`ShadowRoot` register itself in Document` from constructor and
unregister itself from `finalize()`. The problem is that `finalize()`
won't be invoked for as long as `ShadowRoot` is visited by
`Document`, leading to GC leaks.
2025-07-30 00:54:57 +02:00
Aliaksandr Kalenik
52b4f2a40a LibWeb: Don't visit registered document observers from Document
`DocumentObserver` register itself in Document` from constructor and
unregister itself from `finalize()`. The problem is that `finalize()`
won't be invoked for as long as `DocumentObserver` is visited by
`Document`. By not visiting registered observers from `Document` we
move this responsibility to object that allocated observer, which is
always exactly what we want, e.g. once `SVGUseElement` that uses
observer is gone, observer won't be visited anymore which will lead to
`finalize()` being called.
2025-07-30 00:54:57 +02:00
Andreas Kling
81d4079c12 LibWeb: Support CSS content property images (and lists, too!)
This patch expands our generated content support beyond single strings
to lists of strings and/or images.

Pseudo-elements like ::before and ::after can now use content:url(...)
to insert anonymous image boxes into the layout tree.

This is heavily used in Google Docs for UI elements.
2025-07-28 22:46:27 +02:00
Timothy Flynn
5c561c1a53 LibWeb: Port node text content to UTF-16 2025-07-28 18:30:50 +02:00
Timothy Flynn
c8888609f4 LibWeb: Port the FormAssociatedElement value to UTF-16
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
This porting effort makes it pretty clear we will want a UTF-16-aware
GenericLexer. But for now, we can actually make ASCII assumptions about
what we are parsing, and act accordingly.
2025-07-28 12:25:11 +02:00
Timothy Flynn
e7b08cf291 LibWeb: Add a DOM::Element::set_attribute override for UTF-16 strings
This just transcodes to UTF-8 for now, but primarily serves to to keep
compatibility with generated IDL definitions.
2025-07-28 12:25:11 +02:00
Aliaksandr Kalenik
789016841e LibWeb: Pass device_pixels_per_css_pixel into DisplayList constructor
Having a setter for `device_pixels_per_css_pixel` was confusing because
display lists are immutable, so it doesn't make sense to override this
value after the display list has been created.
2025-07-27 10:20:18 +02:00
Aliaksandr Kalenik
8569124b87 LibWeb: Fix scroll state refresh in cached display list for iframes
6507d23 introduced a bug when snapshot for iframe is saved in
`PaintNestedDisplayList` and, since display lists are immutable, it's
not possible to update before the next repaint.

This change fixes the issue by moving `ScrollStateSnapshot` for
nested display lists from `PaintNestedDisplayList` to
`HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>` that is
placed into pending rendering task, making it possible to update
snapshots for all display lists before the next repaint.

This change doesn't have a test because it's really hard to make a ref
test that will specifically check scenario when scroll offset of an
iframe is advanced after display list is cached. We already have
`Tests/LibWeb/Ref/input/scroll-iframe.html` but unfortunately it did
not catch this bug.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/5486
2025-07-26 11:53:21 -04:00
Timothy Flynn
2da615ed31 LibWeb: Port document.execCommand and InputEvent to UTF-16 2025-07-26 00:40:06 +02:00
Timothy Flynn
017a6cc687 LibWeb+WebContent: Port FormAssociatedTextControlElement APIs to UTF-16 2025-07-26 00:40:06 +02:00
Timothy Flynn
c566cc5024 LibWeb: Invoke correct word-jumping method in EditingHostManager
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
2025-07-25 12:57:03 -04:00
Timothy Flynn
97548f48de LibWeb: Port rendered text to UTF-16
This migrates TextNode::text_for_rendering() to Utf16String and deals
with the fallout. As a consequence, this effectively reverts commit
3df83dade8.

The layout test expecation file updates are because we now express text
lengths and offsets in UTF-16 code units.

The selection-over-multiple-code-units expectation file update actually
represents a correctness fix. Our result now matches Firefox.
2025-07-25 18:16:22 +02:00
Timothy Flynn
a270706411 LibWeb: Port DOM::Range stringification to UTF-16 2025-07-25 18:16:22 +02:00
Tim Ledbetter
89fb783b42 LibWeb: Throw pre insertion validity errors from the correct global 2025-07-25 09:08:14 +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
Jelle Raaijmakers
e029e785d2 LibWeb: Convert Editing API internals to UTF-16
Both sides of the Editing internals now have to deal with some awkward
converting between UTF-8 and UTF-16, but the upside is that it
immediately exposed an issue with the `insertText` command: instead of
dealing with code units, it was iterating over code points causing the
selection to be updated only once instead of twice. This resulted in the
final selection potentially ending up in between a surrogate pair.

Fixes #5547 (pasting/typing surrogate pairs).
2025-07-24 07:18:25 -04:00
Aliaksandr Kalenik
c8b574e9b4 LibWeb: Remove animations from timeline in Element::removed_from()
`AnimationTimeline` visits pointers of all registered animations, so if
element is removed from DOM tree but its animations remain registered in
timeline, then `Animation` and owner `Element` will be kept alive until
`AnimationTimeline` is destroyed.
2025-07-23 17:50:01 -04:00
Tim Ledbetter
a990de65e4 LibWeb: Implement SVGViewElement
This identifies a particular region of an SVG image, which can then be
linked to by an SVG fragment identifier.
2025-07-22 00:52:24 +02:00
Gingeh
55129644d5 LibWeb: Don't crash when root element becomes a popover or fullscreen 2025-07-20 08:54:53 +02:00
Andreas Kling
92221f0c57 LibWeb: Apply all animations and transitions before invalidating style
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
This fixes an issue where only the last KeyframeEffect applied to an
element would actually have an effect on the computed properties.

It was particularly noticeable when animating a shorthand property like
border-width, since only one of the border edges would have its width
actually animate.

By deferring the invalidation until all animations have been processed,
we also reduce the amount of work that gets done on pages with many
animations/transitions per element. Discord is very fond of this for
example.
2025-07-19 11:08:46 -04:00
Andrew Kaster
f9f854b493 LibWeb: Preserve comments in XML documents 2025-07-19 14:56:20 +02:00
Aliaksandr Kalenik
2fc405f1b2 LibWeb: Allow to pass pseudo-element type in computed properties getter
...and setter. We had lots of places where we check if pseudo-element
type is specified and then use `pseudo_element_computed_properties()` or
`computed_properties()`. This change moves these checks from caller side
to the getter and setter.
2025-07-18 21:19:37 +02:00
Timothy Flynn
9582895759 AK+LibJS+LibWeb+LibRegex: Replace AK::Utf16Data with AK::Utf16String 2025-07-18 12:45:38 -04:00
Timothy Flynn
2803d66d87 AK: Support UTF-16 string formatting
The underlying storage used during string formatting is StringBuilder.
To support UTF-16 strings, this patch allows callers to specify a mode
during StringBuilder construction. The default mode is UTF-8, for which
StringBuilder remains unchanged.

In UTF-16 mode, we treat the StringBuilder's internal ByteBuffer as a
series of u16 code units. Appending a single character will append 2
bytes for that character (cast to a char16_t). Appending a StringView
will transcode the string to UTF-16.

Utf16String also gains the same memory optimization that we added for
String, where we hand-off the underlying buffer to Utf16String to avoid
having to re-allocate.

In the future, we may want to further optimize for ASCII strings. For
example, we could defer committing to the u16-esque storage until we
see a non-ASCII code point.
2025-07-18 12:45:38 -04:00
Timothy Flynn
fe676585f5 AK: Add a UTF-16 string with optimized short- and ASCII-string storage
This is a strictly UTF-16 string with some optimizations for ASCII.

* If created from a short UTF-8 or UTF-16 string that is also ASCII,
  then the string is stored in an inlined byte buffer.

* If created with a long UTF-8 or UTF-16 string that is also ASCII,
  then the string is stored in an outlined char buffer.

* If created with a short or long UTF-8 or UTF-16 string that is not
  ASCII, then the string is stored in an outlined char16 buffer.

We do not store short non-ASCII text in the inlined buffer to avoid
confusion with operations such as `length_in_code_units` and
`code_unit_at`. For example, "😀" would be stored as 4 UTF-8 bytes
in short string form. But we still want `length_in_code_units` to
be 2, and `code_unit_at(0)` to be 0xD83D.
2025-07-18 12:45:38 -04:00
Timothy Flynn
7fad8c333d LibWeb: Use forward-declarations of structured serialized types
This reduces the rebuilt targets when touching StructuredSerialize.h
from ~1200 to ~400. The remaining are due to generated IPC headers.
2025-07-18 10:09:02 -04:00
norbiros
7ad01d28a8 LibWeb/CSS: Add basic registered properties with initial values
Add global registry for registered properties and partial support
for `@property` rule. Enables registering properties with initial
values. Also adds basic retrieval via `var()`.

Note: This is not a complete `@property` implementation.
2025-07-18 11:12:39 +01:00
Luke Wilde
8b0b3b186f LibWeb/CSP: Implement the style-src directive 2025-07-18 11:58:04 +12:00
Aliaksandr Kalenik
1bf4d3391e LibWeb: Use GC::Ptr for BrowsingContext pointer saved in Document
Likely we forgot to update `WeakPtr` to `GC::Ptr` after converting
`BrowsingContext` to GC-allocated object.
2025-07-17 15:55:30 +01:00
Sam Atkins
b7b504cf2d LibWeb: Remove XML-derived attribute name validation from DOMStringMap
Corresponds to 841fcfdb2c
2025-07-17 13:54:01 +02:00
Aliaksandr Kalenik
aa31b69e7e LibWeb+WebContent: Delete unused page_did_layout() 2025-07-17 08:24:19 +02:00
Aliaksandr Kalenik
b0ef7f4427 LibWeb: Defer invalidation sets processing until update_style()
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
This change converts `Node::invalidate_style()` (invalidation sets
overload) from eagerly doing tree traversal that marks elements affected
by invalidation set to instead adding "pending invalidation sets" into
`StyleInvalidator`, processing of which is deferred until the next
`update_style()`. By doing that we sometimes substantially reduce amount
of work done performing tree traversal that marks elements for style
recalculation.

Improves performance on Discord, were according to my measurements we
were previously spending 20% of time in style invalidation, but now it's
down to <1%.
2025-07-17 00:43:26 +02:00
Aliaksandr Kalenik
8cbe27b2f9 LibWeb: Move perform_pending_style_invalidations() in StyleInvalidator
This change introduces StyleInvalidator as a preparation for upcoming
change that will make `perform_pending_style_invalidations()` take care
of pending invalidation sets.
2025-07-17 00:43:26 +02:00
Aliaksandr Kalenik
9a5ef95022 LibWeb: Delete unused m_animation_driver_timer in Document
Some checks failed
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Build Dev Container Image / build (push) Has been cancelled
2025-07-16 22:09:46 +02:00
Sam Atkins
9bc5c6fb35 LibWeb/DOM: Add document getter to AbstractElement 2025-07-16 14:47:45 +01:00
Callum Law
6a9c8d7767 LibWeb: Don't resolve colors with unresolved components
`CSSColorValue`s which have unresolved `calc` components should be able
to be resolved. Previously we would always resolve them but with
incorrect values.

This is useful as we will now be able to now whether we should serialize
colors in their normalized form or not.

Slight regression in that we now serialize (RGB, HSL and HWB) colors
with components that rely on compute-time information as an empty
string, but that will be fixed in the next commit.
2025-07-16 13:05:33 +01:00
Aliaksandr Kalenik
6ddc582551 LibWeb: Delete unreachable code in Node::invalidate_style()
`if (invalidation_set.needs_invalidate_whole_subtree())` branch in the
end of the function was always false.
2025-07-15 18:31:43 -04:00
Sam Atkins
632ce9523b LibWeb/CSS: Add :unchecked pseudo-class
This just got added to the Selectors spec:

b78c97c19d

It's thus missing from the HTML spec and WPT, but I figured it was
simple enough to add.
2025-07-15 21:27:44 +02:00
Sam Atkins
b973c8d275 LibWeb/CSS: Remove the :target-within pseudo-class
This has been removed from the spec:

03b340c34f
2025-07-15 21:27:44 +02:00
Aliaksandr Kalenik
8142c311ec LibWeb: Reset document's cursor blink timer after selection change
For contenteditable `Selection` in collapsed state represents cursor
position, so whenever it's update, we should also reset the blink timer.

Fixes bug on Discord when cursor is blinking while it is being moved by
pressing arrow keys.
2025-07-15 17:25:02 +01:00
Aliaksandr Kalenik
9b9acf0bd5 LibWeb: Delete schedule_style_update() and schedule_layout_update()
Both functions schedule HTML event loop processing but that's
unnecessary, because we schedule a rendering task that checks if
style/layout needs an update 60/s anyway.
2025-07-15 16:57:35 +02:00
Shannon Booth
15657ec4c3 LibWeb: Include or declare missing definitions for PseudoElement.h
clangd was loudly complaining about this file.
2025-07-15 13:54:17 +01:00
Aliaksandr Kalenik
9bb5e70f5a LibWeb: Skip tree traversal if invalidation set doesn't have properties
This allows us to skip unnecessary tree traversal to mark style for
recalculation when invalidation set only has `InvalidateSelf`.
2025-07-14 09:32:05 +02:00
Aliaksandr Kalenik
8ae7417445 LibWeb: Add internals call to dump display list
It's useful to have tests that dump display list items, so we can more
easily see how changes to the display list recording process affect the
output. Even the small sample test added in this commit shows that we
currently record an unnecessary AddClipRect item for empty paint phases.

For now, the dump doesn't include every single property of an item, but
we can shape it to include more useful information as we iterate on it.
2025-07-13 19:15:05 +02:00
Aliaksandr Kalenik
410e82c9fd LibWeb: Rearrange code such that a lot less files include Command.h
Some checks failed
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Build Dev Container Image / build (push) Has been cancelled
With this change number of recompiled files after modification of
`Command.h` goes down from >1000 to <100.
2025-07-11 17:37:27 +02:00
Callum Law
d980321a77 LibWeb: Include own shadow root in for_each_shadow_including_descendant
Fixes a crash in wpt.live/css/cssom/CSSStyleSheet-constructable.html
2025-07-11 16:21:11 +02:00
Jelle Raaijmakers
2998049fe9 LibWeb: Implement the unreachable scrollable overflow
Whenever we end up with a scrollable overflow rect that goes beyond
either of its axes (i.e. the rect has a negative X or Y position
relative to its parent's absolute padding box position), we need to clip
that rect to prevent going into the "unreachable scrollable overflow".

This fixes the horizontal scrolling on https://ladybird.org (gets more
pronounced if you make the window very narrow).
2025-07-11 08:23:46 +02:00
Shannon Booth
05ddf5c718 LibWeb/DOM: Add StorageEvent and DragEvent to Document.createEvent
We have since implemented these interfaces.
2025-07-11 08:22:36 +02:00