Commit graph

149 commits

Author SHA1 Message Date
Jelle Raaijmakers
e97de2e7e3 LibWeb: Check for an active browsing context in the sniff bytes callback
The navigable can lose its browsing context while waiting for network
tasks to complete. Fixes a crash seen in the `encoding` WPT tests.
2026-03-26 18:48:27 +01:00
Shannon Booth
346fa16b1e LibWeb: Don't cancel pending navigations in navigate to a javascript URL
Other browsers appear to only do this for form submission, not for
all javascript URL navigations. Let's remove the handling in the
general javascript URL navigation handling so that our behaviour
diference to other browsers is limited specifically to form
elements, instead of the general case.

Unfortunately this does (expectedly) cause the test added in
3e0ea4f62 to start timing out, so that test is marked as skipped.
2026-03-25 22:05:25 +01:00
Timothy Flynn
58791db818 AK+LibWeb: Move generation of random UUIDs into AK
This will let us use this more outside of LibWeb more easily.

Stop handling tiny OOM while we are here.
2026-03-24 12:04:50 -04:00
Jelle Raaijmakers
dd6d17d60d LibWeb: Don't crash accessing stale nested navigable paintable
The scroll state collection loop in
record_display_list_and_scroll_state() called paintable() on hosted
documents, which asserts layout is up to date. This crashes when a
nested document has stale layout but a cached display list, e.g. a
render-blocked iframe whose DOM was modified by document.open().
Since scroll offsets are independent of layout freshness, use
unsafe_paintable() to skip the assertion.
2026-03-24 12:47:02 +01:00
Tim Ledbetter
279248960b LibWeb: Handle null active window in navigation abort task
There was already a null check before this task was queued, but it's
possible for the active window to become null between the time the task
is queued and executed, so we need to check again.
2026-03-20 15:56:27 -05:00
Jelle Raaijmakers
c8baa6e179 LibWeb: Remove tasks for destroyed documents instead of running them
Previously, destroyed-document tasks were forced to be runnable to
prevent them from leaking in the task queue. Instead, discard them
during task selection so their callbacks never run with stale state.

This used to cause issues with a couple of `spin_until()`s in the past,
but since we've removed some of them that had to do with the document
lifecycle, let's see if we can stick closer to the spec now.
2026-03-19 15:24:46 -05:00
Tim Ledbetter
26389363ad LibGfx+LibWeb: Move Skia backend context to process level singleton
Previously, this was attached to the traversable navigable. Using a
singleton instead allows us to use the context for detached documents.
2026-03-19 13:35:16 +01:00
Aliaksandr Kalenik
9d2ebe90ed LibWeb: Store visual context nodes in arena-based tree
Replace per-node heap-allocated AtomicRefCounted
AccumulatedVisualContext objects with a single contiguous Vector inside
AccumulatedVisualContextTree. All nodes for a frame are now stored in
one allocation, using type-safe VisualContextIndex instead of RefPtr
pointers.

This reduces allocation churn, improves cache locality, and opens the
door for future snapshotting of visual context state — similar to how
scroll offsets are snapshotted today.
2026-03-11 11:16:36 +01:00
Aliaksandr Kalenik
01a7c8e424 LibWeb: Add PaintableBox::transform_rect_to_viewport()
Extract the repeated pattern of transforming a rectangle from absolute
coordinates to viewport coordinates via the accumulated visual context
into a helper method.
2026-03-11 02:31:30 +01:00
Aliaksandr Kalenik
eae94a8a46 LibWeb: Route repaint requests through paintables, not Document
Rename Document::set_needs_display() to set_needs_repaint() and make it
private. External callers must now go through Node/Paintable which
route the request to the document internally.

Fix one existing misuse in AnimationEffect that was calling
document-level set_needs_display() instead of routing through the
target element's paintable.

