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.
We should not need to check if the result of a hit test is actually
visible for hit testing, because if it wasn't, it should not have been
returned from PaintableBox::hit_test() in the first place.
Making navigables responsible for backing store allocation will allow us
to have separate backing stores for iframes and run paint updates for
them independently, which is a step toward isolating them into separate
processes.
Another nice side effect is that now Skia backend context is ready by
the time backing stores are allocated, so we will be able to get rid of
BackingStore class in the upcoming changes and allocate PaintingSurface
directly.
It is confusing to have both URL::Host::public_suffix and
URL:get_public_suffix, both with slightly different semantics.
Instead, use PublicSuffixData for cases that just want a direct
match against the list, and URL::Host::public_suffix in LibWeb
land as the URL spec defined AO.
This commit implements the fallback to the documents fallback base url
if the href of the first base element is a data or javascript url.
Additionally the frozen base url is set, if a base element becomes the
first base element with an href content attribute because the previous
one got removed.
As part of the effort of removing the default constructor of
Origin, since document has the origin set after construction,
port Document's origin over to an Optional<Origin>.
This exposes that we were never setting the origin of the document
during fragment parsing. For now, to maintain previous behaviour,
let's explicitly set it to an opaque origin.
We were passing in byte offsets instead of UTF-16 code unit offsets,
which could lead to crashes if the offsets found exceeded the number of
code units in text fragments on the page.
Fixes#4908.
Co-authored-by: Tim Ledbetter <tim.ledbetter@ladybird.org>
Instead, collect a list of all the elements with content-visibility:auto
after layout.
This way we can skip the tree traversal when updating the rendering.
This was previously eating up ~300 µs of the 60fps frame budget on
our GitHub repo pages (and even more on large pages).
For example, button inputs shouldn't have a cursor
displayed in their text since they're not editable,
and are not meant to be editable.
Fixes#4140
Co-authored-by: Sam Atkins <sam@ladybird.org>
Make copies of the animation timeline list and animations to dispatch
events at before iterating over them. This ensures that they can't be
modified during iteration.
We move m_pending_nodes_for_style_invalidation_due_to_presence_of_has to
a local variable before iterating over it. This ensures that nothing can
be added to it while iterating.
When an element's ID is removed we only want to remove it from
`m_potentially_named_elements` if it is not also considered a
potentially named element due to it's name content attribute.
Which has an optmization if both size of the string being passed
through are FlyStrings, which actually ends up being the case
in some places during selector matching comparing attribute names.
Instead of maintaining more overloads of
Infra::is_ascii_case_insensitive_match, switch
everything over to equals_ignoring_ascii_case instead.
156c1083e9 introduced a text blocks cache
for better performance when searching through text on a page, but when
we partially recreate the layout tree, this cache does not get
invalidated. We now rebuild the entire text blocks cache after a layout
update.
These are not associated with a javascript realm, so to avoid
confusion about which realm these need to be created in, make
all of these objects a GC::Cell, and deal with the fallout.
Instead of indiscriminately clearing the cache for all anonymous boxes,
we now only clear it for those that were generated by a non-anonymous
box in need of layout update.
This increases the cache hit rate and allows us to avoid more work.
Before this change, we were going through the chain of base classes for
each IDL interface object and having them set the prototype to their
prototype.
Instead of doing that, reorder things so that we set the right prototype
immediately in Foo::initialize(), and then don't bother in all the base
class overrides.
This knocks off a ~1% profile item on Speedometer 3.
The basic idea is that style sheets can block script execution under
some circumstances. With this commit, we now handle the simplest cases
where a parser-inserted link element gets to download its style sheet
before script execution continues.
This improves performance on Speedometer 3 where JavaScript APIs that
depend on layout results (like Element.scrollIntoView()) would get
called too early (before the relevant CSS was downloaded), and so we'd
perform premature layout work. This work then had to be redone after
downloading the CSS anyway, wasting time.
Note that our Text/input/link-re-enable-crash.html test had to be
tweaked after these changes, since it relied on the old, incorrect,
behavior where scripts would run before downloading CSS.
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.
Corresponds to dba6e7b6c2
and 4c0401186c
The spec algorithms now use "the relevant global object's associated
document", so remove the concept of the History object itself having an
associated document. The spec has also combined the implementations for
forward/back/go so I've matched that too.
Corresponds to 03ab71775b
I've also split the `Document::has_focus()` method for clarity. Actually
implementing the "has focus steps" turns out to be quite involved so
I've left it for now.
We achieve this by keeping track of all checked pseudo class selectors
in the SelectorEngine code. We also give StyleComputer per-pseudo-class
rule caches.
These callbacks are evaluated synchronously via JS::Call. We do not need
to construct an expensive RootVector container just to immediately
invoke the callbacks.
Stylistically, this also helps indicate where the actual arguments start
at the call sites, by wrapping the arguments in braces.
Check if box has associated layout node is not mentioned in the spec,
but it is required to match behavior of other browsers that do not
invoke intersection observer steps for boxes without layout node.
With this change we save a copy of of scroll state at the time of
recording a display list, instead of actual ScrollState pointer that
could be modifed by the main thread while display list is beings
rasterized on the rendering thread, which leads to a frame painted with
inconsistent scroll state.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/4288
There's a bit of a UTF-8 assumption with this change. But nearly every
caller of these methods were immediately creating a String from the
resulting ByteString anyways.
This fixes the frame-ancestors WPT tests from crashing when an iframe
is blocked from loading. This is because it would get an undefined
location.href from the cross-origin iframe, which causes a crash as it
expects it to be there.
Allows us to avoid DOM node lookup whenever we need to query natural
size of a box during layout.
Makes 3-4% of `Box::preferred_aspect_ratio()` go away from profiles on
www.nyan.cat
With this change we maintain a data structure that maps ids to
corresponding elements. This allows us to avoid tree traversal in
getElementById() in all cases except ones when lookup happens for
unconnected elements.
All abspos boxes are expected to be blockified, so we are certain that
we can ignore non-box elements when collecting abspos nodes for layout.
Fixes a crash caused by an attempt to cast a BreakNode to a Box while
performing abspos layout.
The upcoming generated types will match those for pseudo-classes: A
PseudoElementSelector type, that then holds a PseudoElement enum
defining what it is. That enum will be at the top level in the Web::CSS
namespace.
In order to keep the diffs clearer, this commit renames and moves the
types, and then a following one will replace the handwritten enum with
a generated one.
Instead of bothering the GC heap with a bunch of DOMRect allocations,
we can just pass around CSSPixelRect internally in many cases.
Before this change, we were generating so much DOMRect garbage that
we had to do a garbage collection *every frame* on the Immich demo.
This was due to the large number of intersection observers checked.
We still need to relax way more when idle, but for comparison, before
this change, when doing nothing for 10 seconds on Immich, we'd spend
2.5 seconds updating intersection observers. After this change, we now
spend 600 ms.
This allows us to parse the Content-Security-Policy header and
Referrer-Policy header from navigation responses and actually allow
them to start having an effect.
This isn't actually necessary, since we already invalidate style for the
entire document, and the subsequent style update will discover any
additional layout invalidation needed as well.
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);
```
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()`)
This allows us to avoid a full layout tree rebuild after change of
"display" property, which happens frequently in practice. It also
allows us to avoid a full rebuild after DOM node insertion, since
previously, computing styles for newly inserted nodes would trigger a
complete layout tree rebuild.
All necessary invalidations are issued while invalidating animated
style. There is no need to drop display list simply because there are
some animations that might need an update.
We set the page's focused navigable upon mouse-down events from the UI.
However, we neglected to ever clear that focused navigable upon events
such as subsequent page navigations. This left the page with a stale
reference to a no-longer-active navigable. The effect was that any key
events from the UI would not be sent to the new page until either the
reference was collected by GC, or another mouse-down event occurred.
In the test added here, without this fix, the text sent to the input
element would not be received, and the change event would not fire.
This commit implements the main "render blocking" behavior for link
elements, drastically reducing the amount of FOUC (flash of unstyled
content) we subject our users to.
The document will now block rendering until linked style sheets
referenced by parser-created link elements have loaded (or failed).
Note that we don't yet extend the blocking period until "critical
subresources" such as imported style sheets have been downloaded
as well.
This change fixes a bug that can be reproduced with the following steps:
```js
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.location.href = ("http://localhost:8080/demo.html");
```
These steps are executed in the following order:
1. Create iframe and schedule session history traversal task that adds
session history entry for the iframe.
2. Generate navigation id for scheduled navigation to
`http://localhost:8080/demo.html`.
3. Execute the scheduled session history traversal task, which adds
session history entry for the iframe.
4. Ooops, navigation to `http://localhost:8080/demo.html` is aborted
because addings SHE for the iframe resets the navigation id.
This change fixes this by delaying all navigations until SHE for a
navigable is created.
Previously, they would stay open for the entire WebContent lifetime,
or until the server closed the connection. This was particularly
noticeable on collaborative websites/games such as
https://jigsawpuzzles.io/, where the user using Ladybird would stick
around even after they had navigated away.
The spec changes seem to mostly be about introducing a TrustedHTML type
which we do not yet support, so we have a couple of FIXMEs.
TrustedTypes::InjectionSink is an attempt at matching the spec, but it's
not entirely clear to me how it should work. I'm sure it'll get
revisited once we start implementing trusted types.
Our own Inspector differs from most other DevTools implementations with
regard to highlighting DOM nodes as you hover elements in the inspected
DOM tree. In other implementations, as you change the hovered node, the
browser will render a box model overlay onto the page for that node. We
currently don't do this; we wait until you click the node, at which
point we both paint the overlay and inspect the node's properties.
This patch does not change that behavior, but separates the IPCs and
internal tracking of inspected nodes to support the standard DevTools
behavior. So the DOM document now stores an inspected node and a
highlighted node. The former is used for features such as "$0" in the
JavaScript console, and the latter is used for the box model overlay.
Our Inspector continues to set these to the same node.
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:
CSS/ComputedProperties.h: 1113 -> 49
CSS/ComputedValues.h: 1120 -> 209
Analysis of selectors on modern websites shows that the `:hover`
pseudo-class is mostly used in the subject position within relatively
simple selectors like `.a:hover`. This suggests that we could greatly
benefit from segregating them by id/class/tag name, this way reducing
number of selectors tested during hover style invalidation.
With this change, hover invalidation on Discord goes down from 70ms to
3ms on my machine. I also tested GMail and GitHub where this change
shows nice 2x-3x speedup.
This is required to store Content Security Policies, as their
Directives are implemented as subclasses with overridden virtual
functions. Thus, they cannot be stored as generic Directive classes, as
it'll lose the ability to call overridden functions when they are
copied.
If selector does not have any descendant combinators then we know for
sure it won't be filtered out by ancestor filter, which means there is
no need to check for it.
This change makes hover style invalidation go faster on Discord where
with this change we spend 4-5% in `should_reject_with_ancestor_filter()`
instead of 20%.
Before this change, we did the following:
1. Created a bitmap with the matching state for each rule containing
`:hover`.
2. Changed the actively hovered element in the document.
3. Created another bitmap with the matching state for each rule
containing `:hover`.
With this change, we iterate rule by rule and compare the matching
state. This allows us to break early once we find the first rule whose
matching state changes after the hovered element update. Additionally,
this removes the need to allocate a bitmap.
Instead of using `has_pseudo_elements()` that iterates over all pseudo
elements, only check if `::before` or `::after` are present.
Before this change, `has_pseudo_elements()` was 10% of profiles on
Discord while now it's 1-2%.