Commit graph

1724 commits

Author SHA1 Message Date
Psychpsyo
44ef574902 LibWeb: Properly set visibility state for nested documents
This cannot happen inside the Make Active algorithm, since that gets
called during document creation, which commonly happens before the
document's navigable is created.

Aligns us with a recent spec change and rids us of some AD_HOC
behavior.
2026-04-01 17:26:46 +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
Zaggy1024
1467127d35 LibMedia+LibWeb: Disable audio output in headless mode
Audio output on macOS was consuming Core Audio resources until the
PlaybackStream creation took well over the timeout for some tests.
This was observed in media-source-buffered.html, where it would time
out due to the long-running callback on the main thread to create the
PlaybackStream for AudioMixingSink.

However, the AudioUnit init should definitely not be blocking the main
thread, so I've added a FIXME there.
2026-04-01 02:54:22 -05:00
Zaggy1024
94be6c7611 LibWeb: Prevent a crash when triggering media load in the error handler
If we fire the error event synchronously within the on_error callback,
then we'll end up destroying the PlaybackManager inside its own
callback and crash. Instead, queue a task to execute the error steps.

This could happen with or without MSE, but I observed it occurring on
YouTube with MSE when we hit a decoding error, since they immediately
try another source when an error is reported.
2026-04-01 02:54:22 -05:00
Zaggy1024
51c3f7c41e LibWeb: Implement appending and demuxing WebM MSE segments
The segments are parsed for the SourceBufferProcessor by the
WebMByteStreamParser. It parses the initialization segment to update
its internal set of tracks, then SourceBufferProcessor/SourceBuffer set
them up for playback. When a media segment is received, it also parses
as much of it as is available, returning all the coded frames found so
far. SourceBufferProcessor then tells TrackBufferDemuxer to remove any
overlapping frames and insert the new ones.

TrackBufferDemuxer implements the Demuxer interface in terms of the
coded frame store maintained by the SourceBufferProcessor. It returns
the frames in decode order when requested by a data provider. When a
is needed, it finds the keyframe prior to the target timestamp, and
checks that there are no gaps in data up to the target timestamp. If
there are any gaps, it blocks until the gaps are gone.
2026-04-01 02:54:22 -05:00
Zaggy1024
d960c1eaf5 LibWeb: Implement assignment of MediaSource to HTMLMediaElement src
Also, support the srcObject attribute, and fire the sourceopen event
at the MediaSource.
2026-04-01 02:54:22 -05:00
Zaggy1024
6dcfe20f1e LibWeb: Rename HTMLMediaElement::FetchData -> RemoteFetchData
This struct is only used for remote resources, so let's make that
clearer before adding local resource support in the form of
MediaSources.
2026-04-01 02:54:22 -05:00
Zaggy1024
a41e1ad080 LibWeb: Include a comment for media src attribute and match spec
We're apparently not supposed to load anything when the attribute is
removed.
2026-04-01 02:54:22 -05:00
Zaggy1024
6034a93c83 LibWeb: Expose PlaybackManager's buffered range on the media element 2026-04-01 02:54:22 -05:00
Zaggy1024
f3832c0b36 LibWeb: Use MSE steps to update the media element's ready state
These steps are the best definition we have for how the ready state
should be set, and it seems to be reasonable to apply to plain file
playback as well.

Since our file demuxers are hardcoded to return the entire duration as
buffered, the ready state immediately progresses to HAVE_CURRENT_DATA.
This will probably change once we can check the demuxers for buffered
data.
2026-04-01 02:54:22 -05:00
Zaggy1024
29db875b7f LibWeb: Don't remove video displays on media element finalization
Removing a display risks triggering callbacks on the playback manager
that may cause a recursive GC. This wasn't having any effect since the
playback manager became an OwnPtr.
2026-04-01 02:54:22 -05:00
Zaggy1024
b4db8f11c5 LibMedia+LibWeb: Align Media::Track more to the web spec
...giving tracks a kind attribute, and renaming name to label.

Demuxers will need to determine the kind attribute, since the spec for
sourcing tracks requires us to select based on info we don't expose.
2026-04-01 02:54:22 -05:00
Zaggy1024
9664c11c15 LibMedia+LibWeb: Remove an unnecessary parameter from on_track_added
The TrackType parameter is redundant, since the actual Track object
already contains it.
2026-04-01 02:54:22 -05: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
e20fd9ba74 LibWeb: Simplify iframe sandbox attribute lookup
Fold the has_attribute and attribute call into an if statement
initializer.
2026-04-01 04:41:11 +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
5a7ef7d494 LibWeb: Handle null active document in content_document()
The Crash/HTML/image-load-after-iframe-navigated.html test was
crashing on CI with a null pointer dereference at
NavigableContainer.cpp:178. The crash occurs because content_document()
dereferences the return value of active_document() without checking for
null.

