Commit graph

79 commits

Author SHA1 Message Date
Andreas Kling
3ec9733efe LibWeb: Resume intercepted history traversals
When a traverse navigate event is intercepted, queue the resume step
specified by the Navigation API. This keeps intercepted traversals from
stopping at URL update time.

Do not resume that queued step if the intercepted NavigateEvent was
aborted or replaced by a newer ongoing event before it runs. Add tests
for the basic intercepted traverse case and the superseded intercepted
traverse case.
2026-06-06 11:34:50 +02:00
Andreas Kling
164ed80244 Meta: Enable exit-time destructor warnings for libraries
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.

Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
2026-06-04 19:20:49 +02:00
Shannon Booth
2ae54d77e0 LibWeb: Reuse initial about:blank window for first same-origin load
Follow the spec reuse of the initial same-origin about:blank window for
a browsing context's first real navigation. This fixes a crash in
promise-job-entry-different-function-realm.html by preserving the
correct iframe realm.

However, reusing the initial about:blank Window means
create-and-initialize can associate that Window with the pending
Document before session history activation has made the Document active.
Treating a browsing context's active document as its active Window's
associated Document therefore exposes the pending Document too early.

To fix this, add an explicit active document slot to BrowsingContext and
update it when a Document is made active.

A similar fix was attempted in 7fc7263a4d,
but that version still queued navigation tasks through the reused active
Window, tagging them with the pending Document before it was active.
This caused parser-created iframe loads to hang in encoding WPTs. This
commit instead queues those navigation-internal tasks against the
navigable's currently active Document.

The newly added iframe-initial-load-chunked-body.html mimics the same
type of failure seen by the WPT regressions mentioned above.
2026-05-22 18:17:58 +02:00
Aliaksandr Kalenik
6b912038d3 LibWeb+WebContent: Route compositor through in-process IPC
LibWeb still exposed the concrete CompositorThread to Page,
Navigable, and EventHandler, so compositor IPC would have leaked the
thread implementation into callers. The old thread APIs also bundled
page presentation callbacks and main-thread wakeups into the same
object, which made it awkward for WebContent to put an actor boundary
in between.

Introduce CompositorHost and context handles as the caller-facing API,
and move shared compositor protocol values out of CompositorThread. Add
WebContentCompositor IPC endpoints and route PageHost through a paired
in-process transport. The actor owns CompositorThread with explicit
main-thread and UI presentation clients, while screenshot completion is
serialized on the WebContent event loop using request IDs.

The intention for introducing IPC here is to prepare for moving the
compositor thread into a separate process.
2026-05-21 11:45:06 +01:00
Andreas Kling
f7411a36a1 LibWeb: Notify clients when reload starts
Emit the existing page_did_start_loading notification when a
top-level reload starts repopulating the document. Normal navigation
already sends this notification earlier in the algorithm, but reload
enters through the session history path and skipped the UI signal.

This lets browser chrome show its loading indicator for reloads without
frontend-specific reload button hooks.
2026-05-20 09:37:33 +02:00
Aliaksandr Kalenik
81b5b343d9 LibWeb: Share WebContent compositor thread across pages
Move compositor-thread ownership to WebContent's PageHost so every Page
object in one WebContent process registers its navigables on the same
compositor thread. This covers auxiliary pages created by window.open(),
while worker and SVG helper pages continue to skip compositor thread
creation.

Keep page presentation keyed by page id. Each presenting context records
the page id it presents for, and static compositor entry points route
ready-to-paint, async scrolling, and viewport scrollbar events to that
page's presenting context on the shared thread.
2026-05-18 20:11:31 +02:00
Martin Chrástek
437c8b1d19 LibWeb: Implement legacy-clone of session storage shed on window.open
When a new window is opened via window.open() with an opener
(non-null), the session storage must be cloned from the opener's
storage shed into the new window's storage shed. This implements
the legacy-clone a traversable storage shed algorithm from the
Storage spec.
2026-05-18 16:38:15 +02:00
Andreas Kling
d447e6925d LibWeb: Handle reloads queued after same-document pushes
Treat an already-active target entry from a synchronous same-document
history step as update-only even if a later queued reload has marked it
reload-pending. This keeps the push/replace application from consuming
the reload state and leaves the reload traversal to populate the entry.

