Commit graph

90 commits

Author SHA1 Message Date
Andreas Kling
545ed08519 LibWeb: Add debug logging of reasons for layout tree update
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, 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-04-21 00:31:08 +02:00
Andreas Kling
3c15fec303 LibWeb: Move "needs layout update" flag from DOM to layout tree
This is in preparation for allowing anonymous boxes to retain their
intrinsic size cache across layouts.
2025-04-21 00:31:08 +02:00
Andreas Kling
aecb144df0 LibWeb: Add more fast_is<T> helpers for DOM::Node subclasses 2025-04-18 14:45:56 +02:00
Shannon Booth
3e17b1c9ae LibWeb: Make Node::parent_element return GC::Ptr
This is useful for people like myself who run with debug mode to
more reliably get stacktraces without spinning up a debugger.
2025-04-18 10:49:02 +02:00
Andreas Kling
8d51c41a42 LibWeb: Remove no-longer-used StyleInvalidationReasons :^) 2025-04-17 19:45:55 +02:00
Andreas Kling
2aa6d7636c LibWeb: Invalidate sheet owners after mutating cssText of its rules
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, 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 fixes one source of flakiness on WPT (of many) where we wouldn't
recompute style after programmatically altering the contents of a style
sheet, but instead had to wait for something else to cause invalidation.
2025-04-12 14:15:57 +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
Timothy Flynn
2c4b420acc LibWeb+LibWebView+WebContent: Inform the UI about DOM mutations
This will allow our DevTools server to inform the Firefox DevTools
client about DOM mutations.
2025-03-08 01:25:55 +01:00
Andreas Kling
6606eecce5 LibWeb: Invalidate style (and rule cache) on MediaList changes
This makes dynamic changes to a style sheet's media attribute actually
take effect immediately.
2025-03-04 19:07:40 +01:00
Aliaksandr Kalenik
b92a8553c7 LibWeb: Cancel animations when element is moved in display none subtree
We already have logic to play or cancel animations in an element's
subtree when the display property changes to or from none. However,
this was not sufficient to cover the case when an element starts/stops
being nested in display none after insertion.
2025-03-04 18:06:46 +01:00
Sam Atkins
f148af0a93 LibWeb: Move XMLSerializer into HTML directory
The DOMParsing spec is in the process of being merged into the HTML one,
gradually. The linked spec change moves XMLSerializer, but many of the
algorithms are still in the DOMParsing spec so I've left the links to
those alone.

I've done my best to update the GN build but since I'm not actually
using it, I might have done that wrong.

Corresponds to 2edb8cc7ee
2025-03-04 16:44:41 +00:00
Sam Atkins
0fcd7f9aea LibWeb/DOM: Update an attribute's node document
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, 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
Corresponds to b64559cc08
2025-02-23 22:36:42 +00:00
Tim Ledbetter
a9ffc6359a LibWeb: Set inertness of HTMLElement when inert attribute is changed 2025-02-21 12:41:57 +00:00
Timothy Flynn
82206d2abb LibWeb: Move DOM::NodeType to its own file
This is to allow using these values in libraries that otherwise do not
need to depend on LibWeb.
2025-02-19 08:45:51 -05:00
Aliaksandr Kalenik
327dc8e82a LibWeb: Avoid full tree traversal for non-subject :has() invalidation
Instead of checking all elements in a document for containment in
`:has()` invalidation set, we could narrow this down to ancestors and
ancestor siblings, like we already do for subject `:has()` invalidation.

This change brings great improvement on GitHub that has selectors with
non-subject `:has()` and sibling combinators (e.g., `.a:has(.b) ~ .c`)
which prior to this change meant style invalidation for whole document.
2025-02-13 16:24:51 +01:00
Aliaksandr Kalenik
761e9aeaf7 LibWeb: Optimize inherited style update
This commit changes the strategy for updating inherited styles. Instead
of marking all potentially affected nodes during style invalidation, the
decision is now made on-the-fly during style recalculation. Child nodes
will only have their inherited styles recalculated if their parent's
properties have changed.

