Commit graph

78 commits

Author SHA1 Message Date
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
Aliaksandr Kalenik
93bbdf1f55 LibWeb: Use compositor surface IDs for embedded content
Display lists kept canvas and nested navigable content alive through
ExternalContentSource objects. That made the resource graph depend on
process-local object identity instead of a stable surface handle, which
blocks compositor process isolation and made teardown-sensitive embedded
content harder to reason about.

Allocate CompositorSurfaceId values for canvases and child navigables,
publish their backing stores to the owning compositor, and paint them
with DrawCompositorSurface. Child navigables now publish to parent
compositors by CompositorContextId instead of raw object pointers, so
the in-process path uses the same stable addressing model required by a
remote compositor.

Clear and skip stale child surfaces during teardown, preserve Skia
canvas state while drawing compositor surfaces, and add display-list
coverage for canvas and iframe compositor surfaces. The nested navigable
async-scrolling baseline now expects DrawCompositorSurface.
2026-05-18 15:27:59 +02:00
Aliaksandr Kalenik
8906011a6b LibWeb: Synchronize display list resources via transactions
Display lists used to own the resource storage needed to replay their
command bytes. That kept the compositor tied to in-process object
ownership: sending a display list update also meant sharing the same
resource container with the recording side.

Move resource storage out of DisplayList and make display list updates
carry a transaction of resources to add and remove. Navigable now tracks
the resources referenced by the current display list, sends only the
delta to the compositor, and trims its recording-side storage to the
active set. The compositor applies those transactions to its own storage
before replacing the cached display list.

This still carries in-process resource objects, but it puts the
ownership boundary in the right place. Command bytes and resource
lifetime are now synchronized explicitly, which is the shape needed
before the compositor can receive serializable resource updates across a
process boundary.
2026-05-16 19:35:24 +02:00
Aliaksandr Kalenik
fd823becd7 LibWeb: Move backing store management into compositor thread
BackingStoreManager was owned by Navigable and allocated on the GC heap,
which left backing-store sizing decisions on the main thread even though
the compositor thread was already responsible for allocating the actual
surfaces. That split ownership makes it harder to isolate the compositor
behind a process boundary.

Move the manager into LibWeb's compositor code and let CompositorThread
thread data own it. The main thread now reports viewport size changes
through a compositor command, and the compositor uses that message to
decide when to resize and publish backing stores. The delayed shrink
timer remains with the main-thread CompositorThread facade so it can use
the Core event loop, but it now only sends another viewport-size update.

This removes the GC edge from Navigable, drops stale WebContent includes
and keeps the existing resize padding and delayed shrink behavior.
2026-05-16 16:36:05 +02:00
Aliaksandr Kalenik
f743263871 LibWeb: Let internals.wheel await async scroll adoption
Async scrolling tests used requestAnimationFrame() as a proxy for the
compositor thread to return pending scroll updates to the main thread.
That waited for a rendering opportunity, so tests could observe stale
DOM scroll offsets when the compositor update had not been adopted yet.

Make internals.wheel() return a promise that resolves after a tracked
async scroll operation has been applied by Navigable. Tracking is opt-in
from the internals test API, so regular page wheel input and compositor
IPC keep the boolean async-scroll path without allocating operation IDs.
Tracked test scrolls are the only operations that record completions.

Update async scrolling and wheel propagation tests to await the wheel
promise directly instead of relying on animation frame timing in tests.
2026-05-15 19:10:27 +02:00
Aliaksandr Kalenik
d353954993 LibWeb: Adopt async scroll offsets for nested scroll nodes
Nested scroll nodes were present in the async scroll tree, but the
compositor rejected any non-viewport wheel target and tracked only one
pending viewport offset. That prevented element scrollports from being
scrolled asynchronously and made compositor-side offsets impossible to
adopt after the main thread rebuilt scroll state.

Let async wheel commands target any scroll node selected by the tree.
Store every offset produced by a compositor scroll and reapply pending
offsets by stable ID across display-list and scroll-state updates. The
main thread now adopts viewport, element and pseudo-element offsets
before rendering-update observers run, including scroll event
bookkeeping for element scrollers.