Add a regression test that pushes a same-document entry and immediately
reloads it, matching the queue ordering that exposed the crash.
2026-05-13 20:54:10 +02:00
Andreas Kling
1b810a5e15 LibWeb: Present frames through Compositor IPC
Introduce a dedicated Compositor IPC channel between the UI process and
WebContent. Use it for backing-store setup, presented bitmap delivery,
and bitmap-specific ready_to_paint acknowledgements.

This makes CompositorThread the single owner of frame presentation
bookkeeping before async scrolling starts producing frames without the
main thread.

Remove old paint and backing-store messages from WebContentClient and
PageClient so the UI process no longer observes two presentation
protocols.
2026-05-12 20:57:08 +02:00
Andreas Kling
c9fa971905 LibWeb: Don't strand a fresh navigation behind a sync history step
When a click handler calls history.replaceState and the link's
cross-document activation behavior runs in the same task, the queued
sync step runs apply-the-history-step on the navigable mid-navigation,
transitioning its ongoing navigation through "traversal" and back to
null. That aborts the link's navigate event and bails out its deferred
work, leaving the link nav abandoned.

Skip this transient when the navigable already has a fresh ongoing
navigation. No major engine reproduces the race; long term, sync
same-document nav should bypass the traversal queue entirely (matching
Chromium).

This solves the long-standing issue where clicking on a box of tea
on https://twinings.co.uk/ would freeze the browser. :^)
2026-04-27 17:24:13 +02:00
Jelle Raaijmakers
a5e1c33743 LibWeb: Parse srcdoc documents synchronously during activation
Bypass the async body-reading pipeline for about:srcdoc iframes whose
body bytes are already in memory. Set up a deferred parser at document
load time and run the post-activation update synchronously, so the body
element exists before parent script can observe the new document via
contentDocument. This matches Chrome and Firefox behavior for srcdoc
iframes and fixes the flaky test
`set-innerHTML-inside-iframe-srcdoc-document.html` that relied on body
being non-null.

Co-authored-by: Tim Ledbetter <tim.ledbetter@ladybird.org>
2026-04-22 13:27:45 +01:00
Jonathan Gamble
1c5907d87f LibWeb: Correct initiator origin logic for new top level traversables 2026-04-19 13:11:48 +02:00
Tim Ledbetter
a8144a2608 LibWeb: Use null-document task when checking is unloading is canceled
Previously, `CheckUnloadingCanceledState::start_phase2()` used
`queue_global_task()` to fire `beforeunload` on each relevant document.
However, tasks associated with a document are only runnable when the
document is fully active and it is possible for the document to become
not fully-active before the task is run. This caused a 15 second delay
in `CheckUnloadingCanceledState` while we waited for a timeout, which
could cause test timeouts when under heavy load.
2026-04-10 00:02:53 +02:00
Aliaksandr Kalenik
610ffffe81 LibWeb: Replace ad-hoc target step recomputation with verification
Previously, we had to recompute targetStep in the middle of history
step application because our session history traversal queue (SHTQ)
implementation was broken and didn't provide actual task serialization.
This meant the step could change while we were waiting for tasks to
complete.

Now that the SHTQ correctly serializes tasks, the step should no longer
change mid-application. Replace the recomputation with a VERIFY() assert
to enforce this invariant.
2026-04-03 18:41:55 +02:00
Aliaksandr Kalenik
e875f2b18b LibWeb: Make SessionHistoryEntry and DocumentState ref-counted
WebContent process keeps session history entries for pages we have
navigated away from. Before this change, those entries could prevent GC
objects (e.g. PolicyContainer and its CSP PolicyList) from being
collected, since the GC-allocated SHE/DocumentState held live GC::Ref
pointers into the heap.

