Commit Graph

229 Commits

Author SHA1 Message Date
Andreas Kling
cd364ea375 LibWeb: Don't require layout to toggle cursor blink state
The cursor blink timer fires every 500ms and only needs to toggle
the blink state and mark the paintable as needing display. If the
paintable doesn't exist yet, we can simply skip the blink -- the
cursor will appear after the next natural rendering update.

This avoids a potentially expensive synchronous layout every 500ms
for what is a purely cosmetic operation.
2026-02-22 13:24:05 +01:00
Andreas Kling
3a2de5374c LibWeb: Don't require layout to determine clientLeft and clientTop
These properties only return the computed border width, which is a
style-level value that doesn't depend on layout geometry. Replace
the full update_layout() call with update_style_if_needed_for_element()
and resolve border widths directly from computed properties.

This avoids potentially expensive synchronous layout when only
CSS computed values are needed.

Very profitable on https://x.com/ where we avoid lots of layout work.
2026-02-22 13:24:05 +01:00
Andreas Kling
52eefc3714 LibWeb: Remove unused CanvasSetFillStyle layout reason 2026-02-22 12:43:01 +01:00
Andreas Kling
20cc055ec9 LibWeb: Don't require layout node when setting canvas 2D strokeStyle
Use update_style_if_needed_for_element() and resolve colors via
computed properties instead of forcing a full layout update.
2026-02-22 12:43:01 +01:00
Andreas Kling
c7bf2021bc LibWeb: Don't require layout node when setting canvas 2D fillStyle
Use update_style_if_needed_for_element() and resolve colors via
computed properties instead of forcing a full layout update.
2026-02-22 12:43:01 +01:00
Andreas Kling
ca72156497 LibWeb: Don't require layout node when setting canvas 2D shadowColor
Use update_style_if_needed_for_element() and resolve colors via
computed properties instead of forcing a full layout update.
2026-02-22 12:43:01 +01:00
Andreas Kling
cbf8b70d42 LibWeb: Don't require layout node when setting canvas 2D filter
Setting the filter property on a CanvasRenderingContext2D would crash
with a null pointer dereference if the canvas element had no layout
node (e.g. a detached canvas not in the document).

Instead of forcing a full layout update and requiring a layout node,
we now only update style if needed and resolve lengths via the
element's computed properties when available, falling back to
document-level defaults otherwise. This matches the pattern used by
CanvasTextDrawingStyles.
2026-02-22 12:43:01 +01:00
Andreas Kling
ef6368924e LibWeb: Add Document::update_style_if_needed_for_element()
This is a targeted version of update_style() that only performs a
style update if the given element (or its ancestors) actually have
dirty style. Useful when we only need up-to-date computed properties
for a specific element.
2026-02-22 12:43:01 +01:00
Shannon Booth
d6d80e5d52 LibWeb: Load cookies test from HTTP server
Allowing us to remove the internals hook.
2026-02-21 23:00:57 +01:00
Jelle Raaijmakers
ded42e649b LibWeb: Do not scroll cursor into view on programmatic selection changes
We were mimicking Firefox' behavior that whenever a programmatic change
to an <input>'s or <textarea>'s selection happened, the new selection
focus is brought into view by scrolling. Currently we run a layout
update synchronously for that to make sure we have the fragment's
correct dimensions, which caused a significant performance regression in
Speedometer.

Since this is non-standard behavior, let's mimic Chromium instead which
does not scroll at all - only for direct user initiated input such as
typing.

Relevant issues:

* https://github.com/whatwg/html/issues/6217
* https://bugzilla.mozilla.org/show_bug.cgi?id=232405
* https://issues.chromium.org/issues/41081857
2026-02-17 10:24:00 +01:00
Tim Ledbetter
ae0f5ef9ce LibUnicode+LibWeb: Add infrastructure for line segmentation using ICU
No behavior change This is needed for correct UAX#14 line breaking.
2026-02-14 16:23:18 -05:00
Andreas Kling
4a7ca32af0 LibWeb: Skip full document style update in getComputedStyle if possible
Before calling update_style() for a getComputedStyle property access,
we now check whether the target element actually needs a style update
by walking the flat tree ancestor chain. If neither the element nor any
of its ancestors have dirty style bits, and there are no document-level
reasons to recalculate style, we skip the update_style() call entirely.

We walk the flat tree (not the DOM tree) because style inheritance
follows slot assignment -- slotted elements inherit from their assigned
slot, not their DOM parent.

This avoids unnecessary work when scripts access computed style
properties on elements whose styles are already up-to-date, which is a
common pattern on the web.
2026-02-13 10:22:30 +01:00
Psychpsyo
dab742ed84 Everywhere: Remove double // on comments 2026-02-11 13:28:01 -06:00
Aliaksandr Kalenik
901cc28272 LibWeb: Reduce recompilation impact of DOM/Document.h
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.

This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
2026-02-11 20:02:28 +01:00
Jelle Raaijmakers
6c2583eade LibWeb: Implement mouse auto scrolling of scrollable containers
When the mouse is dragged from inside a scrollable container to outside
of it, we now automatically scroll the container so the selection can be
extended. Scroll speed scales with the distance past the scrollport
edge, capped at a maximum. Edges close to the viewport boundary get a
wider activation zone so the speed ramp works predictably even when the
mouse has limited room to move.

The logic is encapsulated in AutoScrollHandler, which EventHandler
creates lazily on mouse selection start.
2026-02-11 11:04:53 +01:00
Jelle Raaijmakers
2d4728d353 LibWeb: Keep cursor in view for text controls
When editing or changing the selection inside an <input> or <textarea>,
we should scroll the container so the cursor is always visible. Note
that currently the cursor might still become invisible at the end of the
container since we do not reserve enough space for it to be made
visible.
2026-02-11 11:04:53 +01:00
Aliaksandr Kalenik
eea9837438 LibWeb: Skip :has() invalidation in update_style when nothing is pending
Add a document-level boolean flag that tracks whether any :has()
invalidations have been scheduled. This avoids iterating over all
shadow roots just to check is_empty() on each style scope when no
:has() invalidations are pending, which is the common case during
scrolling on complex pages like Reddit.

Results in ~10% reduction of is_empty() calls in profiles when
scrolling on Reddit.
2026-02-11 00:28:42 +01:00
Aliaksandr Kalenik
38e53b5600 LibWeb: Templatize Document::for_each_shadow_root()
Replace AK::Function parameter with a template parameter so the
compiler can inline the lambda at each call site. This eliminates
type-erasure overhead (vtable indirection + ScopeGuard) that was
showing up in profiles during Reddit scrolling, where this function
is called repeatedly from update_style() for every shadow root on
every style update.
2026-02-11 00:28:42 +01:00
Andreas Kling
bacd946721 LibWeb: Bail out of image callbacks when document becomes inactive
HTMLImageElement's "update the image data" algorithm checks
is_fully_active() at the start, but its async continuations
(microtasks, element tasks, batching dispatcher callbacks) skip
this check. When an iframe is removed or navigated, these
callbacks fire on an inactive document, causing crashes.

Fix this with two changes:

1) Add is_fully_active() bail-out checks at all async callback
   entry points in HTMLImageElement. Each bail-out also clears
   the DocumentLoadEventDelayer to prevent blocking the parent
   document's load event forever.

2) Create the DocumentObserver eagerly in initialize() (like
   HTMLMediaElement) with a document_became_inactive callback
   that clears the load event delayer and stops the animation
   timer. Fire document_became_inactive from Document::destroy()
   in addition to did_stop_being_active_document_in_navigable(),
   since iframe removal takes a different path than navigation.
   A guard flag prevents duplicate firing.
