Encode transfer-data attachments as raw IPC attachments instead of first
rewrapping them as IPC::File values.
This is preparatory refactoring for the upcoming Mach-port transport
introduction on macOS, where attachments should remain transport-native
rather than being normalized through file descriptors.
Implement the accessKeyLabel IDL attribute per the HTML spec.
We mimic Chromium's behavior and treat the accesskey attribute value as
a single key rather than splitting on whitespace, since no browser
besides IE/Edge implemented the spec's splitting approach.
See https://github.com/whatwg/html/issues/3769
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.
Depending on the underlying implementation of the storage bottle, we
have to do IPC calls to get either the size or keys. We crash on an
out-of-bounds if the bottle changes size after the bounds check in step
1. Fix this by obtaining the keys immediately and using that for the
bounds check.
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.
Matching the behaviour of the other big three browser engines,
also tested by WPT, but does not appear to be explicitly specified.
I also noticed when investigating that browser engines disagree
about whether a child mutation should trigger script execution.
For this, I've just gone with the behaviour of Webkit/Chromium of
not running script.
The IDL-generated getter for ImageData.data lives on ImageDataPrototype,
which means every access goes through a getter call that cannot be
cached by the GetById inline cache.
By also storing the Uint8ClampedArray as an own data property directly
on each ImageData instance, the prototype getter is shadowed and
GetById can cache the access like any other data property. This matches
the approach used by WebKit and is compatible with web expectations.
Replace the BytecodeFactory header with cbindgen.
This will help ensure that types and enums and constants are kept in
sync between the C++ and Rust code. It's also a step in exporting more
Rust enums directly rather than relying on magic constants for
switch statements.
The FFI functions are now all placed in the JS::FFI namespace, which
is the cause for all the churn in the scripting parts of LibJS and
LibWeb.
Chromium and Firefox skip calling the activation behavior on the first
click, so that double clicking toggles checkboxes twice.
Also, activation behaviors may be triggered by spacebar in the future,
so this check is limited to click events.
This moves normal/double/triple click checking into WebContent, the
client only has to send a click count in order to activate a double
or triple click in the content. This means that the AppKit UI will no
longer fire multiple double clicks when clicking in place more than 3
times. This matches the behavior of other browsers on macOS.
We will now also fire the click event regardless of whether a dblclick
event will follow, as the spec requires.
We were conflating elements being the active element and elements being
activated. The :active pseudo class is supposed to be based on whether
an element will have its activation behavior run upon a button being
released.
Store whether an element is being activated as a flag that is set/reset
by EventHandler.
Doing this allows label elements to visually activate their control
without doing a weird paintable hack, so the Labelable classes have
been yeeted.
The media element's timeupdate event only fires every 250ms. To make
the timeline update more smoothly, poll currentTime to update the
timeline every frame.
Storage objects are created lazily when window.localStorage or
window.sessionStorage is first accessed. Previously, broadcast()
iterated over already-created Storage objects, so windows that had never
accessed these properties would not receive storage events.
Fix this by iterating over all active windows and initializing Storage
objects as part of the broadcast loop so all eligible windows receive
the event regardless of whether they had previously accessed
their storage property.
The microtask queue is a pure FIFO (enqueue at back, dequeue from
front) but was using a Vector, making every dequeue O(n) due to
element shifting.
Replace it with AK::Queue which has O(1) dequeue. This makes a huge
difference when processing large numbers of microtasks, e.g. during
async-heavy JavaScript workloads where each `await` generates a
microtask.
Also add a for_each() method to AK::Queue so the GC can visit the
queued tasks.
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
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.
Before, we could re-enter into this method and it would clear the skip
flag once the innermost call would finish. Instead, remember what the
flag's original value was and restore that.
On macOS, Objective-C methods frequently return autoreleased objects
that accumulate until an autorelease pool is drained. Our event loop
(Core::EventLoop) and rendering thread both lacked autorelease pools,
causing unbounded accumulation of autoreleased objects.
The rendering thread was the worst offender: every Skia flush triggers
Metal resource allocation which sets labels on GPU textures via
-[IOGPUMetalResource setLabel:], creating autoreleased CFData objects.
With ~1M+ such objects at 112 bytes each, this leaked ~121MB. Metal
command buffer objects (_MTLCommandBufferEncoderInfo, etc.) also
accumulated, adding another ~128MB.
Add Core::ScopedAutoreleasePool, a RAII wrapper around the ObjC runtime
autorelease pool (no-op on non-macOS), and drain it:
- Every event loop pump (like NSRunLoop does)
- Every compositor loop iteration on the rendering thread
Previously, `create_paired()` returned two full Transport objects, and
callers would immediately call `from_transport()` on the remote side to
extract its underlying fd. This wasted resources: the remote
Transport's IO thread, wakeup pipes, and send queue were initialized
only to be torn down without ever sending or receiving a message.
Now `create_paired()` returns `{Transport, TransportHandle}` — the
remote side is born as a lightweight handle containing just the raw fd,
skipping all unnecessary initialization.
Also replace `release_underlying_transport_for_transfer()` (which
returned a raw int fd) with `release_for_transfer()` (which returns a
TransportHandle directly), hiding the socket implementation detail
from callers including MessagePort.
Replace IPC::File / AutoCloseFileDescriptor / MessageFileType in
the IPC message pipeline with a new IPC::Attachment class. This
wraps a file descriptor transferred alongside IPC messages, and
provides a clean extension point for platform-specific transport
mechanisms (e.g., Mach ports on macOS) that will be introduced later.
The timer nesting level throttling change (7577fd2a57) inadvertently
reverted the repeating timer optimization from 4d27e9aa5e, causing
setInterval() to use single-shot timers that re-arm after the callback
completes. This meant the next firing was scheduled relative to callback
completion rather than the previous fire time, causing drift
proportional to callback execution time.
For example, DiabloWeb's 50ms game loop with ~20ms of WASM work per
frame was only achieving 14 FPS (71ms intervals) instead of 20 FPS.
Fix this by using repeating Core::Timer for setInterval() again. The
repeating timer fires on schedule regardless of callback duration. On
re-arm, we update the callback (for nesting level changes) and the
interval (in case nesting level clamping kicks in), but don't restart
the timer since it's already running on the correct schedule.
Instead of passing RequestServer and ImageDecoder socket FDs as
command-line arguments to WebWorker, send them over the main IPC channel
after launch. The worker-agent handoff now carries all three transport
handles (worker, RequestServer, ImageDecoder) so the connection path
matches WebContent.
Add IPC::TransportHandle as an abstraction for passing IPC
transports through .ipc messages. This replaces IPC::File at
all sites where a transport (not a generic file) is being
transferred between processes.
TransportHandle provides from_transport(),
clone_from_transport(), and create_transport() methods that
encapsulate the fd-to-socket-to-transport conversion in one
place. This is preparatory work for Mach port support on
macOS -- when that lands, only TransportHandle's internals
need to change while all .ipc definitions and call sites
remain untouched.
Our implementation of image decoding and its effect on
presenting/rendering the DOM is already effectively "async". The value
"auto" is implementation-defined, but should likely default to async
behavior as well. That leaves us with `decoding="sync"`, for which we
should still show a FIXME.
Replace per-frame heap-allocated RefCounted ScrollFrame objects with a
single contiguous Vector<ScrollFrame> inside ScrollState. All frames for
a viewport are now stored in one allocation, using type-safe
ScrollFrameIndex instead of RefPtr pointers.
This reduces allocation churn, improves cache locality, and moves
parent-chain traversal (cumulative offset, nearest scrolling ancestor)
into ScrollState — similar to how visual context nodes were recently
consolidated into AccumulatedVisualContextTree.
The invalidate_style() calls on text nodes in did_receive_focus() and
did_lose_focus() were no-ops since Node::invalidate_style() returns
early for character data nodes. Replace them with set_needs_repaint()
which properly invalidates the containing PaintableWithLines' paint
cache, ensuring selection highlights are cleared and the caret is
repainted when switching focus between text controls.
Fixes#8363
Consolidate the repeated socketpair + adopt + configure pattern from
4 call sites into a single Transport::create_paired() factory method.
This fixes inconsistent error handling and socket configuration across
call sites, and prepares for future mach port support on macOS.
Replace the 16-byte Variant<Empty, GC::Ref<Script>, GC::Ref<Module>>
with a simple 8-byte GC::Ptr<Cell> that points to either a Script or
Module (or is null for Empty).
A helper function script_or_module_from_cell() converts back to the
full ScriptOrModule variant when needed (e.g. in
VM::get_active_script_or_module).
The arguments Span (pointer + size = 16 bytes) was always derivable
from the tail array layout: data = values + (total_count - arg_count).
Replace it with a u32 argument_count and derive the span on demand
via arguments_span() / arguments_data() accessors.
Shrinks ExecutionContext from 136 to 120 bytes.
This field was only used by LibWeb to prevent GC collection of the
EnvironmentSettingsObject while its execution context is on the stack.
This is unnecessary because the ESO is already reachable through the
realm's host_defined pointer: EC -> realm -> host_defined ->
PrincipalHostDefined -> environment_settings_object.
Shrinks ExecutionContext from 152 to 144 bytes.
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.
Extract the repeated pattern of transforming a rectangle from absolute
coordinates to viewport coordinates via the accumulated visual context
into a helper method.
Previously if you had a selection and you pressed the left/right arrow
key it would collapse the selection *and* perform the movement. This is
not how most text editors work and felt unnatural.