By making both classes RefCounted and storing SerializedPolicyContainer
instead of a live PolicyContainer, history entries no longer keep alive
any GC objects. This eliminates the leak and is also a step toward
moving the session history entry tree to the UI process.
2026-04-03 14:20:09 +02:00
Aliaksandr Kalenik
4985dabf3d LibWeb: Replace cached navigable with Navigable-maintained back-pointer
Now that Navigable directly owns its active document (m_active_document)
we can have Navigable maintain a back-pointer on Document instead of
using the old cache-with-validation pattern that fell back to a linear
scan of all navigables via navigable_with_active_document().
2026-04-01 11:51:43 +02:00
Aliaksandr Kalenik
2645695fdd LibWeb: Make Navigable directly own its active document
Previously, the active document's lifecycle was bound to
SessionHistoryEntry via DocumentState. The ownership chain was:
  Navigable → SessionHistoryEntry → DocumentState → Document

This made it impossible to move SessionHistoryEntry to the UI process
(which cannot own DOM::Document). This commit decouples the two by
giving Navigable a direct m_active_document field that serves as the
authoritative source for active_document().

- Navigable owns m_active_document directly; active_document() reads
  from it instead of going through the active session history entry.

- DocumentState no longer holds a Document pointer. Instead, it stores
  a document_id for "same document?" checks. Same-document navigations
  share a DocumentState and thus the same document_id, while
  cross-document navigations create a new DocumentState with a new ID.

- A pending_document parameter is threaded through
  finalize_a_cross_document_navigation → apply_the_push_or_replace →
  apply_the_history_step so the newly created document reaches
  activation without being stored on DocumentState.

- For traversal, the population output delivers the document.
  A resolved_document is computed per continuation from either the
  pending document, the population output, or the current active
  document (for same-document traversals).
2026-04-01 11:51:43 +02:00
Aliaksandr Kalenik
f3ea882d6e LibWeb: Remove "signal to continue SHTQ" from document loading
This promise was previously used to signal the session history traversal
queue that it could continue processing, but is no longer needed.
2026-04-01 06:47:59 +02:00
Shannon Booth
0086a7899d LibWeb: Remove some uneeded navigation error propogation
We should not have any errors to propogate down these paths.
2026-04-01 04:41:11 +02:00
Aliaksandr Kalenik
2a69fd4c52 LibWeb: Replace spin_until in apply_the_history_step with state machine
Replace the blocking spin_processing_tasks_with_source_until calls
in apply_the_history_step_after_unload_check() with an event-driven
ApplyHistoryStepState GC cell that tracks 5 phases, following the
same pattern used by CheckUnloadingCanceledState.

Key changes:
- Introduce ApplyHistoryStepState with phases:
  WaitingForDocumentPopulation, ProcessingContinuations,
  WaitingForChangeJobCompletion, WaitingForNonChangingJobs and Completed
- Add on_complete callbacks to apply_the_push_or_replace_history_step,
  finalize_a_same_document_navigation,
  finalize_a_cross_document_navigation, and
  update_for_navigable_creation_or_destruction
- Remove spin_until from Document::open()
- Use null-document tasks for non-changing navigable updates and
  document unload/destroy to avoid stuck tasks when documents become
  non-fully-active
- Defer completely_finish_loading when document has no navigable yet,
  and re-trigger post-load steps in activate_history_entry for documents
  that completed loading before activation

Co-Authored-By: Shannon Booth <shannon@serenityos.org>
2026-03-31 09:47:59 +02:00
Jenn Barosa
8808a54f37 LibWeb: Fix session history replacement for superseded navigations 2026-03-29 03:11:14 +02:00
Aliaksandr Kalenik
200c72ae5c LibWeb: Remove spin_until from check_if_unloading_is_canceled()
Replace the two spin_processing_tasks_with_source_until() calls in
TraversableNavigable::check_if_unloading_is_canceled() with a
callback-based GC cell (CheckUnloadingCanceledState) that tracks
completion across both phases (traverse navigate event + per-document
beforeunload handlers) and invokes a callback when done.