2026-02-10 21:19:35 +01:00
Timothy Flynn
d050206fc3 LibWeb: Remove cookie source parameter from document cookie APIs
The source of any document cookie must be non-HTTP.
2026-02-10 12:21:20 +01:00
Timothy Flynn
8d97389038 LibHTTP+Everywhere: Move the cookie implementation to LibHTTP
This will allow parsing cookies outside of LibWeb.

LibHTTP is basically becoming the home of HTTP WG specs.
2026-02-10 12:21:20 +01:00
Aliaksandr Kalenik
abecc746d7 LibWeb: Implement partial SVG relayout
Previously, any SVG geometry attribute change would mark the entire
document layout tree as dirty, triggering a full layout pass even though
only the SVG subtree was affected. This made SVG geometry animations
unnecessarily expensive.

Fix this by stopping `needs_layout_update` propagation at the SVGSVGBox
boundary and tracking dirty SVG roots separately on the Document. When
`update_layout()` finds that only SVG roots need relayout (and the
document layout root is clean), it runs SVGFormattingContext on each
dirty SVG root in a fresh LayoutState and commits the results directly,
bypassing the full document layout pass entirely.

This results in a substantial performance improvement on pages with
animated SVGs, such as https://www.cloudflare.com/,
https://www.duolingo.com/, and our GC graph explorer page.
2026-02-09 03:02:49 +01:00
Jelle Raaijmakers
2a2f8ef90b LibWeb+UI: Support triple clicking and dragging paragraphs
When triple clicking on text, we should select the entire paragraph, or
entire line in <input>s and <textarea>s. If the mouse button is held
down and the user starts dragging, the selection expands with additional
paragraphs or lines.

