The synchronous wheel path treats wheel deltas as CSS-pixel scroll
distances, while the async compositor scroll tree mutates scroll state
stored in device pixels. Passing the same unscaled delta into the
compositor made async scrolling advance too little whenever
device pixels per CSSPixel was greater than 1, so scrolling felt slower
than with async scrolling disabled.
Convert wheel deltas before crossing the compositor boundary and make
the compositor IPC carry only the device-pixel position and delta it
needs. This keeps AsyncScrollTree device-pixel native and makes async
viewport scrolling match the synchronous path across high-DPI displays
and page zoom levels.
Exit WebContent immediately when either browser-side IPC peer
disconnects. Plumb Unix process exit status through LibWebView so the
browser process can tell clean owner-driven shutdown apart from renderer
crashes.
This keeps nonzero exits and signal deaths reported as crashes, while
letting status 0 exits disappear without making test-web report the page
as crashed.
Trace the compositor state transitions that matter once presentation and
async scrolling share one path: page registration, backing-store swaps,
did_paint delivery, ready_to_paint acknowledgements, wheel admission,
and deferred presents waiting for reusable bitmaps.
Keep the logs under COMPOSITOR_DEBUG and use the [Compositor] prefix
consistently. The trace is about ownership and backpressure instead of
dumping every wheel event detail, so stalls can be diagnosed without
reintroducing noisy async-scrolling-only logging.
Send the experimental UI-process wheel bypass to WebContent over the
Compositor IPC connection instead of adding a separate async scrolling
transport. This keeps bypass admission on the channel that owns bitmaps
and ready_to_paint acknowledgements.
If the compositor rejects the wheel, LibWebView falls back to regular
input delivery. If it accepts, mark the queued DOM event as having
already performed its default action so dispatch preserves ordering
without scrolling twice.
Use the snapshot from the previous commit to let CompositorThread apply
experimental viewport wheel deltas when async scrolling is enabled. The
event handler first performs synchronous admission on the main thread,
then enqueues a compositor scroll command instead of mutating live
document scroll state directly.
Rasterize accepted scrolls through the same compositor presentation
path added earlier. The compositor stores the newest async viewport
offset so the next main-thread display-list recording can adopt it
before repainting, preventing older paints from snapping the visible
position backward.
Keep DOM wheel dispatch on the main thread. When the compositor already
performed the default action, dispatch the wheel as non-cancelable and
suppress a second default scroll. Non-viewport targets, nested
scrollers, and pages with blocking wheel listeners stay synchronous.
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.
Move HeapTimer out of LibWeb and into LibGC as GC::Timer, inheriting
from GC::Cell instead of JS::Cell. Add a finalize() override that
stops the timer, ensuring it is cleaned up during GC finalization.
Previously we were inconsistent by generating code for enum definitions
but not generating code for dictionaries. With future changes to the
IDL generator to expose helpers to convert to and from IDL values
this produced circular depdendencies. To solve this problem, also
generate the dictionary definitions in bindings headers.
This commit splits out synchronization primitives from LibThreading into
LibSync. This is because LibThreading depends on LibCore, while LibCore
needs the synchronization primitives from LibThreading. This worked
while they were header only, but when I tried to add an implementation
file it ran into the circular dependency. To abstract away the pthread
implementation using cpp files is necessary so the synchronization
primitives were moved to a separate library.
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.
We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
Attach cached JavaScript bytecode sidecars to HTTP response headers so
WebContent can materialize classic and module scripts directly from a
decoded cache blob on cache hits.
Carry the disk cache vary key with the sidecar and reuse it when storing
fresh bytecode, avoiding mismatches against the augmented network
request headers used to create the cache entry.
Keep CORS-filtered module responses intact for status, MIME, and script
creation checks. Read bytecode sidecar data only from the internal
response, and treat decode or materialization failure as a cache miss
that falls back to normal source compilation.
Schedule JavaScript bytecode cache generation after downloaded classic
scripts and modules have been handed back to the main thread.
The cache job reparses and fully compiles on the thread pool,
serializes the bytecode blob, and stores it as HTTP cache sidecar data.
RequestServer finalizes the disk cache entry before notifying
WebContent, so the script fetcher can attach the sidecar immediately.
Add RequestServer messages and LibRequests helpers for storing and
retrieving HTTP cache sidecar data.
Transfer sidecar payloads as anonymous buffers so large JavaScript
bytecode blobs do not have to be serialized into IPC message bodies.
Add the same connect timeout used by regular requests to curl-backed
websockets, buffer unsent websocket bytes, and resume them from a write
notifier instead of retrying curl_easy_send() in a tight loop.
This prevents CPU spins on CURLE_AGAIN and keeps large websocket sends
from hanging when the peer applies backpressure.
Convert DNS failures and connecting-stage close requests into terminal
websocket state changes, and remove websocket entries once they close.
This keeps clients from getting stuck in CONNECTING and makes early
cancelled or failed websocket attempts terminate like the other close
paths.
The Take Screenshot and Take Element Screenshot endpoints add a callback
to the animation frame driver and rely on it firing to encode and return
the framebuffer. Since rendering updates moved to on-demand scheduling,
that callback never runs unless something else happens to request a
frame, so the WebDriver client times out waiting for the response.
Request a frame after registering the callback, matching what
Window::request_animation_frame() does for the JS-exposed API.
Add --dump-style-invalidation-counters=N to Ladybird and propagate it
to WebContent helper processes.
When enabled, WebContent dumps the current document style invalidation
counters with dbgln() after every N recorded style invalidations. This
makes it possible to collect the counters while browsing without adding
temporary C++ logging.
Previously a fixed-rate paint refresh timer kept queueing rendering
update tasks at the maximum configured frame rate, regardless of whether
anything had actually changed. This wasted CPU on idle pages, which
spend most of their time in a steady state where no style, layout, or
paint work is needed.
Replace the repeating timer with a single-shot frame timer driven by
PageClient::request_frame(). A rendering update is now scheduled only
when something requires one. The configured maximum frame rate is
preserved as a ceiling on how closely consecutive frames can follow each
other.
Fixes flakiness in worker tests that create a Worker or SharedWorker
with a missing script URL and only attach an error handler to it.
Once the test callback returns, nothing keeps the worker rooted from
JavaScript. If GC ran before the WebWorker process reported the
script fetch failure, the Worker/WorkerAgentParent cycle could be
collected and the error event never delivered, leaving the test hung
until timeout.
Hold startup-pending WorkerAgentParents from the outside
EnvironmentSettingsObject and release that edge once the script load
succeeds, fails, or the worker closes. The worker now survives long
enough to deliver its first script-load result.
handle_connect_state (used by <link rel=preconnect>) attached easy
handles to the multi without setting CURLOPT_RESOLVE, so libcurl spawned
its own threaded resolver for them. When the system stub resolver was
slow, the thread got stuck and the next ~Request pthread_join'd it on
the main thread for many seconds.
Route Connect-mode requests through the same DNS + CURLOPT_RESOLVE path
that Fetch uses, so libcurl never spawns the thread.
Per-request and per-connection logging that surfaces enough detail
to diagnose where time goes when a page load misbehaves. Gated by a
new REQUESTSERVER_WIRE_DEBUG cmakedefine.
Documentation/RequestServerWireLogging.md describes each label
(wire/wire+/wire++/wire^, wire-batch, wire-stall, wire-burst,
wire-pipe-pressure, LibDNS wire-dns, UI wire-cookie) and how to read
them.
Previously, `AnonymousBuffer::create_with_size(0)` returned an error
because POSIX `mmap` rejects a zero length with `EINVAL`, and Windows
`CreateFileMapping` rejects a zero maximum size for an anonymous
mapping. This caused a crash when using `--headless=text` with zero
size pages like `about:blank`.
write_queued_bytes_without_blocking() used to allocate a Vector sized
to the entire queued response buffer and memcpy every queued byte into
it, on every curl on_data_received callback. When the client pipe was
slower than the network, the buffer grew, and each arriving chunk
triggered a full copy of everything still queued. With enough pending
data this added up to tens of gigabytes of memcpy per request and
stalled RequestServer for tens of seconds.
Drain the stream in a loop using peek_some_contiguous() + send()
directly from the underlying chunk, and discard exactly what the
socket accepted. No intermediate buffer, no copy. The loop exits on
EAGAIN and enables the writer notifier, matching the previous
back-pressure behavior.
When curl invokes the socket callback to tell us it only wants one
polling direction (e.g. CURL_POLL_IN without CURL_POLL_OUT, or vice
versa), we previously had no way to disable the other direction's
notifier. Once created, a notifier stayed enabled and kept firing
curl_multi_socket_action() for events curl was no longer interested in.
Merge the read and write branches into a single helper that also
disables the notifier for a direction when its CURL_POLL_* flag is
absent from the mask. This measurably improves performance by avoiding
redundant curl_multi_socket_action() calls on sockets curl has asked
us to stop watching in a given direction.
Passing the browser command line and executable path to every WebContent
process just in case we load about:version always felt a bit weird. We
now use the WebUI framework to load this information on demand.
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.
Route BroadcastChannel messages over IPC so matching channels can
receive them across WebContent and WebWorker processes, rather than only
within a single process.
Each channel now serializes its payload, sends it upward over IPC, and
receiving processes deliver it locally after matching by storage key and
channel name.
When we request the HTTP cookie for a SWR request, we were providing the
cookie to the standard request corresponding to the SWR request's ID.
This had two effects:
1. The SWR request would never finish.
2. If the corresponding standard request happened to be a connect-only
request, this would result in a crash as we were expecting it to have
gone through the normal fetch process.
This was seen on some articles on news.google.com.
The bytecode interpreter only needed the running execution context,
but still threaded a separate Interpreter object through both the C++
and asm entry points. Move that state and the bytecode execution
helpers onto VM instead, and teach the asm generator and slow paths to
use VM directly.
Generalize the backing store sharing abstraction into SharedImage, which
represents shared GPU memory independently of Skia and can be used to
share memory between different processes or different GPU contexts.
Split JS::ErrorData out of JS::Error so that it can be used both
by JS::Error and WebIDL::DOMException. This adds support for
Error.isError to DOMException, also letting us report DOMException
stack information to the console.
When a dedicated worker has an unhandled exception, we should propogate
that exception to be fired at the parent global. Fixes a timeout
in the included WPT test.
This prevents a race condition:
1. Try to connect a websocket
2. DNS lookup starts
3. JS causes the websocket to no longer be alive, and it is GCed
4. websocket_close() is called, but it doesn't find a websocket with
that websocket_id, so nothing happens
5. DNS lookup completes, and opens the websocket
6. This websocket never gets closed
By separately tracking which websockets we are trying to connect, we can
record the fact we tried to close it, and then the DNS lookup callback
can skip creating the now-unwanted websocket.
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>
Move MachPortServer from LibWebView into LibIPC as MachBootstrapListener
and move the Mach message structs from MachMessageTypes.h into LibIPC.
These types are IPC infrastructure, not UI or platform concerns.
Consolidating them in LibIPC keeps the Mach bootstrap handshake
self-contained in a single library and removes LibWebView's dependency
on LibThreading.
Previously, the bootstrap handshake used a two-state machine
(WaitingForPorts / WaitingForReplyPort) to handle a race: the parent
registering transport ports and the child sending a bootstrap request
could arrive in either order, so whichever came first stored its half
and the second completed the handshake.
Eliminate the race by holding a mutex across spawn() and
register_child_transport(). Since the child cannot send a bootstrap
request before it exists, and the lock isn't released until its
transport is registered, handle_bootstrap_request() is guaranteed to
find the entry. This reduces the pending map to a simple pid-to-ports
lookup and collapses the two-variant state into two straightforward
branches: known child, or on-demand (non-child) caller like WebDriver.
Now that LibIPC uses Mach ports for transport on macOS, IOSurface port
rights can be sent as regular IPC message attachments instead of through
a separate ad-hoc Mach message side-channel. Introduce
Web::SharedBackingStore that wraps either a MachPort (macOS) or
ShareableBitmap (other platforms) with IPC encode/decode support,
unifying backing store allocation into the existing
did_allocate_backing_stores IPC message.
Registering multiple Mach port names with the bootstrap server at
runtime is not how macOS expects it to be used — the bootstrap server
is meant for static services, and the only reason we used it originally
was so child processes could reach back to the UI process.
Remove bootstrap_transport_over_socket(), which had both sides register
dynamic names with the bootstrap server and exchange them over a socket.
Instead, WebDriver and BrowserProcess connections now go through
MachPortServer instances directly. When a non-child process contacts a
MachPortServer, the server creates a port pair on demand (detected via
sysctl ppid check) and returns the local half immediately. This keeps
bootstrap server usage limited to the one original case: child processes
looking up their parent's MachPortServer.
WebDriver Session now runs its own MachPortServer per session.
--webdriver-content-path becomes --webdriver-mach-server-name on macOS.
Spare WebContent launches are skipped when a WebDriver session is active
to avoid bootstrap races.
On macOS, use Mach port messaging instead of Unix domain sockets for
all IPC transport. This makes the transport capable of carrying Mach
port rights as message attachments, which is a prerequisite for sending
IOSurface handles over the main IPC channel (currently sent via a
separate out-of-band path). It also avoids the need for the FD
acknowledgement protocol that TransportSocket requires, since Mach port
right transfers are atomic in the kernel.
Three connection establishment patterns:
- Spawned helper processes (WebContent, RequestServer, etc.) use the
existing MachPortServer: the child sends its task port with a reply
port, and the parent responds with a pre-created port pair.
- Socket-bootstrapped connections (WebDriver, BrowserProcess) exchange
Mach port names over the socket, then drop the socket.
- Pre-created pairs for IPC tests and in-message transport transfer.
Attachment on macOS now wraps a MachPort instead of a file descriptor,
converting between the two via fileport_makeport()/fileport_makefd().
The LibIPC socket transport tests are disabled on macOS since they are
socket-specific.
Instead of immediately firing fullscreenchange, defer that until
WebContent's client has confirmed that it is in fullscreen for the
content. The fullscreenchange is fired by the viewport change, so in
cases where the fullscreen transition is instantaneous (i.e. the
fullscreen state is entered at the exact moment the viewport expands),
the resize event should precede the fullscreenchange event, as the spec
requires.
This fixes the WPT element-request-fullscreen-timing.html test, which
was previously succeeding by accident because we were immediately
fullscreenchange upon requestFullscreen() being called, instead of
following spec and doing the viewport (window) resize in parallel. The
WPT test was actually initially intended to assert that the
fullscreenchange event follows the resize event, but the WPT runner
didn't actually have a different resolution for normal vs fullscreen
viewports, so the resize event doesn't actually fire in their setup. In
our headless mode, the default viewport is 800x600, and the fullscreen
viewport is 1920x1080, so we do fire a resize event when entering
fullscreen. Therefore, that imported test is reverted to assert that
the resize precedes the fullscreenchange.
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 RequestPipe uses a socketpair for streaming response body data
from RequestServer to WebContent. On macOS, the default socket buffer
size for AF_LOCAL sockets is only ~8KB, which meant every read/write
syscall could only transfer ~8KB at a time. For large responses, this
resulted in thousands of tiny reads with significant per-read overhead.
Increase the send and receive buffer sizes to 512KB, matching the
approach already used by IPC::TransportSocket. This dramatically
improves throughput for large response bodies -- for example, fetching
a 25MB file from localhost went from ~850ms to ~25ms in testing.
I'm unsure exactly why this is possible, but using a freed GC pointer
on macOS was causing a segmentation violation to reach our signal
handler, but instead of exiting, the process would get stuck.
To solve this, when terminal signals are received, just exit() instead
of trying to let it get to the default handler. This allows test-web to
get unstuck in cases like this, and instead of timing out and leaving a
zombie, count the test as a crash.
In particular, the issue was caused by calling top_level_traversable()
on a null Navigable. Since GC::Ptr's null checks are debug-only, it was
trying to access garbage m_parent fields. With or without the signal
handlers, this would result in an (unsurprising) EXC_BAD_ACCESS, as
observed by attaching lldb. Regardless of debuggers being attached, or
signal handlers being enabled, after the signal, the process would get
stuck and refuse to exit, even after a SIGKILL. Sampling the stuck
processes didn't seem to indicate that the program counter was moving
in this state, so I'm unsure what causes it to get stuck.
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.
This fixes a race condition where a WebSocket would report a fatal
connection error instead of a clean close when the server dropped the
underlying connection immediately after sending a Close frame.
This fixes various timeouts in WPT, such as in:
https://wpt.live/websockets/Send-null.any.worker.html?wss
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.
Instead of passing RequestServer and ImageDecoder socket FDs as
command-line arguments to WebContent, send them over the main IPC
channel after launch. This unifies initial connection and reconnection
into a single code path.
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.
When a WebContent process receives a fatal signal, print a backtrace to
stderr before re-raising the signal. The backtrace is captured by
the test runner and written to a .stderr.html file.
Uses SA_RESETHAND so the handler runs only once, then resets to the
default disposition and re-raises the signal for normal crash behavior.
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.
Without IOSurface, the Metal rendering path introduced in #7956 hits a
VERIFY(iosurface_ref) failure and crashes on launch on Intel Macs.
The FIXME stated that the implementation of IOSurface does not work on
Intel macOS, but testing confirms it now works correctly.
Using a Promise in BackgroundAction was not doing anything since the
change to use a weak reference to the event loop, so let's just drop
that.
The thread will now always move itself (and therefore its callbacks)
over to the originating thread before completing, regardless of the
presence of callbacks. This ensures that ref counting remains on the
main thread.
In addition, BackgroundAction's completion callback can no longer
return errors. This functionality wasn't actually used anywhere, it was
a holdover from the behavior of Core::Promise.
WebView::FontPlugin was the only implementation of the abstract
FontPlugin base class. Its dependencies (LibGfx, LibCore) are
already visible to LibWeb.
Remove the virtual dispatch by making FontPlugin concrete and
absorbing the WebView::FontPlugin implementation directly.
EventLoopPluginSerenity was the only implementation of the abstract
EventLoopPlugin base class. Its methods simply wrapped Core::EventLoop
calls with GC function unwrapping.
Remove the virtual dispatch by making EventLoopPlugin concrete and
absorbing the EventLoopPluginSerenity implementation directly.
Request ids from a client connection must never be reused. This is also
enforced in the LibImageDecoderClient library by main thread asserts
around a monotonically increasing request id counter.
The Fetch spec defines HTTP whitespace as tab, LF, CR, and space.
Previously, trim_whitespace was also stripping vertical tab (U+000B)
and form feed (U+000C), which are not HTTP whitespace characters.
Switch to HTTP::normalize_header_value which matches the fetch
definition.
Fixes 4 subtests for WPT test:
https://wpt.live/cors/origin.htm
It's possible for the client (WebContent) to stop a request while the
request is waiting for an async callback to be invoked. So we cannot
assume the request itself will still be alive once these callbacks are
finally invoked.
We have a common pattern of creating a `WeakPtr<T>` from a reference and
passing that into a lambda, to then take the strong ref when the lambda
is executed. Add `weak_callback(Weakable, lambda)` that returns a lambda
that only invokes the callback if a strong ref exists, and passes it as
the first argument.
Stop converting between CSS and device pixels as part of rendering - the
display list should be as simple as possible, so convert to DevicePixels
once when constructing the display list.
Fixes the included imported test. Note that this required a minor
edit of the WPT import to work with our test harness setup to
try and create a non secure context setup as both file:// and
localhost are considered secure contexts.
These IPC methods should be expanded in the future to allow WebContent
to specify what UI elements should be kept/removed, for example, the
navigation UI.
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.
Instead of passing through window's associated document's URL as
an extra argument to starting up a worker. This will allow for
improving the representation of 'outside settings' when setting
up a Worker.
This aligns our behaviour closer to other browsers, which
_mostly_ consider file scheme URLs as opaque. For test
purposes, allow overriding this behaviour with a commandline
flag.
We were effectively copying the logic from .set_viewport_size() into
PageClient, but we forgot to actually update the viewport size on device
pixel ratio changes.
This adds the --expose-experimental-interfaces command line flag to
enable experimental IDL interfaces. Any IDL interface with Experimental
in its exposed attributes will be disabled by default.
The problem is that by stubbing out or partially implementing interfaces
in LibWeb, we actually make some sites behave worse. For example, the
OffscreenCanvas interface being exposed makes sites believe we fully
support it, even though we don't. If the interface was not exposed,
these sites may fall back to ordinary canvas objects. Similarly, to
use YouTube, we currently have to patch out MSE interfaces.
This flag will allow developers to iteratively work on features,
without breaking such sites. We enable experimental interfaces during
tests.
We will need to propagate test mode behavior to both the WebContent and
WebWorker processes. By moving this handling to the UI process, we will
only need to update one location.
Especially for the disk cache, it always felt a bit sketchy to create
these objects on the global stack. We now create them in RequestServer's
main() where we can be more certain of destruction order, and pass them
to ConnectionFromClient instances.
This will allow the UI to request WebContent to properly close the top
level traversable when closing a tab. For example, this allows the site
to ask if the user is sure they want to leave, closes WebSocket
connections and more.
Add IPC messages and server-side implementation for streaming
animated image decode. Instead of decoding all frames upfront,
only decode an initial batch and keep the decoder alive for
on-demand frame requests.
New IPC messages:
- request_animation_frames: request decode of a batch of frames
- stop_animation_decode: clean up a decode session
- did_decode_animation_frames: deliver decoded frames to client
- did_fail_animation_decode: report decode errors
The existing did_decode_image message gains a session_id parameter
(0 for single-shot decode, non-zero for streaming sessions).
This adds a settings box to about:settings to allow users to limit the
disk cache size. This will override the default 5 GiB limit. We do not
automatically delete cache data if the new limit is suddenly less than
the used disk space; this will happen on the next request. This allows
multiple changes to the settings in a row without thrashing the cache.
In the future, we can add more toggles, such as disabling the disk
cache altogether.
Replace per-element OrderedHashMap storage for custom properties with
a RefCounted chain (CustomPropertyData) that enables structural
sharing. Each chain node stores only the properties declared directly
on its element, with a parent pointer to the inherited chain.
Elements that don't override any custom properties share the parent's
data directly (just a RefPtr copy). During cascade, only entries that
actually differ from the parent are stored in own_values - the rest
are inherited through the chain. During var() resolution, resolved
values are compared against the parent's and matching entries are
dropped, enabling further sharing.
The chain uses a depth limit (max 32) with flattening, plus
absorption of small parent nodes (threshold 8) to keep lookups fast.
This reduces custom property memory from ~79 MB to ~5.7 MB on
cloudflare.com.
Substituted responses were missing the Access-Control-Allow-Origin
header, causing the CORS check to fail and the script to be treated as
a network error.
Fix this by adding `Access-Control-Allow-Origin: *` to all substituted
responses.
Also include the script's src attribute in the error message when a
script element's result is null, to make debugging easier.
Remove includes from Node.h that are only needed for forward
declarations (AccessibilityTreeNode.h, XMLSerializer.h,
JsonObjectSerializer.h). Extract StyleInvalidationReason and
FragmentSerializationMode enums into standalone lightweight
headers so downstream headers (CSSStyleSheet.h, CSSStyleProperties.h,
HTMLParser.h) can include just the enum they need instead of all of
Node.h. Replace Node.h with forward declarations in headers that only
use Node by pointer/reference.
This breaks the circular dependency between Node.h and
AccessibilityTreeNode.h, reducing AccessibilityTreeNode.h's
recompilation footprint from ~1399 to ~25 files.
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).
The caching RFC is quite strict about the format of date strings. If we
received a revalidation attribute with an invalid date string, we would
previously fail a runtime assertion. This was because to start a
revalidation request, we would simply check for the presence of any
revalidation header; but then when we issued the request, we would fail
to parse the header, and end up with all attributes being null.
We now don't parse the revalidation attributes at all. Whatever we
receive in the Last-Modified response header is what we will send in the
If-Modified-Since request header, verbatim. For better or worse, this is
how other browsers behave. So if the server sends us an invalid date
string, it can receive its own date format for revalidation.
We currently attach HTTP cookie headers from LibWeb within Fetch. This
has the downside that the cookie IPC, and the infrastructure around it,
are all synchronous. This blocks the WebContent process entirely while
the cookie is being retrieved, for every request on a page.
We now attach cookie headers from RequestServer. The state machine in
RequestServer::Request allows us to easily do this work asynchronously.
We can also skip this work entirely when the response is served from
disk cache.
Note that we will continue to parse cookies in the WebContent process.
If something goes awry during parsing. we limit the damage to that
process, instead of the UI or RequestServer.
Also note that WebSocket requests still have cookie headers attached
attached from LibWeb. This will be handled in a future patch.
In the future, we may want to introduce a memory cache for cookies in
RequestServer to avoid IPC altogether as able.
Move the inline dom_node() method to Viewport.cpp so the header no
longer needs the full Document definition. Add explicit includes to
files that relied on the transitive dependency.
LibJS+DevTools: Implement console.trace() with source locations
- Add Console::TraceFrame struct with source location data
- Implement Console::trace() to gather stack information
- Add WebView::StackFrame and ConsoleTrace for IPC
- Implement DevToolsConsoleClient::printer() for traces
- Update FrameActor to format traces for DevTools
- Update WorkerDebugConsoleClient trace handling
- Update ReplConsoleClient to format trace output
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.
This patch introduces a cookie cache in the WebContent process to reduce
blocking IPC calls when JS accesses document.cookie. The UI process now
maintains a cookie version counter per-domain in shared memory. When JS
reads document.cookie, we check whether we have a valid cached cookie by
comparing the current shared version to the last used version. If they
match, the cached cookie is returned without IPC.
This optimization is based on Chromium's shared versioning, in which it
was observed that 87% of document.cookie accesses were redundant. See:
https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html
Note that this cache only supports document.cookie, not HTTP Cookie
headers. HTTP cookies are attached to requests with varying URLs and
paths. The cookies that match the document URL might not match the
request URL, which we wouldn't know from WebContent. So attaching the
cached document cookie would be incorrect.
On https://twinings.co.uk, we see approximately 600 document.cookie
requests while the page loads. This patch reduces the time spent in
the document.cookie getter from ~45ms to 2-3ms.
This change makes it so that signal handlers aren't registered on
windows. The reason for that is that the aim of these handlers was to
catch signals that came from another process, but the windows signal
implementation doesn't actually support signaling another process. So we
will need to use a different mechanism for this.
These can get very large, exceeding the new IPC message size limits.
Instead of serializing them into messages (which was silly anyway)
we now send them as Core::AnonymousBuffer which uses shared memory.
When cookies change or expire, we currently send a list of all changed
cookies to all WebContent processes. We then filter that list in the
WebContent process for cookies that match the page's URL before sending
out cookie change events to JS.
We now perform this filtering in the UI process, so each WebContent
process only receives the cookies it would be interested in, if any.
This serves two purposes:
1. Less IPC chatter.
2. This will let each ViewImplementation know that its cookie value has
actually changed.
(2) is for an upcoming change that will introduce a cookie cache, and
will allow each view to know it should bust that cache.
Note that for this filtering to work, we must iterate ViewImplementation
instances rather than WebContentClient in order to have the view's URL.
We must then associate the IPC with the view's page ID.
No changes to the /cookiestore WPT subtests.
This commit provides IPC endpoint to WebWorker processes that allows new
WebWorker processes to be requested (just like what WebContent has).
This is implemented by proxying the request_worker_agent call from
WebWorker through WebContent to LibWebView. This allows the WPT test...
http://wpt.live/referrer-policy/gen/worker-classic.http-rp/unsafe-url/worker-classic.http.html
...to pass, and the same is likely true for similar tests.
The fetch requests for web worker scripts should be treated as if they
occurred in the caller global scope (which may be a Window) and not the
new worker's global scope. The referrer-policy/4K WPT subtests,
specifically those that test worker subresources, depend on this
behavior to work correctly. This commit fixes many of these subtests,
albeit via a hack where the serialized ESO's creation url is set to
the caller's document URL.
WebContentConsoleClient is a GC::Cell, so PageClient (also a Cell)
should use GC::Ptr instead of WeakPtr to reference it. Visit it in
visit_edges instead.
Also remove unused GC::Root<JS::GlobalObject> member.
We don't want to be terminated when we write to a pipe that's closed,
so set SIGPIPE to be ignored in main. We already pass MSG_NOSIGNAL when
sending over our socket in RequestPipe, so this is a secondary measure.
However, cURL assumes by default that SIGPIPE is unhandled, so before
any operations that interact with pipes, they set their own handler,
interact with the pipe, then restore the original handler. Since we now
ignore the signal, we can just tell cURL not to do this extra work.
The only-if-cached directive currently behaves differently in the HTTP
Caching RFC compared to the Fetch spec. In the former, we must return
an HTTP 504 response if we do not find a cache entry. In the latter, we
must return a network error.
Note that similar to commit aa1517b727, we
cannot test the only-if-cached directive. We implement a same-origin
restriction aligned with the Fetch API that prevents our test infra from
excercising this directive.
During process exit, static variables and thread-local variables are
destroyed in an unpredictable order. The connections HashMap was static,
so when destroyed during static destruction, it would destroy
ConnectionFromClient objects whose notifiers would try to unregister
from thread data that may have already been destroyed.
Fix this by moving the connections HashMap from static storage to stack
storage in ladybird_main(). This guarantees it will be destroyed before
the function returns, while the event loop and all thread data are still
fully alive.
If a request failed, or was stopped, do not attempt to write the cache
entry footer to disk. Note that at this point, the cache index will not
have been created, thus this entry will not be used in the future. We do
still delete any partial file on disk.
This serves as a more general fix for the issue addressed in commit
9f2ac14521.
We now partition the HTTP disk cache based on the Vary response header.
If a cached response contains a Vary header, we look for each of the
header names in the outgoing HTTP request. The outgoing request must
match every header value in the original request for the cache entry
to be used; otherwise, a new request will be issued, and a separate
cache entry will be created.
Note that we must now defer creating the disk cache file itself until we
have received the response headers. The Vary key is computed from these
headers, and affects the partitioned disk cache file name.
There are further optimizations we can make here. If we have a Vary
mismatch, we could find the best candidate cached response and issue a
conditional HTTP request. The content server may then respond with an
HTTP 304 if the mismatched request headers are actually okay. But for
now, if we have a Vary mismatch, we issue an unconditional request as
a purely correctness-oriented patch.
We need to store request headers in order to handle Vary mismatches.
(Note we should also be using BLOB for header storage in sqlite, as they
are not necessarily UTF-8.)
If the cache mode is no-store, we must not interact with the cache at
all.
If the cache mode is reload, we must not use any cached response.
If the cache-mode is only-if-cached or force-cache, we are permitted
to respond with stale cache responses.
Note that we currently cannot test only-if-cached in test-web. Setting
this mode also requires setting the cors mode to same-origin, but our
http-test-server infra requires setting the cors mode to cors.
This is expected by WPT. For this to work, we must be able to determine
the network partition key for shared worker environments. So we now set
a top-level origin for these environments, with a FIXME to implement it
in accordance with the Client-Side Storage Partitioning spec.
We had skipped some steps in the spec and were:
* Always broadcasting an old value of null, instead of what it
actually was previously.
* Still broadcasting a storage event even if the value had
not changed in storage compared to the last value.
Fix both issues by returning what the old value is in the setter and
implementing the missing logic.
We currently disable the disk cache because the WPT runner will run more
than one RequestServer process at a time. The SQLite database does not
handle this concurrent read/write access well.
We will now enable the disk cache with a per-process database. This is
needed to ensure that WPT Fetch cache tests are sufficiently handled by
RequestServer.
This adds support for intercepting network requests and serving local
file content instead. When a URL matches an entry in the substitution
map, the local file is served while preserving the original URL's
origin for cross-origin checks.
Usage:
Ladybird --resource-map=/path/to/map.json
The JSON file format is:
{
"substitutions": [
{
"url": "https://example.com/script.js",
"file": "/path/to/local/script.js",
"content_type": "application/javascript",
"status_code": 200
}
]
}
Fields:
- url (required): Exact URL to intercept (query string and fragment
are stripped before matching)
- file (required): Absolute path to local file to serve
- content_type (optional): Override Content-Type header (defaults to
guessing from filename)
- status_code (optional): HTTP status code (defaults to 200)
This is incredibly useful for debugging production websites: you can
intercept any script, stylesheet, or other resource and replace it with
a local copy containing your own debug instrumentation, console.log
statements, or experimental fixes - all without modifying the actual
site or setting up a local dev server.
- Add WindowManagement to PolicyControlledFeature enum
- Add screen_count() virtual method to PageClient
- Store all screen rects in WebContent::PageClient, derive both
screen_rect() and screen_count() from stored data
- Implement screen_count() overrides in SVGPageClient and PageHost
- Replace FIXME stub in Screen.cpp with spec-compliant implementation
Add support for WPT test variants, which allow a single test file to be
run multiple times with different URL query parameters. Tests declare
variants using `<meta name="variant" content="?param=value">` tags.
When test-web encounters a test with variants, it expands that test into
multiple runs, each with its own expectation file using the naming
convention `testname@variant.txt` (e.g., `test@run_type=uri.txt`).
Implementation details:
- WebContent observes variant meta tags and communicates them to the
test runner via a new `did_receive_test_variant_metadata` IPC call
- test-web dynamically expands tests with variants during execution,
waking idle views after each test completion to pick up new work
- Use index-based test tracking to avoid dangling references when the
test vector grows during variant expansion
- Introduce TestRunContext to group test run state, and store a static
pointer to it for signal handler access
This enables proper testing of WPT tests that use variants, such as the
html5lib parsing tests (which test uri, write, and write_single modes)
and the editing/bold tests (which split across multiple ranges).
To avoid unnecessary IPC traffic, we now only send network response
bodies when a DevTools client is connected.
This requires tracking DevTools connection state in ViewImplementation
so we can propagate it to new WebContent processes created during
cross-site navigation.
Propagate the request initiator type (e.g., "xmlhttprequest", "fetch",
"script", "stylesheet") from LibWeb through the IPC layer to DevTools.
This enables Firefox DevTools to correctly identify XHR/fetch requests
and display appropriate cause types in the Network panel's "Initiator"
column.
This adds support for viewing request payloads (POST data) and response
bodies in the Firefox DevTools network panel.
Request bodies are captured when network requests start and passed
through IPC to the NetworkEventActor, which returns them via the
getRequestPostData protocol method.
Response bodies are streamed via a new IPC message as data is received,
accumulated in NetworkEventActor (with a 10MB size limit to prevent
memory issues), and returned via getResponseContent. Text content is
returned as UTF-8, while binary content (images, etc.) is base64.
Previously, console messages were sent using an index-based system where
DevTools would be notified of new message indices and then request them
in batches. This created synchronization issues during page navigation
when the WebContent process resets while DevTools still has stale index
state.
This changes to a push-based model where console messages are sent
immediately as resources when they are logged, matching how Firefox
DevTools handles console messages. Each message is pushed through IPC
and forwarded to DevTools as a "console-message" or "error-message"
resource.
This eliminates the need for index tracking in FrameActor and simplifies
the entire console message pipeline from WebContent through to DevTools.
Hook ResourceLoader to emit network request lifecycle events through
IPC to the UI process, where FrameActor creates NetworkEventActor
instances that serialize requests using Firefox's Remote Debug Protocol.
The Network panel now shows requests with method, URL, status, MIME
type, size, and timing information. Several features remain stubbed
(POST data, response content, cause detection) marked with FIXMEs.
In the next commit, we will rely on global scope's URL being
initialized even earlier in the process of setting up the environment
for the Worker global. Move this hack just before that.
If the cURL request completes with anything other than CURLE_OK, we must
not keep the cache entry. For example, if the server's connection closes
while transferring data, we receive CURLE_PARTIAL_FILE. We don't want
this cache entry to be treated as valid in a subsequent request.
Implement PlaybackStream using WASAPI. The design is similar to
PlaybackStreamAudioUnit in that it uses a task queue. A high priority
thread is used to render the stream. All the stream controls save for
the exit being requested which happens on destruction of the stream are
managed by the render thread.
Due to the design of the windows audio mixer the audio we receive must
be resampled to match the sample rate of the mixer. We use a float based
interleaved PCM stream which matches both our existing code and the
audio mixer which internally usues floats.
Having to use a mutex around a queue for the task queue is suboptimal,
in a future PR a MPSC queue could be added to AK and used instead.
This directive allows our disk cache to serve stale responses for a time
indicated by the directive itself, while we revalidate the response in
the background.
Issuing requests that weren't initiated by a client is a new thing for
RequestServer. In this implementation, we associate the request with
the client that initiated the request to the stale cache entry. This
adds a "background request" mode to the Request object, to prevent us
from trying to send any of the revalidation response over IPC.
Not super important, but this will match an upcoming ID for stale-while-
revalidate requests. It also would techinically be UB if this ID had
ever overflowed. Let's make it 64-bit while we are here to also avoid
the possibility that it ever will overflow.
The bug fixed here is that in RequestServer's ConnectionFromClient, we
stored the websocket ID as i32, despite it being i64 everywhere else.
Let's make it unsigned while we are here to be consistent with how all
other RS-related IDs will be soon.
Computing the font for an element in `compute_font` is premature since
we are yet to apply animated properties - instead we should compute the
value on the fly (with a cache to avoid unnecessary work) to ensure we
are respecting the latest values
The system uses ssize_t so it can return -1 in case of an error. But in
our case, we will transform that to an AK::Error, thus we never return
-1. Let's return size_t instead.
This is largely editorial. One behaviour change is that events are now
sent from a global task on the DOMManipulation task source.
Somewhat awkwardly, the spec refers to `this` before the Worker exists.
As it's for getting the relevant global object / settings object, I've
had to work around that.
Corresponds to:
917c2f6a73
SocketAddressWindows.h contains (re)definitions of a bunch of system-
level types. This leads to a bunch of conflicts when trying to include
proper Windows headers in SystemWindows.cpp. This patch removes this
inclusion and just forward-declares the couple of types we need.
We currently have two ongoing implementations of RFC 9111, HTTP caching.
In order to consolidate these, this patch moves the implementation from
RequestServer to LibHTTP for re-use within LibWeb.
The end goal here is for LibHTTP to be the home of our RFC 9111 (HTTP
caching) implementation. We currently have one implementation in LibWeb
for our in-memory cache and another in RequestServer for our disk cache.
The implementations both largely revolve around interacting with HTTP
headers. But in LibWeb, we are using Fetch's header infra, and in RS we
are using are home-grown header infra from LibHTTP.
So to give these a common denominator, this patch replaces the LibHTTP
implementation with Fetch's infra. Our existing LibHTTP implementation
was not particularly compliant with any spec, so this at least gives us
a standards-based common implementation.
This migration also required moving a handful of other Fetch AOs over
to LibHTTP. (It turns out these AOs were all from the Fetch/Infra/HTTP
folder, so perhaps it makes sense for LibHTTP to be the implementation
of that entire set of facilities.)
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>
We currently have a FIXME to validate cached data with a crc32. But this
is sort of a non-starter, because we never actually have the cached data
in-memory - we transfer it to the WebContent process via system calls,
and it never reaches userspace in RequestServer.
Chrome makes a bit of an educated gamble here. They assume cosmic bit
blips are extremely unlikely, thus the cached data does not get verified
with a hash. Instead, they store non-cryptographic hashes of some select
fields, and they validate just those hashes.
Here, we store a hash of the cache key in the cache header, and a hash
of the cache header in the cache footer. With these validations, along
with other validations already in-place, we can be reasonably sure we
are not sending corrupt data to the WebContent process.
This effectively reverts 9b8f6b8108.
I misunderstood what Chrome was doing here - they will issue a range
request only for what they call "sparse" cache entries. These entries
are basically used to cache partial large file, e.g. a multi-gigabyte
video. If they hit a legitimate read error, they will fail the request
with a ERR_CACHE_READ_FAILURE status.
We will now (again) fail with a network error when a cache read fails.
For example, we will want to be able to test that a cached object was
expired after N seconds. Rather than waiting that time during testing,
this adds a testing-only request header to internally advance the clock
for a single HTTP request.
This mode allows us to test the HTTP disk cache with two mechanisms:
1. If RequestServer is launched with --http-disk-cache-mode=testing, it
will cache requests with a X-Ladybird-Enable-Disk-Cache header.
2. In test mode, RS will include a X-Ladybird-Disk-Cache-Status response
header indicating how the response was handled by the cache. There is
no standard way for a web request to know what happened with respect
to the disk cache, so this fills that hole for testing.
This mode is not exposed to users.
This is causing misaligned reads with Address Sanitizer enabled. We
could maintain the packed attribute, and deal with alignment - but we
ended up not actually needing these to be packed anyways. The only thing
we need to know is the serialized size of the header, which we can just
determine differently.
We previously excluded headers exempted from storage when we serialized
the headers into the database. However, we stored the original headers
in-memory. So when a subsequent request hit CacheIndex::find_entry, we
would return an entry with response headers that should have been
excluded.
We can use Duration as-is without coercing values to seconds. This
probably doesn't make much difference in real-world scenarios, but when
we hammer the cache during tests, this truncation will cause flakey
behavior.
The Windows RequestPipe implementation uses a non blocking local socket
pair, which means the non-fatal "resource is temporarily unavailable"
error that can occur in the non-blocking HTTP Response data writes can
be retried. This was seen often when loading https://ladybird.org.
While the EAGAIN errno is defined on Windows, WSAEWOULDBLOCK is the
error code returned in this scenario, so we were not detecting that we
could retry and treated the failed write attempt as a proper error.
We now detect WSAEWOULDBLOCK and convert it into the errno equivalent
EWOULDBLOCK. There is precedent for doing a similar conversion in the
Windows PosixSocketHelper::read() implementation.
Finally, we retry when we receive either EAGAIN or EWOULDBLOCK error
codes on all platforms. While POSIX allows these 2 error codes to have
the same value, which they do on Linux according to
https://www.man7.org/linux/man-pages/man3/errno.3.html, it is not
guarenteed. So we now ensure platforms that return EWOULDBLOCK with a
value different than EAGAIN also perform write retries.
This commit extends is_cacheable() to allow storage of responses that
rely on heuristic cacheability, including status codes defined as
heuristically cacheable.
We implement heuristic freshness lifetime calculation based on the
Last-Modified header as guidance, and apply it when no explicit
expiration information is present.
We were treating Content-Type as exempt from header updates during cache
revalidation and incorrectly allowing Content-Length to get overwritten
when handling an HTTP 304 response.
This caused cached entries to end up with a mismatched Content-Length
that described the validating response instead of the stored body.
We currently store ByteString as TEXT, but that is interpreted by SQLite
as UTF-8. All string-related operations will try to operate on the text
as if it is stored with UTF-8. We should use BLOB instead, which does
not make any encoding assumptions.
The only user of ByteString in the database currently is the HTTP disk
cache. So we bump its version here to remove existing data. Again, we
will want to handle changes like this more gracefully eventually.
Per RFC 9111, we're allowed to cache HTTP responses that don't have a
"Cache-Control" header, provided they do have an "Expires" header.
This lets us cache JavaScript resources on https://x.com/ and makes
it load much faster when cached.
This change moves the initial alpha premultiplication step for all
decoded images from WebContent to the ImageDecoder process. This
doesn't reduce the overall amount of work, but it can make sites with a
lot of images more responsive.
Prevents observably calling Trusted Types, which can run arbitrary JS,
cause crashes due to use of MUST and allow arbitrary JS to modify
internal elements.
We set bInheritHandles to TRUE for all child processes we spawn. Some
of the types of objects that support handle inheritence is all of the
STD handles (STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE) and
the console screen buffer. This means if Ladybird and all the child
service processes it launches/communicates with our console apps, only
a single console needs to be allocated and all child process output
their logs to that single console.
When a request becomes stale, we will now issue a revalidation request
(if the response indicates it may be revalidated). We do this by issuing
a normal fetch request, with If-None-Match and/or If-Modified-Since
request headers.
If the server replies with an HTTP 304 status, we update the stored
response headers to match the 304's headers, and serve the response to
the client from the cache.
If the server replies with any other code, we remove the cache entry.
We will open a new cache entry to cache the new response, if possible.
We currently store response headers in the cache entry file, before the
response body. When we implement cache revalidation, we will need to
update the stored response headers with whatever headers are received
in a 304 response. It's not unlikely that those headers will have a size
that differs from the stored headers. We would then have to rewrite the
entire response body after the new headers.
Instead of dealing with those inefficiencies, let's instead store the
response headers in the cache index. This will allow us to update the
headers with a simple SQL query.
This currently just holds a cache version. If we make a breaking change
to the cache format, we can increment the version here to wipe the cache
database. This is certainly a blunt hammer approach; we will want to
handle such changes more gracefully when we can.
In the initial work that got Ladybird running on Windows, there were
some DLLs that WebContent implicitly depended on which was causing
runtime errors when launching as they didn't exist in libexec.
So the workaround was to explicitly link the targets that had issues to
WebContent and use lagom_copy_runtime_dlls() to ensure they got copied
to libexec.
But given libexec is not a standard Windows convention, in a later
review we made sure Services got output to the bin folder, but those
initial workarounds were not removed even though they were now
unnecessary.
The function currently has 2 purposes: (1) To copy dependent dlls for
executables to output binary directory. This ensures that these helper
processes can be ran after a build given not all DLLs from vcpkg libs
get implicitly copied to the bin folder. (2) Allow fully background
and/or GUI processes to use the Windows Subsystem. This prevents
unnecessarily launching a console for the process, as we either require
no user interaction or the user interaction is all handled in the GUI.
The Win32 API equivalent to pipe2() is CreatePipe(), which creates read
and write anonymous pipe handles that we can set to non-blocking via
SetNamedPipeHandleState(); however, this initial approach caused issues
as our Windows infrastructure assumes socket-based handles/fds and that
we don't use Windows pipes at all, see Core::System::is_socket() in
SystemWindows.cpp. So we use socketpair() to keep our current
assumptions true.
Given that Windows uses socketpair() and Unix uses pipe2(), this
RequestPipe abstraction avoids ifdef soup by hiding the details about
how the read/write fds pair is created and how response data is written
to the client.