On Discord this allows to 1000x reduce number of nodes with recalculated
inherited style.
2025-02-11 19:23:12 +01:00
Sam Atkins
dc58f6567f LibWeb: Support :open for file and color <input> elements 2025-02-10 13:57:36 +00:00
Aliaksandr Kalenik
e677ab1699 LibWeb: Narrow :has() style invalidation to ancestor nodes
The current implementation of `:has()` style invalidation is divided
into two cases:
- When used in subject position (e.g., `.a:has(.b)`).
- When in a non-subject position (e.g., `.a > .b:has(.c)`).

This change focuses on improving the first case. For non-subject usage,
we still perform a full tree traversal and invalidate all elements
affected by the `:has()` pseudo-class invalidation set.

We already optimize subject `:has()` invalidations by limiting
invalidated elements to ones that were tested against `has()` selectors
during selector matching. However, selectors like `div:has(.a)`
currently cause every div element in the document to be invalidated.
By modifying the invalidation traversal to consider only ancestor nodes
(and, optionally, their siblings), we can drastically reduce the number
of invalidated elements for broad selectors like the example above.

On Discord, when scrolling through message history, this change allows
to reduce number of invalidated elements from ~1k to ~5.
2025-02-10 01:13:53 +01:00
Aliaksandr Kalenik
f7a3f785a8 LibWeb: Don't invalidate style of parent of inserted node
There is no need for this invalidation because taking care of siblings
is already done by invalidation with `NodeInsertBefore` reason. Parent
element itself (without subtree) is always invalidated by
`Node::children_changed()` hook, so `:empty` pseudo-class invalidation
is already covered.
2025-02-06 20:07:11 +01:00
Aliaksandr Kalenik
dfcee2bbdf LibWeb/DOM: Inherit Node from TreeNode
This allows to delete lots of tree helper functions duplicated between
Node and TreeNode.
2025-02-03 18:36:57 +01:00
Shannon Booth
903c8860f8 LibWeb: Add metadata to children update steps invocation
Currently, this metadata is only provided on the insertion steps,
though I believe it would be useful to extend to the other cases
as well. This metadata can aid in making optimizations for these
steps by providing extra context into the type of change which
was made on the child.
2025-01-30 13:55:40 -07:00
Aliaksandr Kalenik
7da3b06e3e LibWeb: Postpone invalidating style of elements affected by :has()
...until Document::update_style(). This allows to avoid doing full
document DOM tree traversal on each Node::invalidate_style() call.

Fixes performance regression on wpt.fyi
2025-01-29 17:21:47 +01:00
Aliaksandr Kalenik
d762d16938 LibWeb: Use invalidation sets for :has() invalidation
Prior to this change, we invalidated all elements in the document if it
used any selectors with :has(). This change aims to improve that by
applying a combination of techniques:
- Collect metadata for each element if it was matched against a selector
  with :has() in the subject position. This is needed to invalidate all
  elements that could be affected by selectors like `div:has(.a:empty)`
  because they are not covered by the invalidation sets.
- Use invalidation sets to invalidate elements that are affected by
  selectors with :has() in a non-subject position.

Selectors like `.a:has(.b) + .c` still cause whole-document invalidation
because invalidation sets cover only descendants, not siblings. As a
result, there is no performance improvement on github.com due to this
limitation. However, youtube.com and discord.com benefit from this
change.
2025-01-29 09:30:18 +01:00
Aliaksandr Kalenik
d79bb1aac2 LibWeb: Fix underinvalidation when inline style has custom properties
We have an optimization that allows us to invalidate only the style of
the element itself and mark descendants for inherited properties update
when the "style" attribute changes (unless there are any CSS rules that
use the "style" attribute, then we also invalidate all descendants that
might be affected by those rules). This optimization was not taking into
account that when the inline style has custom properties, we also need
to invalidate all descendants whose style might be affected by them.

This change fixes this bug by saving a flag in Element that indicates
whether its style depends on any custom properties and then invalidating
all descendants with this flag set when the "style" attribute changes.
Unlike font relative lengths invalidation, for elements that depend on
custom properties, we need to actually recompute the style, instead of
individual properties, because values without expanded custom properties
are gone after cascading, and it has to be done again.