When an iframe is navigated, Document::destroy() sets the old
document state's document to null via set_document(nullptr), but
the navigable (m_content_navigable) remains non-null since it is
reused for the new navigation. During the window between the old
document being destroyed and the new document being set,
active_document() returns null. If JS code accesses
iframe.contentDocument during this window (e.g. via a timer
callback), content_document() would dereference the null pointer.
2026-03-31 18:31:53 +02:00
Callum Law
75ecdaca41 LibWeb: Use document LRC in normalize_source_densities 2026-03-31 10:06:18 +02:00
Aliaksandr Kalenik
baecba0d08 LibWeb: Remove unused spin_processing_tasks_with_source_until()
This function is no longer used after apply_the_history_step was
converted to use an event-driven state machine.
2026-03-31 09:47:59 +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
Dylan Hart
c710ff5afa LibWeb: Fire textarea input events immediately instead of debouncing
The 100ms debounce timer on textarea input events causes character
loss when JavaScript restores a previously captured value via
requestAnimationFrame. The text node mutation happens immediately
during input processing, but the input DOM event is delayed,
creating a window where stale rAF callbacks overwrite new input.

Remove the debounce timer and fire the input event immediately
via queue_an_element_task, matching HTMLInputElement behavior.
The spec notes this delay is optional ("User agents may wait").

Fixes #7793.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:17:47 -04:00
Adam Colvin
f79c3a4bfe LibWeb: Implement exportparts forwarding in part element map
Implement the forwarded part names step of the CSS Shadow Parts
spec in ShadowRoot::calculate_part_element_map(). When a shadow
host has an exportparts attribute, the inner shadow root's part
element map is consulted and matching parts are added to the
outer shadow root's map under the exported name.

This supports both shorthand same-name forwarding (exportparts=
"foo") and renamed forwarding (exportparts="foo: bar"), and
chains transitively through nested shadow boundaries via
recursive part_element_map() calls.

Fixes 4 WPT tests: simple-forward, simple-forward-shorthand,
double-forward, and precedence-part-vs-part.
2026-03-30 16:47:34 +01:00
Callum Law
0219eb2ef9 LibWeb: Remove FooOrCalculated classes
These are unused since we now store values as `StyleValue`s before
used-value time, and as their resolved type (e.g. CSSPixels) after
2026-03-30 14:05:10 +01:00
Callum Law
f2a8099d13 LibWeb: Parse sizes attribute as StyleValue
Gets us a step closer to removing the `FooOrCalculated` classes
2026-03-30 14:05:10 +01:00
Guilherme Mendes
2064bde5f9 LibWeb: Add case when <br> has display other than 'none'
When <br> element style display is not 'none', it must be an inline box.
Add a condition to ensure <br> is treated as an inline element
instead of a table, flex, or grid in that case,
preventing program from crashing.
Fixes #5568
2026-03-30 12:34:46 +01:00
Dylan Hart
9be298e39c LibWeb: Use document resolution context in canvas set_font
When a canvas belongs to a detached document (e.g. one created via
document.implementation.createHTMLDocument()), document->window()
returns null, causing a null pointer crash in set_font.

Use Length::ResolutionContext::for_document() instead of for_window(),
which handles the no-navigable case gracefully and is already the
recommended pattern (per existing FIXME in Length.h). This also fixes
the same crash path via fillText, strokeText, and measureText which
trigger lazy font initialization through set_font.

Fixes #8515.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:24:29 +02:00
Tim Ledbetter
f601c0cd4b LibWeb: Disable Navigation API after document.open() on about:blank
This matches the behavior of other engines.
2026-03-29 16:39:03 +02:00
InvalidUsernameException
7002c47ce1 LibJS+LibWeb: Pass constants into execution context constructor
The additional data being passed will be used in an upcoming commit.
Allows splitting the churn of modified function signatures from the
logically meaningful code change.

No behavior change.
2026-03-29 13:44:06 +02:00
Jenn Barosa
8808a54f37 LibWeb: Fix session history replacement for superseded navigations 2026-03-29 03:11:14 +02:00
Aliaksandr Kalenik
76d9cc4baf LibWeb: Replace spin_until in execute_script with deferred parser start
HTMLScriptElement::execute_script() and SVGScriptElement had spin_until
calls waiting for ready_to_run_scripts to become true. The race exists
because load_html_document() resolves the session history signal and
starts the parser in the same deferred_invoke — so the parser can hit a
<script> before update_for_history_step_application() sets the flag.