This required making check_if_unloading_is_canceled() async
(callback-based), splitting apply_the_history_step() into pre-check
and continuation parts, and updating all callers to move session
history traversal queue promise resolution into callbacks.

The trusted-event test is rebaselined because beforeunload now fires
as a queued NavigationAndTraversal task rather than being processed
inline by spin_until. This allows the unhandledrejection microtask
to run before the beforeunload task, swapping their order.
2026-03-28 04:17:21 +01:00
Aliaksandr Kalenik
ce81f16530 LibWeb: Remove SessionHistoryEntry::clone() and DocumentState::clone()
With apply_to() now self-contained (carrying its own replacement
DocumentState rather than reading from the live entry), the clone at
the traversal call site is no longer needed.

The clone previously served two purposes:
1. Input snapshot: freeze entry fields before deferred population.
   Now solved by changing populate_session_history_entry_document() to
   take explicit input parameters, snapshotted before the
   deferred_invoke.
2. Output isolation: absorb apply_to() and post-population adjustments
   without mutating the live entry during unload. Now solved by storing
   the PopulateSessionHistoryEntryDocumentOutput on the continuation
   state and deferring all mutations (including the origin-based
   classic_history_api_state reset and navigable_target_name clear)
   to after_potential_unload.

The post-population adjustments run unconditionally in
after_potential_unload, covering both the population path and the
non-population path (e.g. traversal to an already-populated error
entry).
2026-03-27 02:34:55 +01:00
Aliaksandr Kalenik
49690f1e1e LibWeb: Separate input/output in populate_session_history_entry_document
Previously, populate_session_history_entry_document() took a
SessionHistoryEntry as both input and output — reading URL and
document_state fields while also mutating the entry across a chain of
async functions. This made it very hard to reason about data flow.

Refactor the internal helpers
(create_navigation_params_from_a_srcdoc_resource,
create_navigation_params_by_fetching, NavigationParamsFetchStateHolder,
perform_navigation_params_fetch) to take individual field values instead
of reading from the entry, and accumulate redirect mutations on the
state holder rather than writing them to the entry immediately.

Introduce PopulateSessionHistoryEntryDocumentOutput, a GC cell that
collects all mutations (document, redirect URL, classic history API
state, replacement document state, resource cleared flag, and
finalization data). The completion_steps callback now receives this
output object (or nullptr on cancellation), and callers apply it to the
entry via apply_to().

The replacement DocumentState for the redirect path is built eagerly at
redirect time from values captured on the state holder, making
apply_to() fully self-contained — it never reads from the target entry's
live document_state. This is important for the traversal path where the
entry may be mutated during unload (e.g. window.name writes
navigable_target_name through the active session history entry).
2026-03-27 02:34:55 +01:00
Jelle Raaijmakers
95955f40b1 LibWeb: Bail from apply_the_history_step() if document lost navigable
We pump the event loop just before these steps which can cause the
displayed document to be destroyed and lose its navigable. This was a
cause for crashes in the `encoding` WPT tests.
2026-03-26 18:48:27 +01:00
Jelle Raaijmakers
94062e4c5b LibWeb: Set the is_closing flag in close_top_level_traversable()
`close_top_level_traversable()` checks the `is_closing` flag to prevent
duplicate closes, but it is only set by callers of
`definitely_close_top_level_traversable()`. The flag is a bit in between
specs as things move from browsing contexts to navigables, but its
purpose is clear: without setting it, the check is ineffective and
`definitely_close_top_level_traversable()` runs multiple times for the
same traversable when the page has child navigables. This queues
duplicate session history traversal steps, where the second step
accesses the already-destroyed active document and segfaults.
2026-03-26 18:48:27 +01:00
Jelle Raaijmakers
0a81470bff LibWeb: Rewrite unload/destroy document lifecycle to follow the spec
1. unload_a_document_and_its_descendants() now follows the spec
   algorithm structure: recursively unload child navigables via queued
   tasks (step 4), wait for them (step 5), then queue this document's
   own unload as a separate task (step 6). Previously, this was
   flattened into a single spin that unloaded all descendants and the
   document together, followed by a non-spec call to
   destroy_a_document_and_its_descendants().