This is preparation for per-paintable display list command caching:
repaint requests must go through specific paintables so their cached
command lists can be invalidated.
2026-03-04 19:35:45 +01:00
Tim Ledbetter
65b08e7d9f LibWeb: Use correct point type in to_top_level_position()
This occurred because 90a211b changed the return type of
`transform_rect_to_viewport()` but 7fc945d didn't take this into
account.
2026-03-01 09:54:59 +00:00
Tim Ledbetter
7fc945d524 LibWeb: Account for scroll and transforms in to_top_level_position()
This change means the right click context menu is displayed in the right
place when clicking inside an iframe on a scrolled page, including when
the iframe has CSS transforms applied to it.
2026-03-01 08:31:41 +00:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Andreas Kling
e3b55e2642 LibWeb: Update layout before getting selected text
Ensure layout is up to date before querying selected text, so that
paintable geometry used for text extraction is correct.
2026-02-26 21:09:08 +01:00
Andreas Kling
787a83b50a LibWeb: Stop calling Window::scroll_by() during layout
Replace the Window::scroll_by(0, 0) call at the end of
Document::update_layout() with a dedicated
Navigable::clamp_viewport_scroll_offset() that directly clamps the
viewport scroll offset to valid bounds.

The old approach re-entered layout from within layout, since scroll_by()
would trigger another layout update. The new approach is called from the
event loop's rendering steps, after layout is complete.
2026-02-26 21:09:08 +01:00
Andreas Kling
7b0d818f8e LibWeb: Update layout before accessing paintables in viewport scroll
perform_a_scroll_of_the_viewport() accesses paintable_box() without
ensuring layout is up to date. This can lead to a null dereference
if the paintable tree was torn down (e.g. by adding a dialog to the top
layer via showModal()) between the last layout update and the scroll.

One concrete path: Window::scroll() has an optimization that skips
update_layout when scrolling to (0, 0), but still calls
perform_a_scroll_of_the_viewport if the viewport is at a non-zero
position.

Fix by adding an update_layout call at the top of
perform_a_scroll_of_the_viewport.
2026-02-25 10:18:08 +01:00
Jelle Raaijmakers
66c54b1295 LibWeb: Defer navigations during traversal instead of dropping them
In `TraversableNavigable::apply_the_history_step()`, we have a
`spin_until(..)` that pumps the event loop. If it's invoked as a result
of `Navigable::begin_navigation()` and we've received an IPC call at the
same time, we might reenter `begin_navigation()` which then drops the
new navigation as there is an ongoing navigation.

The IPC call most likely causing tests to time out, is
`page_did_finish_loading`.

This is a workaround until we get rid of the `spin_until()`. It fixes
almost all of the test timeouts I had locally on macOS.
2026-02-23 17:14:56 +01:00
Jelle Raaijmakers
1cc29c669a WebContent: Combine viewport size and DPR into a single IPC message
The set_viewport_size and set_device_pixel_ratio IPC messages were sent
separately, potentially causing a race condition when the DPR changes
(e.g. moving a window between screens): the DPR message would arrive
and use a stale viewport size, computing a temporarily wrong CSS
viewport. Combine both into a single set_viewport IPC that updates the
device viewport size and DPR together.
2026-02-23 15:22:12 +01:00
Praise-Garfield
6a3b9c5ffc LibWeb: Use correct condition and field for navigation initiator origin
The guard for setting top-level navigation initiator origin called
top_level_traversable()->parent() == nullptr, which is tautologically
true: top_level_traversable() already walks to the topmost traversable,
whose parent is always null. This caused the field to be set on every
navigation, including child navigable navigations inside iframes.

The value was also read from document_state()->origin() instead of
document_state()->initiator_origin(), giving the document's own origin
rather than the origin of whoever initiated the navigation.

Use is_top_level_traversable() and initiator_origin() to match the
spec step.
2026-02-23 13:07:32 +01:00
Tim Ledbetter
5060a61081 LibWeb: Align Navigable::reload() with the specification 2026-02-14 20:21:53 +01:00
Praise-Garfield
4a5746db10 LibWeb: Correct body null check in navigation params fetch
Step 8 of "create navigation params by fetching" says "if
request's body is null, then set entry's document state's
resource to null." The condition is inverted: it checks
!body().has<Empty>(), entering the block when the body is
non-null instead of null. For GET navigations this is mostly
a no-op since resource is typically already Empty, but for
POST navigations the inverted check incorrectly clears the
POSTResource from the document state, breaking session history
entries that rely on it for form re-submission.
2026-02-13 15:26:54 -05:00
Praise-Garfield
ebdb9c34bd LibWeb: Check for NavigationParams in finalize_session_history_entry
Step 7.3 of finalize_session_history_entry checks whether
navigationParams "is a navigation params (i.e., neither null
nor a non-fetch scheme navigation params)." The condition
checks has<NonFetchSchemeNavigationParams>() instead of
has<NavigationParams>(), matching the wrong variant type.
The block body already accesses the variant as NavigationParams,
so if the condition ever matched it would VERIFY-crash. The
identical spec clause five lines above (step 7.2.3.1) is
implemented correctly as has<NavigationParams>().