The test added for this change is a version of an existing test we had
restructured such that it doesn't trigger aggressive style invalidation
caused by DOM structured changes until the last moment when test results
are printed.
2025-01-28 11:38:06 +00:00
Shannon Booth
4f80c7a5f3 LibWeb/DOM: Use as_if for node of type checks in iteration helpers
These are very hot functions in profiles, so let's avoid a potential
double dynamic_cast or virtual call. For consistency, port all of
these classes of function over to 'as_if' instead.
2025-01-27 00:10:14 +00:00
Andreas Kling
f35152cf61 LibWeb: Defer entire-subtree style invalidations
Instead of traversing the entire DOM subtrees and marking nodes for
style update, this patch adds a new mechanism where we can mark a
subtree root as "entire subtree needs style update".

A new pass in Document::update_style() then takes care of coalescing
all these invalidations in a single traversal of the DOM.

This shaves *minutes* of loading time off of https://wpt.fyi/ subpages.
2025-01-26 22:58:42 +01:00
Andreas Kling
7269fc3e52 LibWeb: Pass old parent's root to Node::removed_from()
This will allow nodes to access the root they've just been removed from.
2025-01-23 21:38:31 +01: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
e0051db62e LibWeb: Use invalidation sets for "style" attribute invalidation 2025-01-20 18:23:34 +01:00
Aliaksandr Kalenik
c5f2a88f69 LibWeb: Use invalidation sets to reduce style recalculation
Implements idea described in
https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8

Invalidation sets are used to reduce the number of elements marked for
style recalculation by collecting metadata from style rules about the
dependencies between properties that could affect an element’s style.

Currently, this optimization is only applied to style invalidation
triggered by class list mutations on an element.
2025-01-19 19:54:38 +01:00
Andreas Kling
c01d810e5a LibWeb: Implement partial layout tree updates
DOM nodes now have two additional flags:

- Needs layout tree update
- Child needs layout tree update

These work similarly to the needs-style-update flags, but instead signal
the need to rebuild the corresponding part of the layout tree.

When a specific DOM node needs a layout tree update, we try to create
a new subtree starting at that node, and then replace the subtree in the
old layout tree with the newly created subtree.

This required some refactoring in TreeBuilder so that we can skip over
entire subtrees during a tree update.

Note that no partial updates happen yet (as of this commit) since we
always invalidate the full layout tree still. That will change in the
next commit.
2025-01-18 21:01:01 +01:00
Tim Ledbetter
a467005855 LibWeb: Make node cloning methods const 2025-01-11 23:10:09 +01:00
Sam Atkins
f11347739a LibWeb/HTML: Stop observing lazy-loaded iframes when navigating 2025-01-11 11:10:43 +01:00
Jelle Raaijmakers
d08febcf67 LibWeb: Add Node::for_each(_inclusive)_ancestor()
Allows for easy iteration over the chain of ancestors for a node.
2025-01-10 23:33:35 +01:00
sideshowbarker
173368bd5e LibWeb: Allow accessible-name computation to skip role-attribute lookup
Per https://w3c.github.io/aria/#document-handling_author-errors_roles,
determining whether to ignore certain specified landmark roles requires
first determining whether the element for which the role is specified
has an accessible name.

But if we then try to retrieve a role for such elements, we end up
calling right back into the accessible-name computation code — which
would cause the calls to loop infinitely.

So to avoid that — and to have handling for any other future cases the
spec may introduce of such recursive calls that will loop indefinitely —
this change introduces a parameter that callers can pass to cause
role-attribute lookup to be skipped during accessible-name computation.
2025-01-09 14:08:23 +00:00
Andreas Kling
49a7a0f378 LibWeb: Avoid invalidation on .textContent setter no-op
When setting the textContent of an element with no children to null or
the empty string, nothing happens. Even so, we were still invalidating
style, layout and collections, causing pointless churn.

Skipping invalidation in this case also revealed that we were missing
invalidation when changing the selected state of HTMLOptionElement.
This was all caught by existing tests already in-tree. :^)
2025-01-05 13:36:17 +01:00
Sam Atkins
c60ad5b0b8 LibWeb/DOM: Update node cloning to latest spec
Main difference is that a chunk of the "clone a node" steps are pulled
out into a "clone a single node" algorithm.

