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.
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.
`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.
`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.
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.
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.
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.
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
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.
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).
`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.
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.
...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.
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.
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.
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.
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%.
This change introduces StyleInvalidator as a preparation for upcoming
change that will make `perform_pending_style_invalidations()` take care
of pending invalidation sets.
`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.
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.
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.
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.
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).