Carry the wheel hit-test rejection reason through the enqueue path while
doing this, so main-thread and blocking-wheel regions remain explicit
rather than being collapsed into a missing target. Existing nested
async-scrolling text tests cover the successful nested scroll path.
2026-05-14 19:41:32 +02:00
Andreas Kling
76fc843978 LibWeb: Run observers after compositor-only scrolls
Adopt pending async viewport scroll offsets before the rendering update
runs scroll steps and IntersectionObserver updates. Queue a rendering
update after the compositor applies an async viewport scroll so
compositor-only wheel scrolling can notify observers without waiting for
unrelated main-thread work.
2026-05-13 15:00:31 +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
584b2748ee LibWeb: Move RenderingThread into Compositor
Create Libraries/LibWeb/Compositor and make the existing rendering
thread the first owner in that subsystem. Rename RenderingThread to
CompositorThread so later commits can grow it into the presentation
owner without leaving that vocabulary in HTML.

Keep display-list rasterization and delivery behavior unchanged in this
commit. Add COMPOSITOR_DEBUG to AK/Debug.h.in in the same step so
compositor diagnostics live beside the rest of the project-wide debug
toggles from the start of the stack.
2026-05-12 20:57:08 +02:00
Aliaksandr Kalenik
671cc8595a LibWeb: Send scroll-state-only updates to the rendering thread
When nothing invalidates the display list between frames, push only an
updated scroll state snapshot to the rendering thread instead of
handing it the display list again.

This is preparation for moving rendering to a separate process, where
sending the display list across the process boundary on every frame
would be expensive.
2026-05-11 22:52:38 +02:00
Timothy Flynn
b221d7fe8b LibWeb+LibWebView+WebContent+UI: Add an action to cut text 2026-05-07 09:13:06 -04:00
Jelle Raaijmakers
b488b7e7b1 LibWeb: Propagate line box borders debug toggle to child navigables
Only the top navigable received this setting; existing and new
descendant navigables never showed these debug boxes.
2026-05-05 23:47:23 +02:00
Shannon Booth
fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00
Aliaksandr Kalenik
ad2ee4fe7a LibWeb: Rasterize each Navigable independently on its own thread
Previously, iframes were rasterized synchronously as nested display
lists inside their parent's display list: the parent's paint walk called
record_display_list() on each hosted iframe document and emitted a
PaintNestedDisplayList command that the player would recurse into. Only
the top-level traversable's RenderingThread was ever active, even though
every Navigable already owned one.

The motivation for splitting this apart:
- Work in the outer document no longer has to be re-recorded when only
  an iframe changes. The parent's cached display list now references the
  iframe's rasterized output live via an ExternalContentSource, so an
  iframe invalidation just needs the parent's display list replayed, not
  re-recorded.
- Each iframe now has a self-contained rasterization pipeline, which is
  prep work for moving iframes into separate sandboxed processes.
2026-04-07 15:09:43 +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
Aliaksandr Kalenik
df96b69e7a LibWeb: Replace spin_until in HTMLParser::the_end() with state machine
HTMLParser::the_end() had three spin_until calls that blocked the event
loop: step 5 (deferred scripts), step 7 (ASAP scripts), and step 8
(load event delay). This replaces them with an HTMLParserEndState state
machine that progresses asynchronously via callbacks.

The state machine has three phases matching the three spin_until calls:
- WaitingForDeferredScripts: loops executing ready deferred scripts
- WaitingForASAPScripts: waits for ASAP script lists to empty
- WaitingForLoadEventDelay: waits for nothing to delay the load event

Notification triggers re-evaluate the state machine when conditions
change: HTMLScriptElement::mark_as_ready, stylesheet unblocking in
StyleElementBase/HTMLLinkElement, did_stop_being_active_document, and
DocumentLoadEventDelayer decrements. NavigableContainer state changes
(session history readiness, content navigable cleared, lazy load flag)
also trigger re-evaluation of the load event delay check.

Key design decisions and why:

1. Microtask checkpoint in schedule_progress_check(): The old spin_until
   called perform_a_microtask_checkpoint() before checking conditions.
   This is critical because HTMLImageElement::update_the_image_data step
   8 queues a microtask that creates the DocumentLoadEventDelayer.
   Without the checkpoint, check_progress() would see zero delayers and
   complete before images start delaying the load event.

2. deferred_invoke in schedule_progress_check():
   I tried Core::Timer (0ms), queue_global_task, and synchronous calls.
   Timers caused non-deterministic ordering with the HTML event loop's
   task processing timer, leading to image layout tests failing (wrong
   subtest pass/fail patterns). Synchronous calls fired too early during
   image load processing before dimensions were set, causing 0-height
   images in layout tests. queue_global_task had task ordering issues
   with the session history traversal queue. deferred_invoke runs after
   the current callback returns but within the same event loop pump,
   giving the right balance.

3. Navigation load event guard (m_navigation_load_event_guard): During
   cross-document navigation, finalize_a_cross_document_navigation step
   2 calls set_delaying_load_events(false) before the session history
   traversal activates the new document. This creates a transient state
   where the parent's load event delay check sees the about:blank (which
   has ready_for_post_load_tasks=true) as the active document and
   completes prematurely.
2026-03-28 23:14:55 +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
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
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
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
Tim Ledbetter
5060a61081 LibWeb: Align Navigable::reload() with the specification 2026-02-14 20:21:53 +01:00
Jelle Raaijmakers
c56c933014 LibWeb+WebContent: Instantly update DPI for painting and media queries
If our UI informed the page of a DPI change, we would store the new
device pixel ratio and leave it at that. It would take a layout/style
update (e.g. by clicking the page) to actually render the page using the
new DPI. This is very visible on macOS when moving the Ladybird window
from a 1x resolution monitor to a HiDPI 2x monitor.

We now instantly update the backing stores and mark media queries for
reevaluation. Moving the Ladybird window on macOS now immediately
updates the page when dragging it to a HiDPI monitor.
2026-02-06 09:01:26 +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
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
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
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
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
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
Aliaksandr Kalenik
ed9c0c1e09 LibWeb: Don't visit registered NavigationObserver from Navigable
NavigationObserver register itself in Navigable from constructor and
unregister itself from `finalize()`. The problem is that `finalize()`
won't be invoked for as long as NavigationObserver is visited by
Navigable, leading to GC leaks.
2025-11-07 04:08:30 +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
Andreas Kling
dbf041aa98 LibWeb: Ignore repaint requests inside iframes with visibility: hidden
This reduces idle CPU usage on https://gymgrossisten.com/ from 100%
(split between WebContent and Ladybird) to ~4% on my machine.
2025-10-23 17:42:37 +02: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
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
Aliaksandr Kalenik
835081d66e LibWeb+LibWebView+WebContent: Reset pinch-to-zoom state on Ctrl/Cmd+0 2025-10-10 15:37:45 +02:00
Aliaksandr Kalenik
b477c6bfc4 LibWeb: Implement VisualViewport aware viewport scrolling
Implements spec algorithm for viewport scrolling that first checks if
it's possible to use delta to move the visual viewport before falling
back to scrolling the layout viewport. This is a part of pinch-to-zoom
support.
2025-10-10 15:37:45 +02:00
Timothy Flynn
efa9311527 LibWeb+WebContent+UI: Port text pasting to UTF-16 2025-09-19 06:38:52 -04:00
Jelle Raaijmakers
26b5fa348c LibWeb: Make Navigable::active_document() const 2025-09-12 15:34:09 -04:00
Psychpsyo
56739b4b16 LibWeb: Implement plumbing for view transitions
This implements large parts of the CSS view-transitions-1 spec.
2025-09-07 13:58:05 +01:00
ayeteadoe
3df8e00d91 LibWeb: Enable EXPLICIT_SYMBOL_EXPORT 2025-08-23 16:04:36 -06:00
Sam Atkins
cfdc4bad7c LibWeb: Update more navigation code to spec
Partly corresponds to 80ebad5fbf

This is mostly to handle null source_documents, which is something that
needs more work elsewhere. The spec change above is about the deferred
fetch quota.
2025-08-11 11:08:04 +01: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