The spec assertion "navigationParams's request is not null"
does not hold for srcdoc navigations, which create
NavigationParams with a null request. The condition guards
on request being non-null instead, with an NB comment
explaining the divergence.
2026-02-13 15:25:52 -05: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
Andreas Kling
3240cd536c LibWeb: Prevent navigation on destroyed navigables
When an iframe is removed before its post-connection session history
steps finish processing, the queued steps still run and call
navigate() on the now-destroyed navigable. This calls
set_delaying_load_events(true), creating a DocumentLoadEventDelayer
on the parent document. Since the navigable is destroyed, no
finalize step ever runs to clear the delayer, permanently blocking
the parent document's load event.

Fix this by checking has_been_destroyed() at the start of
begin_navigation() and bailing out early.
2026-02-10 21:19:35 +01:00
Shannon Booth
1da77d2796 LibWeb/HTML: Simplify checking if navigation ID is no longer the same 2026-02-05 08:38:33 +01:00
Aliaksandr Kalenik
42544c1ff5 LibWeb: Rebuild visual contexts after visual viewport scroll
When the visual viewport scrolls (e.g. panning while zoomed in), the
transform matrix baked into the AccumulatedVisualContext tree becomes
stale. Previously only set_needs_display(InvalidateDisplayList::No) was
called, which re-rendered with the old transform — making hit testing
and painting incorrect after visual viewport panning.
2026-01-31 19:20:09 +01:00
Jelle Raaijmakers
887ed85877 LibWeb: Return early during viewport scrolling if nothing changes
I noticed that when performing a smooth scroll up or down, we would
repeatedly enter `Navigable::perform_a_scroll_of_the_viewport()` when
the top or bottom (respectively) of the page was reached. Optimize this
case by returning early when we've reached the intended target scroll
position and we're not performing any vertical or horizontal scrolling.
2026-01-29 08:56:21 +01:00
Aliaksandr Kalenik
a86f318a9b LibWeb: Move rendering backpressure from main thread to RenderingThread
In preparation for handling input events on the rendering thread, move
backpressure management to RenderingThread. The rendering thread needs
to manage this independently without querying the main thread.

Previously, the main thread would block when the UI process hadn't yet
released the backing surface. Now, the main thread can continue
producing display lists while the UI process is busy, allowing more work
to happen in parallel. When rasterization is slow and display lists are
produced faster than they can be consumed, presentation requests are
naturally coalesced using a flag-based approach -
multiple present_frame() calls simply update the pending state,
resulting in a single rasterization with the latest display list.
2026-01-26 19:05:34 +01:00
Shannon Booth
3e0ea4f62e LibWeb/HTML: Check ongoing navigation before javascript: URL navigation
See: 976d272

