Store reftest expectations as a list of match/mismatch reference URLs
and evaluate each reference in sequence.
This lets test-web run WPT reftests that specify multiple reference
relations, such as tests with both match and mismatch links.
Teach test-web to expose the UI-process history dump. Add focused
navigation tests for same-document traversal, fallback traversal, and
cross-document browser back and forward behavior. The expectations
assert document state and the UI-owned history snapshot.
Move the image loader sources and decoder-only dependencies from LibGfx
into a new LibImageDecoders library. This keeps the APNG-enabled PNG
loader out of processes that only need core graphics and image writers.
Link the ImageDecoder service, direct decoder tests, fuzzers, test-web,
and the image utility against LibImageDecoders where they still decode
images in-process.
Scan sources of Text tests during collection to identify test variants,
using a very basic HTML parser. All tests with variants use the same
pattern for their meta tags, so even this is overkill for what we need,
but it should work for any future tests that happen to be arranged a
little differently.
Load tests that require the echo server through a per-test
*.localhost hostname instead of plain localhost. This prevents shared
per-host browser state, from leaking between parallel or repeated test
runs.
Update HTTPTestServer to create echo URLs on the current origin when a
test is already loaded from the echo server, preserving same-origin
behavior for tests that inspect echo-served iframes.
Page IDs were allocated independently by each WebContent PageHost,
starting at zero for every process. That made them unsuitable as a
global identity for page-presenting compositor contexts, because the
same numeric page ID could exist in multiple WebContent processes.
Move page ID allocation to WebView::Application and initialize each
WebContent process with its browser-assigned first page ID over IPC.
New view requests now get a browser-assigned page ID before
WebContent creates the PageClient, and PageHost no longer owns a
local page counter.
This gives page-presenting compositor contexts a stable global page
identity, which will allow their context IDs to be derived from page
IDs and simplify compositor context allocation in a follow-up.
Problem: Text-mode tests that perform a top-level cross-document
navigation can hang forever — even when the target page reports PASS.
For example, the connect-src test in Tests/LibWeb/Text/input/navigation/
has been intermittently timing out on CI for months, and has its own
in-test workaround. But that’s a mitigation that just narrows the race.
Cause: The Text-mode on_load_finish handler in test-web/main.cpp filters
by URL and bails out if the loaded URL doesn’t match the test’s original
URL — to ignore subframe loads. But page_did_finish_loading is already
gated on navigable->is_traversable(). So, it’s top-level-only — and the
URL filter is actually rejecting load events from top-level navigations
away from the original test URL.
When the original page’s did_finish_loading IPC hasn’t reached test-web
by the time the navigation target loads and signals PASS, the target’s
did_finish_loading is the only surviving load notification — and the URL
filter masks it. So, did_finish_loading is never set test-side. And so
the test-finish handler then bails on the did_finish_loading &&
did_check_variants check — and the test hangs.
Fix: Drop the URL filter from the Text-mode on_load_finish handler. Any
top-level load (the original page, or any subsequent navigation) now
sets did_finish_loading. The variant-loading logic is unchanged: The
second variant-check just no-ops — because test.variant is already set
from the first response.
Look for screenshot expectations in an expected-<platform>
directory before using the shared expected directory. This lets tests
that cover system-derived rendering keep platform-specific baselines
without making other platforms use the same PNG.
These both require the same helper processes, and maintaining the same
list in multiple places is error-prone. For example, I spent too long
debugging why test-web was crashing, when the issue was that Compositor
hadn't been rebuilt as test-web didn't depend on it. That problem can
no longer happen if we define the same dependency list for both.
Add internals helpers so text tests can install local blocker rules and
toggle content blocking without browser-process IPC. Clear installed
rules after each non-crashed test-web case so blocker state cannot leak
into the next test.
Add a repeatable blocker-list option that reads local list files in the
browser process. The files are concatenated into one buffer and shared
with WebContent through the content blocker IPC path when view options
are applied.
Parse the buffer in WebContent and reject malformed UTF-8 without
replacing the currently installed rules.
Rename the local content blocking implementation and its tests from
ContentFilter to ContentBlocker while keeping the existing substring
matcher backend and behavior.
Update the WebContent IPC method, WebView option names, debug toggle,
and default config file name to use content blocker terminology.
Keep a small harness-status.txt file in the test-web results
directory and refresh it whenever tests start, finish, or the run stops
early. This leaves the last known active views, counts, and remaining
test count behind even if the harness does not reach its normal result
generation path.
Also report helper-process log write failures instead of treating them
as "no logs". That makes artifact gaps visible instead of silently
losing the only clue from a helper process failure.
The exit-status line added in b71d82645b was being written with outln()
straight to stdout, which corrupts the live progress bar that the
default display renders on stdout during a run.
Send it through log_helper_message() instead, attributed to the exiting
process. That way the line lands at the tail of that pid's section in
helper-process-logs.html, only tees to the terminal when verbosity is
raised to VERBOSITY_LEVEL_LOG_TEST_OUTPUT, and stays out of the way of
the progress display in normal runs. Drain the helper's pending output
via consume_helper_capture() first so the exit message appears after
the process's final output rather than before it.
Log active views and tests when test-web stops before all tests have
completed. Reuse the same reporting for signal handling, and accept any
future signal registration without asserting while explaining shutdown.
Also log observed helper process exits with decoded Unix wait status so
CI logs show whether a process exited normally or died from a signal.
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.
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
Previously, we signalled completion of a PDF test once the
`pagesloaded` event fired. This fires once page metadata is initialized
but the text and annotation layers are rendered asynchronously after
that. We now wait for the text and annotation layers of the first page
to render. The test runner will currently fail for PDFs with more than
one page bceause pages are loaded lazily once they are visible and
there's no guarantee that anything beyond the first page will be loaded.
This script waits until fonts have loaded and waits for 2 animation
frames before signalling test completion. This is the same mechanism
already used for ref and crash tests.
Deferred warnings were originally intended to suppress output during
live display, ostensibly to avoid glitch scrolls.
Then, 1af74d1a7c added log capture to the
test-web process. Suddenly, deferred warnings became deadly because
they're flushed during a tiny window after the capture notifier has
stopped draining the tee pipe but before stderr is restored.
This caused a deadlock at exit. The fix is to remove this system and
call warnln directly because display integrity is now protected by
other means.
This prevents glitches where stale test status lines can bleed above
the live display list. Log this additional captured output so it can
be reviewed if a main or helper process failure is suspected.
Use files instead of buffers. Consolidate stderr and stdout into one
view. Break handling out into TestRunCapture and CaptureFile helper
classes. We will use them to log output from ALL processes in the next
commit.
When test-web runs inside Claude Code or Codex, per-test progress lines
are suppressed. These environments are non-interactive and the per-test
output (potentially thousands of lines) wastes the agent's context
window without adding value. The agent only needs the pass/fail summary
to decide next steps.
Quiet mode preserves: the "Running N tests..." line, the summary table,
non-passing test names, and the results URL.
This is a little more complicated than screenshot tests, because we deal
with two separate page loads, for the test and expectation pages. When
loading the expectation page, we clear the did_inject_js flag and also
store the URL so that we can compare that later.
Keep track of whether we've injected the JS, and avoid doing so again.
Also skip this entirely unless we're actually dealing with the test's
URL and not a subframe. This check is already done on dump tests.
The MutationObserver was not disconnected after firing, so any other
modifications to the document's class attribute after that point would
signal the test completing again. This is unlikely to happen in
practice as we shouldn't be running any JS after completing a test, but
it's harmless to disconnect it.
We had three separate timeout timer sites in `test-web`, which are now
deduplicated into a single one. This single timeout timer is started
_before_ we start loading `about:blank`, which potentially catches more
timeouts when a WebContent process is left in a broken state by a
previous test.