2. unload() step 19 now calls destroy() when the document is not
   salvageable, as the spec requires. Previously this was a no-op with a
   comment deferring to unload_a_document_and_its_descendants().

3. destroy_top_level_traversable() step 2 now calls
   destroy_a_document_and_its_descendants() instead of destroy().

The iframe-unloading-order test, which exercises named iframe access
during unload handlers (the scenario the previous logic was designed to
protect), still passes.

Fixes #7825
2026-03-15 09:03:20 -04:00
Jelle Raaijmakers
f2a199d321 LibWeb: Reformat and add spec steps
No functional changes.
2026-03-15 09:03:20 -04:00
Jelle Raaijmakers
28a7c0bed8 LibWeb: Use source-filtered spin in check_if_unloading_is_canceled()
check_if_unloading_is_canceled() can be called nested inside an outer
spin_processing_tasks_with_source_until (via apply_the_history_step ->
pump -> deferred_invoke -> begin_navigation). A regular spin_until()
deadlocks in that scenario because m_skip_event_loop_processing_steps
is true, causing process() to return early without ever executing the
queued tasks.

Since both spins here wait exclusively for NavigationAndTraversal
tasks, switch them to spin_processing_tasks_with_source_until() which
bypasses process() and directly executes matching tasks from the queue.
2026-03-15 09:03:20 -04:00
Andreas Kling
1154a9660e LibWeb: Update layout before processing screenshots
Ensure layout is up to date before taking element or page screenshots,
so that paintable boxes have correct geometry.
2026-02-26 21:09:08 +01:00
Andreas Kling
786e93b6c7 LibWeb: Avoid unnecessary GC::Root copies in find_if lambdas
The find_if lambdas in CustomElementRegistry and TraversableNavigable
were taking GC::Root parameters, causing implicit conversion from
GC::Ref (or copying of GC::Root) for each element visited. Each
GC::Root creation/destruction allocates and frees a RootImpl, making
linear scans over these vectors far more expensive than necessary.

Use auto const& to match the actual vector element types.

This was 2.3% of CPU time while opening a YouTube video.
2026-02-21 15:53:22 +01: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
0145e425db LibWeb: Skip destroyed navigables in apply_the_history_step
When queuing tasks for changing navigables in step 12 of
apply_the_history_step, skip navigables that have been destroyed and
increment completed_change_jobs directly. This is necessary because
Document::destroy() removes tasks associated with its document from the
task queue, so a task queued for a destroyed navigable's window may
never run, causing the subsequent spin_until to wait forever.
2026-02-10 21:19:35 +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
f64b0e0351 LibWeb: Avoid GC::Root capture in check_if_unloading_is_canceled()
We never want to capture GC::Roots in GC::Function lambdas, since that
very easily creates reference cycles and leak huge object graphs.
Capturing a raw pointer or GC::Ptr/Ref is fine, since that's exactly
what GC::Function is good at.
2025-12-24 10:19:28 +01:00
Andreas Kling
52da7b649b LibWeb: Move vector of navigables in check_if_unloading_is_canceled
If we copy instead of move, the GC::Root source locations get
overwritten and we can't tell from a heap dump who created the roots.