This fixes a timeout in the included test. In order for the
test to run properly in our test harness it is modified slightly
(as we are loading the HTML from a file:// URL).
2026-01-26 18:46:54 +01:00
Aliaksandr Kalenik
b69ec8757f LibWeb: Store presentation callback in RenderingThread
This is preparation for future work where the rendering thread will
initiate rasterization independently and notify the UI process without
requiring coordination with the main thread.
2026-01-26 17:01:48 +01:00
Aliaksandr Kalenik
516fb5f2fe LibWeb: Make RenderingThread own display list and backing stores
This change prepares for a future where the rendering thread handles
input events directly, allowing it to trigger repainting without
waiting for the main thread. To support this, the compositor needs to
own the display list, scroll state, and backing stores rather than
receiving them per-frame from the main thread.
2026-01-26 14:53:08 +01:00
Andreas Kling
37bdcc3488 LibWeb: Support MIME type sniffing for streaming HTTP responses
Previously, when loading a document, we would try to sniff the MIME
type by reading from the response body's source. However, for streaming
HTTP responses, the body source is Empty (the data comes through the
stream instead), so we had no bytes to sniff.

This caused pages like hypr.land (which sends no Content-Type header)
to be misidentified as plain text instead of HTML, since the MIME
sniffing algorithm would receive zero bytes and fall back to the
default type.

The fix captures the first bytes of the response body during fetch,
storing them on the Body object. These bytes are the "resource header"
defined by the MIME Sniffing spec - up to 1445 bytes, which is enough
to identify any MIME type the spec can detect.

Since bytes may arrive asynchronously during streaming, we use a
callback mechanism: if bytes aren't ready yet when load_document()
needs them, it registers a callback that fires once enough bytes have
been captured (or the stream ends).

The flow is:
1. FetchedDataReceiver receives network bytes, buffers them
2. When Body is created, buffered bytes are flushed to Body's sniff
   buffer, and subsequent bytes are appended as they arrive
3. Before calling load_document(), Navigable waits for sniff bytes
4. load_document() passes the bytes to MimeSniff::Resource::sniff()
2026-01-24 15:21:26 +01:00
Aliaksandr Kalenik
f63674e92d LibWeb: Fix scroll regression when pinch-to-zoom is applied
When scrolling with a visual viewport offset (from pinch-to-zoom),
scroll_viewport_by_delta() was passing m_viewport_scroll_offset + delta
to perform_a_scroll_of_the_viewport(). However, that function calculates
the scroll delta as `position - page_top()`, where page_top() includes
the visual viewport offset. This caused the effective scroll delta to be
reduced by the visual offset amount.

Fix by using the current page position (which includes the visual
offset) as the base for the delta calculation.

Regression from 0a57e1e8ac.
2026-01-23 17:52:50 +01:00
Aliaksandr Kalenik
8d47c98ed3 LibWeb: Cache scroll state snapshot in ViewportPaintable
Cache the scroll state snapshot in ViewportPaintable when
refresh_scroll_state() is called.

The upcoming AccumulatedVisualContext integration requires access to
the scroll state snapshot during hit testing to transform screen
coordinates through scroll frames. Without caching, each hit test would
allocate a new snapshot (a Vector<Entry>), causing many temporary
allocations during mouse movement. Caching the snapshot eliminates this
overhead.
2026-01-15 19:50:53 +01:00
Tim Ledbetter
1022ec3b4a LibWeb: Set referrer policy on navigation fetch requests 2026-01-12 12:47:40 +01:00
Sam Atkins
8a0ba904b9 LibWeb/HTML: Return Promises from Window scroll methods
A version of this was added in a610639119
and reverted in 70671b4c11. The bugs
there (confusing scroll-to-position and scroll-by-delta, and not having
an execution context in some cases) have been fixed in this version.
2026-01-08 14:50:09 +00:00
Sam Atkins
0a57e1e8ac LibWeb: Clarify some scrolling-related code
A lot of our scrolling code is quite old, and doesn't match the spec,
but does use some similar names. This is quite confusing. In particular
`perform_scroll_of_viewport()` is not the same as the spec algorithm.
That algorithm is actually almost implemented in
`scroll_viewport_by_delta()`.

To clarify things, this commit makes a few changes:
- Rename perform_scroll_of_viewport() to
  perform_scroll_of_viewport_scrolling_box(). This is a better match
  for how we use this method, even if it's not actually a match for the
  algorithm. (:yakbait:)
- Move `scroll_viewport_by_delta()`'s code into a new
  `perform_a_scroll_of_the_viewport()` method, and make it take a
  position like it should. `scroll_viewport_by_delta()` now calls it
  with a calculated position.

I've avoided reusing the original `perform_scroll_of_viewport()` name to
avoid accidents.
2026-01-08 14:50:09 +00:00
Jonathan Gamble
7385569a02 LibWeb: Selection toString focused text control delegation
Allows selected text in form controls to be copied to clipboard.
2026-01-02 18:40:05 +01:00
Aliaksandr Kalenik
9a2931527a LibWeb: Use ConservativeVector in get_session_history_entries()
This way we ensure doc states traversal queue elements are visited.
2025-12-27 18:40:21 +01:00
Aliaksandr Kalenik
38af5d623c LibWeb: Avoid shifting vector elements in get_session_history_entries()
This function essentially performs a BFS traversal over document states.
With this change, we let `doc_states` grow instead of removing traversed
states, avoiding shifting elements on every iteration.

This reduces `./test-web -j 1` from ~7m to ~5m on my machine.
2025-12-27 18:40:21 +01:00
Aliaksandr Kalenik
f6a7df78e7 LibWeb: Add missing GC visits for XHR::FormDataEntry
3a6782689 fix up that changes `Vector<XHR::FormDataEntry>` to
`GC::ConservativeVector<XHR::FormDataEntry>`.
2025-12-26 19:48:46 +01:00
Tim Ledbetter
70671b4c11 Revert "LibWeb/HTML: Return Promises from Window scroll methods"
This reverts commit a610639119.
2025-12-26 19:33:51 +01:00
Aliaksandr Kalenik
8af78cff30 LibWeb: Skip scroll promise allocation in mouse event handling
Fixes crashing introduced in a610639 when `scroll_viewport_by_delta()`
is called from `EventHandler::handle_mousewheel()` and there's no
running execution context to grab current realm from to allocate a
promise.
2025-12-23 17:36:48 +01:00
Sam Atkins
a610639119 LibWeb/HTML: Return Promises from Window scroll methods
Corresponds to part of:
c548a9a1d4
2025-12-23 14:24:28 +01: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
Psychpsyo
3a4f7eeb4a LibWeb: Prevent scrolling viewport with overflow:hidden 2025-12-15 09:47:25 +00:00
Sam Atkins
43bd5342c5 LibWeb/HTML: Create a temporary execution context to abort navigation
Stops this WPT test from crashing:
navigation-api/ordering-and-transition/transition-to.html

...it now times out instead, so more work is needed before importing.
2025-12-05 09:20:02 +00:00
Sam Atkins
c8990697d1 LibWeb: Add FIXME for moving "stop parsing" step for error documents
Corresponds to:
48cd393d1e

Actually doing so was causing crashes and other issues, so I'll leave it
as a FIXME for later.
2025-12-01 11:07:16 +00:00
Timothy Flynn
9375660b64 LibHTTP+LibWeb+RequestServer: Move Fetch's HTTP header infra to LibHTTP
The end goal here is for LibHTTP to be the home of our RFC 9111 (HTTP
caching) implementation. We currently have one implementation in LibWeb
for our in-memory cache and another in RequestServer for our disk cache.

The implementations both largely revolve around interacting with HTTP
headers. But in LibWeb, we are using Fetch's header infra, and in RS we
are using are home-grown header infra from LibHTTP.

So to give these a common denominator, this patch replaces the LibHTTP
implementation with Fetch's infra. Our existing LibHTTP implementation
was not particularly compliant with any spec, so this at least gives us
a standards-based common implementation.

This migration also required moving a handful of other Fetch AOs over
to LibHTTP. (It turns out these AOs were all from the Fetch/Infra/HTTP
folder, so perhaps it makes sense for LibHTTP to be the implementation
of that entire set of facilities.)
2025-11-27 14:57:29 +01:00
Timothy Flynn
f675cfe90f LibWeb: Store HTTP methods and headers as ByteString
The spec declares these as a byte sequence, which we then implemented as
a ByteBuffer. This has become pretty awkward to deal with, as evidenced
by the plethora of `MUST(ByteBuffer::copy(...))` and `.bytes()` calls
everywhere inside Fetch. We would then treat the bytes as a string
anyways by wrapping them in StringView everywhere.

We now store these as a ByteString. This is more comfortable to deal
with, and we no longer need to continually copy underlying storage (as
ByteString is ref-counted).

This work is largely preparatory for an upcoming HTTP header refactor.
2025-11-26 09:15:06 -05:00