This expands on the work of Kai Wildberger (PR #7681) but was adjusted
for the work that happened previously to support double click + drag
moves and includes triple click support for our Qt UI.

Co-authored-by: Kai Wildberger <kiawildberger@gmail.com>
2026-02-06 14:18:10 +00:00
Timothy Flynn
0482b6bb57 LibWeb+LibWebView+WebContent: Implement versioning for document cookies
This patch introduces a cookie cache in the WebContent process to reduce
blocking IPC calls when JS accesses document.cookie. The UI process now
maintains a cookie version counter per-domain in shared memory. When JS
reads document.cookie, we check whether we have a valid cached cookie by
comparing the current shared version to the last used version. If they
match, the cached cookie is returned without IPC.

This optimization is based on Chromium's shared versioning, in which it
was observed that 87% of document.cookie accesses were redundant. See:
https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html

Note that this cache only supports document.cookie, not HTTP Cookie
headers. HTTP cookies are attached to requests with varying URLs and
paths. The cookies that match the document URL might not match the
request URL, which we wouldn't know from WebContent. So attaching the
cached document cookie would be incorrect.

On https://twinings.co.uk, we see approximately 600 document.cookie
requests while the page loads. This patch reduces the time spent in
the document.cookie getter from ~45ms to 2-3ms.
2026-02-05 07:28:07 -05:00
Aliaksandr Kalenik
1f6ee6c63a LibWeb: Add internals.dumpStackingContextTree() for testing
This new API allows tests to inspect the stacking context tree structure
which is useful for verifying that stacking context invalidation and
rebuilding work correctly.
2026-01-28 18:05:41 +01:00
Tim Ledbetter
0035690977 LibWeb: Return body from activeElement() when nothing is focused
This matches the behavior of other browsers.
2026-01-19 13:08:30 +01:00
Jelle Raaijmakers
f2f8f3ae57 LibWeb: Propagate <body>'s image-rendering to root element
Whenever we propagated a <body>'s background image to the root element,
we ignored any `image-rendering` property present.
2026-01-19 12:05:08 +01:00
Adam Colvin
3a6d82245b LibWeb: Implement Screen.isExtended attribute
- Add WindowManagement to PolicyControlledFeature enum
- Add screen_count() virtual method to PageClient
- Store all screen rects in WebContent::PageClient, derive both
  screen_rect() and screen_count() from stored data
- Implement screen_count() overrides in SVGPageClient and PageHost
- Replace FIXME stub in Screen.cpp with spec-compliant implementation
2026-01-16 20:34:58 +01:00
Aliaksandr Kalenik
04c3ad669b LibWeb: Rebuild AccumulatedVisualContext on transform changes
Ensure AccumulatedVisualContext stays synchronized when CSS transform
properties change.

AccumulatedVisualContext copies transform and perspective matrices from
the paintable tree at assignment time. When CSS properties that affect
these matrices change (transform, rotate, scale, translate, perspective,
transform-origin, perspective-origin), we must rebuild the
AccumulatedVisualContext tree to reflect the new values.

This adds a rebuild_accumulated_visual_contexts flag to style
invalidation that triggers a full rebuild during the next paint.

Note: The current invalidation strategy is inefficient - it rebuilds
the entire tree even for single-element transform changes. This could
be improved by patching the AccumulatedVisualContext node in-place with
updated matrices, but only when the transform doesn't transition
from/to none (which would change the tree structure). This optimization
is left for future work.
2026-01-15 19:50:53 +01:00
Tim Ledbetter
79a427e1ef LibWeb: Implement HTMLImageElement x() and y() getters
These attributes get the image's top left border edge  relative to the
root element's origin.

These methods ignore any transforms.
2026-01-11 00:33:08 +01:00
Jonathan Gamble
196be843a5 LibWeb: Add helpers for focus management of text editors 2026-01-09 18:09:09 +01:00
Sam Atkins
960558f30a LibWeb: Register and unregister @property rules when added or removed
Previously, we registered `@property` rules during parsing, and treated
them the same as `CSS.registerProperty()` calls. This is not correct
for a couple of reasons: One, the spec wants us to distinguish between
those two sources of registered custom properties, with
`CSS.registerProperty()` calls taking precedence. Two, we never removed
the registered property when its `@property` was removed from the
document.

This commit deals with this by iterating active CSSPropertyRules to find
which ones currently apply, and storing those in a cache. This cache is
invalidated whenever the Document's style is invalidated, which happens
whenever a CSSRule is added or removed from the Document.

The attached test demonstrates this now working as it should.
2026-01-09 10:54:37 +00:00
Sam Atkins
02149a8032 LibWeb: Store registered custom properties as CustomPropertyRegistration
This brings us closer to the spec.
2026-01-09 10:54:37 +00:00
Andreas Kling
2ac363dcba LibGC: Only call finalize() on types that override finalize()
This dramatically cuts down on time spent in the GC's finalizer pass,
since most types don't override finalize().
2026-01-07 20:51:17 +01:00
Andreas Kling
418a243c04 LibWeb: Don't run tasks in documents that haven't been BC associated
Documents that have never been associated with a browsing context will
never become "fully active" so we shouldn't schedule tasks in them since
they'll never run.
2025-12-27 16:40:34 +01:00
Andreas Kling
737d9727c2 LibJS+LibWeb: Add various fast_is<T>() helpers for common things 2025-12-20 09:13:19 -06:00
Sam Atkins
cb0c428b3a LibWeb/DOM: Use a single scroll queue for all events
Corresponds to:
36f05864a6
302490c80c
https://github.com/w3c/csswg-drafts/pull/13238
https://github.com/w3c/csswg-drafts/pull/13239

The latter two are my own corrections which haven't been merged yet.
2025-12-19 12:09:19 -06:00
Callum Law
b89b453c70 LibWeb: Make update_animations_and_send_events parameter non-optional
We always have a value for this so there is no need to have it as
optional
2025-12-12 10:49:18 +00:00
Sam Atkins
80787d3a9c LibWeb/DOM: Stub out Document.exitPointerLock()
This lets us leave the main menu on classic.minecraft.net by tapping
Escape again.
2025-12-09 12:11:21 +01:00
Callum Law
6c236d04d8 LibWeb: Separate font computation logic from StyleComputer
Font computation and loading is distinct enough from style computation
that it makes more sense to have this in it's own class.

This will be useful later when we move the font loading process to
`ComputedProperties` in order to respect animated values.
2025-12-05 10:03:15 +00:00
Callum Law
12e8f503aa LibWeb: Support non-fixed <random-value-sharing>
This works by generating random values using XorShift128PlusRNG at
compute time and then caching them on the document using the relevant
random-caching-key
2025-12-01 11:00:33 +00:00
Andreas Kling
a94335dc5d LibWeb: Block rendering while waiting for CSS @import downloads
The implementation here is a ad-hoc, but there's no clear spec for
exactly how to handle "critical subresources" blocking rendering.

For now, this is overly conservative but fixes ugly FOUC on some
websites like https://hey.com/
2025-11-16 09:14:18 +01:00
Andreas Kling
66263f142b LibWeb: Add StyleScope to keep style caches per Document/ShadowRoot
Before this change, we've been maintaining various StyleComputer caches
at the document level.

This made sense for old-school documents without shadow trees, since
all the style information was document-wide anyway. However, documents
with many shadow trees ended up suffering since any time you mutated
a style sheet inside a shadow tree, *all* style caches for the entire
document would get invalidated.

This was particularly expensive on Reddit, which has tons of shadow
trees with their own style elements. Every time we'd create one of their
custom elements, we'd invalidate the document-level "rule cache" and
have to rebuild it, taking about ~60ms each time (ouch).

This commit introduces a new object called StyleScope.

Every Document and ShadowRoot has its own StyleScope. Rule caches etc
are moved from StyleComputer to StyleScope.

Rule cache invalidation now happens at StyleScope level. As an example,
rule cache rebuilds now take ~1ms on Reddit instead of ~60ms.

This is largely a mechanical change, moving things around, but there's
one key detail to be aware of: due to the :host selector, which works
across the shadow DOM boundary and reaches from inside a shadow tree out
into the light tree, there are various places where we have to check
both the shadow tree's StyleScope *and* the document-level StyleScope
in order to get all rules that may apply.
2025-11-14 22:05:33 +01:00
Callum Law
56e2ac8a9d LibWeb: Always do parent document layout updates before updating style
We were doing this manually within `Document::update_layout()` and
`CSSStyleProperties::get_direct_property()` but we should do it for all
callers of `Document::update_style()`
2025-11-02 23:54:00 +01:00
Tim Ledbetter
9da723b5c6 LibWeb: Ensure layout is up to date before resolving canvas colors 2025-10-27 16:56:01 -07:00
Tete17
1368744d33 LibWeb: Amend Document interface to make it compatible with TrustedTypes 2025-10-27 16:14:20 +00:00
Luke Wilde
eeb5446c1b LibWeb: Avoid including Navigable.h in headers
This greatly reduces how much is recompiled when changing Navigable.h,
from >1000 to 82.
2025-10-20 10:16:55 +01:00
Luke Wilde
11a1e97e40 LibWeb: Invalidate layout tree after removing element from top layer
Otherwise the layout tree will still contain the top layer element(s).

Fixes Steam Events & Announcements `<dialog>` modal visually not fully
disappearing upon removal.
2025-10-19 16:58:47 +02:00
Andreas Kling
dfa796a4e4 LibJS+LibWeb: Use GC::Weak instead of AK::WeakPtr for GC-allocated types
This makes some common types like JS::Object smaller (by 8 bytes) and
yields a minor speed improvement on many benchmarks.
2025-10-17 17:22:16 +02:00
Andreas Kling
127208f3d6 LibWeb: Move Document unregistration from EventLoop to finalizer
This ensure that it's not visible to event loops by the time we're
running destructors.
2025-10-17 17:22:16 +02:00