Instead of spinning, defer parser->run() until the document is ready.
Document gains a m_deferred_parser_start callback that is invoked when
set_ready_to_run_scripts() is called. The callback is cleared before
invocation to avoid reentrancy issues (parser->run() can synchronously
execute scripts). All three document loading paths (HTML, XML, text)
now check ready_to_run_scripts before starting the parser and defer if
needed.

create_document_for_inline_content() (used for error pages) now calls
set_ready_to_run_scripts() before mutating the document, ensuring the
invariant holds for all parser paths.

The spin_until calls are replaced with VERIFY assertions.
2026-03-29 01:05:35 +01: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
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
Sam Atkins
ed6a5f25a0 LibWeb: Implement scoped custom element registries 2026-03-27 19:49:55 +00:00
Sam Atkins
71626ac58c LibWeb: Update "look up a custom element definition" algorithm
This now takes a registry directly. As a temporary stop-gap, the old
method on Document is still here and calls the new one.

We do get a test regression:

"Creating an element in a cloned document and inserting into the
document must not enqueue a custom element upgrade reaction" in
"custom-elements/upgrading.html".

Possibly a spec bug?
2026-03-27 19:49:55 +00:00
Sam Atkins
235534b9da LibWeb/DOM: Return Document's CustomElementRegistry from Window
Window no longer has a CustomElementRegistry of its own.
2026-03-27 19:49:55 +00:00
Sam Atkins
ecbd846272 LibWeb/DOM: Implement DocumentOrShadowRoot.customElementRegistry
Step towards scoped custom element registries.
2026-03-27 19:49:55 +00:00
Andreas Kling
d7bf9d3898 LibRegex: Remove the legacy C++ ECMA-262 engine
Delete the old C++ ECMA-262 parser, optimizer, and matcher now that all
in-tree users compile and execute through `ECMAScriptRegex`.

Stop building the legacy engine, remove its source files and the
POSIX-only fuzzers that depended on it, and update the remaining
LibRegex tests to target the Rust-backed facade instead of the deleted
implementation. Clean up the last includes, comments, and helper paths
that only existed to support the old backend.

After this commit LibRegex has a single ECMAScript engine in-tree,
eliminating duplicated maintenance and unifying future regex work.
2026-03-27 17:32:19 +01:00
Andreas Kling
34d954e2d7 LibRegex: Add ECMAScriptRegex and migrate callers
Add `ECMAScriptRegex`, LibRegex's C++ facade for ECMAScript regexes.

The facade owns compilation, execution, captures, named groups, and
error translation for the Rust backend, which lets callers stop
depending on the legacy parser and matcher types directly. Use it in the
remaining non-LibJS callers: URLPattern, HTML input pattern handling,
and the places in LibHTTP that only needed token validation.

Where a full regex engine was unnecessary, replace those call sites with
direct character checks. Also update focused LibURL, LibHTTP, and WPT
coverage for the migrated callers and corrected surrogate handling.
2026-03-27 17:32:19 +01:00
Jelle Raaijmakers
450c15c63b LibWeb: Finalize session history entry during async sniff bytes
In e97de2e7e3 we added a guard to prevent
crashes, but we should've still finalized the session history entry.
2026-03-27 05:18:30 +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
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
Jelle Raaijmakers
a5000d07c0 LibWeb: Prevent running permanently unrunnable tasks in EventLoop
In `::spin_processing_tasks_with_source_until()`, we would first take a
set of tasks based on a filter, and then run them one by one. If there
was more than one task matched and put in that vector, they could
interfere with each other's runnability by making later tasks
permanently unrunnable.

The `::take_tasks_matching()` API is a footgun - remove it in favor of
an API that takes tasks one by one, performing the runnability check
just in time.
2026-03-26 18:48:27 +01:00
Jelle Raaijmakers
f5d76ec2d0 LibWeb: Simplify TaskQueue::remove_tasks_matching()
No functional changes.
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
Tim Ledbetter
cbd01b8efc LibWeb: Use fallible FormAssociatedElement cast in form elements filter
The `HTMLFormControlsCollection` filter iterates all descendants of the
form's root, which may include non HTMLElement elements such as SVG
elements. The unchecked `as<FormAssociatedElement>` cast would crash on
these elements. Use `as_if` with a null check instead.

This fixes a regression introduced in 9af3e34875.
2026-03-26 08:31:00 +01:00
Glenn Skrzypczak
6074424486 LibWeb/HTML: Update the button activation behaviour steps
This commit updates the activation behaviour steps of the button
element in order to properly determine if a command is valid for
a target.
2026-03-25 23:14:10 +01:00
Glenn Skrzypczak
88167149f6 LibWeb/HTML: Properly get the attr-associated element
This replaces ad-hoc implementations of the get the attr-associated
element steps with a call to the proper implementation. This fixes
issue #8585.
2026-03-25 23:14:10 +01:00