(And it's also more efficient to move instead of copy ofc.)
2025-12-24 10:19:28 +01:00
Tim Ledbetter
244c33112b LibWeb: Handle errors when capturing screenshots
Previously, the browser would crash if the screenshot bitmap it
attempted to capture was too large.
2025-12-16 09:16:23 +01:00
InvalidUsernameException
28ba610f32 Everywhere: Avoid large rebuilds when editing (Immutable)Bitmap headers
This reduces the number of recompiled files as follow:
- Bitmap.h: 1309 -> 101
- ImmutableBitmap.h: 1218 -> 75
2025-11-28 18:32:48 +01:00
Prajjwal
50a79c6af8 LibWeb: Change SessionHistoryTraversalQueue to use Promises
If multiple cross-document navigations are queued on
SessionHistoryTraversalQueue, running the next entry before the current
document load is finished may result in a deadlock. If the new document
has a navigable element of its own, it will append steps to SHTQ and
hang in nested spin_until.
This change uses promises to ensure that the current document loads
before the next entry is executed.

Fixes timeouts in the imported tests.

Co-authored-by: Sam Atkins <sam@ladybird.org>
2025-11-26 12:27:12 +01:00
Psychpsyo
100f37995f Everywhere: Clean up AD-HOC and FIXME comments without colons 2025-11-13 15:56:04 +01:00
Luke Wilde
167de08c81 LibWeb: Remove exception throwing from Fetch
These were only here to manage OOMs, but there's not really any way to
recover from small OOMs in Fetch especially with its async nature.
2025-11-07 04:08:30 +01:00
Jelle Raaijmakers
518c048eb4 LibWeb+WebContent: Rename Document::focused_element to ::focused_area
And make it a DOM::Node, not DOM::Element. This makes everything flow
much better, such as spec texts that explicitly mention "focused area"
as the fact that we don't necessarily need to traverse a tree of
elements, since a Node can be focusable as well.

Eventually this will need to be a struct with a separate "focused area"
and "DOM anchor", but this change will make it easier to achieve that.
2025-08-26 10:25:59 +02:00
Timothy Flynn
7fad8c333d LibWeb: Use forward-declarations of structured serialized types
This reduces the rebuilt targets when touching StructuredSerialize.h
from ~1200 to ~400. The remaining are due to generated IPC headers.
2025-07-18 10:09:02 -04:00
Sam Atkins
df2b2d2c14 LibWeb/HTML: Fix return value checking for unload cancellation
Corresponds to 4467ddf323
2025-07-08 17:08:39 +01:00
Aliaksandr Kalenik
c18314b942 LibWeb+LibGfx: Replace BackingStore with PaintingSurface
Now, when Skia backend context is available by the time backing stores
are allocated, there is no need to have a separate BackingStore class.

This allows us to get rid of BackingStore -> PaintingSurface cache.
2025-07-04 16:12:47 +02:00
Aliaksandr Kalenik
082053d781 LibWeb+WebContent+WebWorker: Move backing store allocation in Navigable
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.
2025-07-04 16:12:47 +02:00
Aliaksandr Kalenik
b73525ba0e LibWeb+WebContent: Delete unused "has focus" flag from paint config 2025-07-04 16:12:47 +02:00
Sam Atkins
423cdd447d LibWeb+LibGfx: Apply editorial punctuation/whitespace/markup fixes
Corresponds to d426109ea1
and fd08f81d06
2025-06-25 03:12:19 +12:00
Jelle Raaijmakers
71f03cb785 LibWeb: Implement emulated Geolocation position retrieval
This implements enough of the Geolocation spec that it is now possible
for websites to retrieve the current geo position or try to watch for
updates (which currently never happen).

As it stands now, it only returns a single emulated position that points
to San Francisco.
2025-06-24 11:33:41 +02:00
Aliaksandr Kalenik
f53559cb55 LibWeb: Change Storage{Bottle,Bucket,Shelf} to be GC-allocated
In upcoming changes StorageBottle will own pointers to GC-allocated
objects, so it needs to be a GC-allocated object itself to avoid
introducing more GC roots.
2025-06-12 17:04:35 +02:00