Reflects these spec PRs:
https://github.com/whatwg/dom/pull/1332
https://github.com/whatwg/dom/pull/1334

Though this code is quite old so there may also be older spec changes
included here.
2025-01-04 12:14:25 +00:00
Andreas Kling
b981e6f7bc LibWeb: Avoid many style invalidations on DOM attribute mutation
Many times, attribute mutation doesn't necessitate a full style
invalidation on the element. However, the conditions are pretty
elaborate, so this first version has a lot of false positives.

We only need to invalidate style when any of these things apply:

1. The change may affect the match state of a selector somewhere.
2. The change may affect presentational hints applied to the element.

For (1) in this first version, we have a fixed list of attribute names
that may affect selectors. We also collect all names referenced by
attribute selectors anywhere in the document.

For (2), we add a new Element::is_presentational_hint() virtual that
tells us whether a given attribute name is a presentational hint.

This drastically reduces style work on many websites. As an example,
https://cnn.com/ is once again browseable.
2024-12-24 17:17:09 +01:00
Andreas Kling
dc8343cc23 LibWeb: Add mechanism to invalidate only inherited styles
We can now mark an element as needing an "inherited style update" rather
than a full "style update". This effectively means that the next style
update will visit the element and pull all of its inherited properties
from the relevant ancestor element.

This is now used for descendants of elements with animated style.
2024-12-23 17:05:09 +01:00
Jelle Raaijmakers
c357fbf036 LibWeb: Remove unused (Tree)Node::index_of_child() 2024-12-18 23:46:22 +00:00
Jelle Raaijmakers
1c55153d43 LibWeb: Refactor "editable" and "editing host" concepts
The DOM spec defines what it means for an element to be an "editing
host", and the Editing spec does the same for the "editable" concept.
Replace our `Node::is_editable()` implementation with these
spec-compliant algorithms.

An editing host is an element that has the properties to make its
contents effectively editable. Editable elements are descendants of an
editing host. Concepts like the inheritable contenteditable attribute
are propagated through the editable algorithm.
2024-12-10 14:54:19 +01:00
Shannon Booth
02efb64e64 LibWeb/DOM: Implement the DOM post connection steps
See: https://github.com/whatwg/dom/commit/0616094
2024-12-10 10:38:56 +00:00
Andreas Kling
ab0dc83d28 LibWeb: Make Node.normalize() ignore CDATASection nodes
We hadn't modeled the "exclusive text node" concept correctly.
2024-11-20 16:10:57 +01:00
Andreas Kling
6ffc7ea36d LibWeb: Make Node::is_text() return true for CDATASection nodes
CDATASection inherits from Text, and so it was incorrect for them to
claim not to be Text nodes.

This fixes at least two WPT subtests. :^)

It also exposed a bug in the DOM Parsing and Serialization spec,
where we're not told how to serialize CDATASection nodes.

Spec bug: https://github.com/w3c/DOM-Parsing/issues/38
2024-11-19 19:24:37 +00:00
Shannon Booth
f87041bf3a LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:

 * JS::NonnullGCPtr -> GC::Ref
 * JS::GCPtr -> GC::Ptr
 * JS::HeapFunction -> GC::Function
 * JS::CellImpl -> GC::Cell
 * JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
sideshowbarker
55b19c3177 LibWeb: Remove unused append_with_space etc functions from DOM::Node
This change removes the append_without_space, append_with_space,
prepend_without_space, and prepend_with_space functions from DOM::Node.

All those methods were added with the initial “Implement Accessible Name
and Description Calculation” commit in da5c918 and were only used in the
code related to accessible-name computation. But subsequent changes to
that code have removed all the calls to those functions — so now they’re
all completely unused.
2024-11-11 14:56:46 -07:00
sideshowbarker
dfd50afa4e LibWeb: Add an alternative_text() getter
This change adds an alternative_text()·getter, for use in computing
accessible names.
2024-11-11 14:56:46 -07:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Renamed from Userland/Libraries/LibWeb/DOM/Node.h (Browse further)