Speedometer removes and recreates its benchmark iframe while nested
session-history bookkeeping is still queued. A live child-frame commit
could find that its nested history list had been pruned and then behave
like a stale detached frame. That dropped the real src navigation and
left the harness waiting for a load event.
Preserve the newest real child navigation until the initial session
history entry is ready. Tolerate detached child navigables while history
steps scan target entries, and recreate the missing nested history only
when the child is still the container's live content navigable. Share
the nested-history append path with initial child creation so the normal
and recovery paths keep the same step handling.
Add iframe remove/recreate coverage for pending child history, same-src
load, and repeated pushState removal.
Apply helper process sandboxing by default and replace the old
--enable-sandbox switch with --disable-sandbox. Propagate the opt-out
from Ladybird, test-web, and WebDriver to WebContent, WebWorker,
RequestServer, ImageDecoder, and Compositor.
This was added in 61c9e819bb for the
Android build. But on environments like macOS where XDG_CACHE_HOME is
not set, it's forcing things like the HTTP disk cache and ccache to
live under the Build directory instead of their proper homes in
~/Library/Caches. If this environment is needed for the Android build,
we should come up with some other solution when that becomes a priority.
Show native history menus when users right-click or long-press
the back and forward toolbar buttons. Populate entries from the
UI-process session history mirror, using saved history titles and
favicons when available and falling back to the URL and globe icon
otherwise.
Share the Qt base64 PNG icon helper so bookmark menus and navigation
history menus render stored favicons consistently.
Previously, uploading files with the "Element Send Keys" Webdriver
command failed when running with the `--enable-sandbox` flag because
the Landlock policy denies access to arbitrary file paths. We now read
these files from the unsandboxed UI process in the same way as normal
interactive uploads
Keep UI process session history authoritative across overlapping
fallback loads and traversals. WebContent can finish a superseded
history load with a live document matching the UI seed URL while its
local step, document state id, and Navigation API keys still describe
a temporary partial list.
Reconstruct the current entry around the UI-owned list in that case.
This avoids making the UI process adopt WebContent's incomplete
snapshot.
Track UI-started fallback loads by URL so unrelated navigations cannot
consume the pending seed state. Resolve deferred WebDriver completions
through the view registry so callbacks queued before a process swap do
not touch a destroyed view.
Add WebDriver coverage that waits for explicit UI/WebContent history
convergence after the relevant document events. The waits poll
observable history state instead of depending on timing.
One big assumption that our "effectively contained node traversal" made
was that the common ancestor container of the range would be all the way
at the root of the effectively contained nodes, but that's not the case
- e.g. a common text ancestor could reside inside a `<span>` whose
children are all effectively contained, causing that element to be
contained as well.
Walk up from the common ancestor container until we've found the
top-most effectively contained ancestor.
We were always checking whether the `createLink` command had a non-empty
value, which was a misinterpretation of the spec text.
WPT's reference implementation of this algorithm explicitly checks
whether a value definition was set for a command, so we do the same.
These are commands that have specific indeterminate and value behaviors.
The value behavior was implemented as a workaround and is now factored
out into a separate algorithm.
Problem: A navigation could intermittently hang forever with no load
event ever firing. In test-web, that surfaced as a 120-second
“pre-navigation timeout, WebContent process may be unresponsive”: The
about:blank load used for clearing the document between tests would
never complete — leaving WebContent idle while the harness waited.
Cause: begin_navigation claims the navigable’s ongoing navigation id and
then awaits an asynchronous unload check. While it waits, a session-
history traversal can re-stamp the navigable’s ongoing navigation to
“traversal”. When the unload check resumes, the navigation finds that
its ongoing navigation ID no longer matches — and silently aborts. But
nothing ever re-runs it — so the navigation is lost. The deferral guard
at the top of begin_navigation, which defers a navigation while a
traversal is already ongoing, runs before this window — so a traversal
that begins during the unload check slips past it.
Fix: When the post-unload-check guard finds the navigable is now running
a traversal, re-defer the navigation into the pending navigations list
instead of dropping it — mirroring the existing deferral guard. Clearing
the ongoing traversal drains the pending navigations — so the navigation
runs to completion as a fresh attempt once the traversal finishes.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/10122.
clobberNextNavigationWithATraversal() arms a one-shot that, on the next
call to Navigable::begin_navigation, re-stamps the navigable’s ongoing
navigation with a synthetic session-history traversal during the unload
check, then clears it on a later turn — draining deferred navigations.
This lets a single-process test deterministically reproduce a race
between a cross-document navigation and a concurrent traversal, which
otherwise only surfaces under scheduling jitter in a multi-process run.
Problem: Building on arm64 Linux via Meta/ladybird.py fails during vcpkg
bootstrap with “CMake was unable to find a build program corresponding
to Ninja” unless VCPKG_FORCE_SYSTEM_BINARIES=1 is set by hand.
Cause: vcpkg ships no prebuilt tool binaries for arm64 Linux — so it has
to be told to use the system CMake and Ninja. CI and the devcontainer
already set the variable for arm64, but ladybird.py only auto-set it for
riscv64 — leaving the documented build path broken on arm64 Linux.
Fix: Also set VCPKG_FORCE_SYSTEM_BINARIES on AArch64 Linux hosts —
matching the condition CI already uses.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/2417
box_baseline() applied CSS2's bottom margin edge rule for non-visible
overflow to every caller, so flex items with hidden overflow were
baseline-aligned by their margin edge instead of their text. CSS Align
scopes that rule to a box's last baseline set, while flex baseline
alignment and table cells use the first set, which always derives from
content. Parameterize box_baseline() on the requested baseline set and
propagate it through the recursive child lookup.
Merge `current_image_frame` and `current_image_frame_sized` into a
single method which takes an `Optional<Gfx::IntSize>`.
Rename `default_image_frame_sized` to `default_image_frame` and make
it's `Gfx::IntSize` argument `Optional`.
Use IncludeStarRule::Yes for cookie public-suffix checks so domains not
explicitly listed in the PSL still get treated as public suffixes via
the implicit * rule. This fixes accepting cookies for bare TLD-like
domains.
Ever since PublicSuffixData was created, it was using "no star rule"
matching, which is what is needed for the address bar to distinguish
between a domain and a search. URL::Host on the other hand requires
the fallback star rule. Which rule is needed depends on the use case
of the PSL. Support both use cases by a flag in PublicSuffixData.
I suspect this is not an important case, but since both Firefox and
Chromium implement it, let's match their behaviour. While this does
not matter the exact letter of the spec, the relevant WPT test was
alongside this spec text as part of a spec change trying to align
to align spec behaviour with Chromium and Firefox, so I believe
what is implemented here to be the intention of the specification
authors.
Preserve fractional pinch focal points when updating the
main-thread visual viewport. Only coalesce queued pinch events
that share the same focal point and modifiers so WebContent sees
a transform equivalent to the event sequence seen by the
compositor.
Also clear a speculative async visual viewport transform once
async wheel or pinch admission becomes blocked. At that point the
compositor can no longer advance that transform to match
WebContent. Use a looser translation tolerance when comparing
visual viewport transforms to account for subpixel differences in
the compositor and main-thread math.
Keep the visual viewport transform out of Element client rects and
IntersectionObserver geometry. Pinch zoom should change the visual
viewport, but not the layout viewport coordinates exposed through DOM
geometry APIs.
Thread an opt-out through rectangle mapping so paint and hit testing
still use the full visual transform while web-observable geometry can
stay in layout viewport coordinates. This matches the Blink and WebKit
page scale model and keeps responsive script from treating pinch zoom
like a relayout.
Add coverage for getBoundingClientRect() under pinch zoom and visual
viewport IntersectionObserver geometry.
Apply pinch zoom deltas to the compositor's visual viewport transform
so the currently presented display list can respond without waiting for
the WebContent main thread. Keep the normal WebContent pinch event path
so the real VisualViewport state and DOM-visible events catch up after.
Only take the compositor path when async scrolling is enabled and there
are no blocking wheel listeners, since pinch zoom dispatches a synthetic
wheel event that script may cancel. Coalesce queued pinch events in
WebContent so main-thread catch-up can adopt multiple gesture deltas
together.
Use the compositor visual viewport transform for wheel hit testing and
consume wheel deltas as visual viewport pan while zoomed. Scale the
handoff to layout viewport scrolling by the inverse visual viewport
scale, so touchpad momentum does not jump when the visual viewport hits
an edge.
The decoded-frame Skia image cache is useful for display-list
rasterization because decoded image resources can be replayed over many
frames. The cache lets that path reuse SkImage wrappers and GPU-backed
copies instead of rebuilding them whenever the same resource is painted.
For canvas, commands are consumed into one backing surface and decoded
frames are already held by the command or paint style for the draw. A
per-painter cache does not match that usage model, and can keep decoded
frames and Skia images alive after the draw has consumed them.
This removes the cache from PainterSkia and drops the now-unused pruning
hook from CanvasCommandPlayer. With the cache gone, PainterSkia can hold
its painting surface directly instead of allocating a private Impl.
DisplayListPlayerSkia keeps owning the cache, so display-list
rasterization keeps the SkImage reuse behavior.
Treat pending session history entries as absent from the used step
graph, and share that through a small step_value() helper so
snapshotting, Navigation API entry construction, target-entry lookup,
and forward clearing do not drift apart.
Keep cross-document history application tied to the navigation id that
created it. Queued changing-navigable work now finishes without
applying when a later navigation has already replaced its target, and
any traversal sentinel is cleared through the shared setter so queued
navigations can drain.
When navigation arrives while traversal is still ongoing, keep only the
newest pending navigation. This matches Chromium, WebKit, and Gecko on
sites that click through product or category links while prior loads
settle.
Revalidate queued same-document child continuations before running them
from null-document tasks, so removed frames or frames claimed by newer
navigations do not receive stale history state.
Preserve nested-history descriptors even when all child entries are
pending, keeping live child navigable identity available for later UI
process history seeds.
Add regression coverage for iframe renavigation during history commit,
for pending child history followed by a real navigation, and for removed
iframes with queued history updates.
Child navigables can lose document-associated apply-history tasks when
a parent page replaces or destroys the child document. Queue child tasks
with no document association so they remain runnable, and share that
choice for both activation and update-only continuations. Keep top-level
work associated with the active document to preserve initial about:blank
Window reuse.
Also abandon a queued child fetch if its parent document is already gone
before reading the parent's relevant settings object. This matches
browser behavior for detached frame navigations and avoids resuming
stale work against a discarded parent.
The Twinings menu to Black Tea to Earl Grey product flow now reaches the
product main content under Ladybird WebDriver. Existing navigation
coverage and the full LibWeb text suite cover the local history cases.
Same-document navigations now commit synchronously in WebContent, while
the UI process mirror learns about them over asynchronous IPC. A stale
UI seed could be accepted back into a live non-initial document and
overwrite its latest entry, making queued traversals target unreachable
entries.
Share descriptor comparison helpers between LibWeb and LibWebView.
Reject stale top-level seeds against the active document latest entry,
and let the UI process adopt WebContent current snapshots when a seed is
rejected. Test-only session history dumps now first send WebContent
current state synchronously, so dumps observe the converged state.
Allow post-load UI seeds to carry UI-owned nested histories that the
freshly loaded top-level document has not reconstructed yet. Add unit
coverage for matching those seeds while still checking top-level state.
Finalize fragment navigations and URL/history updates immediately when
no traversal state is active. Keep the queued same-document finalizer as
the fallback for reentrant traversal work and child navigables whose
nested history is not installed yet.
Share the entry-list portion of same-document navigation finalization
between the fast path and queued fallback, so append and replace
bookkeeping cannot drift.
Preserve unrelated ongoing cross-document navigations when a page starts
a load and then performs a same-document history update in the same
task. This matches Chromium, WebKit, and Gecko: the same-document
update must not cancel the pending real navigation.
The session-history mirror tests now observe synchronous UI updates. A
navigation test covers the pending-load plus pushState race.
Problem: A BMP V5 image whose embedded ICC profile offset points past
the end of the file triggers an OOB read.
Cause: The bounds check summed the profile offset, the file-header size,
and the profile size in 32-bit arithmetic. So, a large offset (e.g.
0xfffffff0) wraps the sum back into range and passes the check. The
decoder then returns a span pointing far past the end of the file.
Fix: Compute the sum in 64 bits — so an out-of-bounds offset can no
longer wrap, and the profile’s rejected.
Fixes: https://github.com/LadybirdBrowser/ladybird/issues/9967
Problem: Decoding a BMP whose height is INT_MIN triggered a UBSan error.
A top-down BMP legitimately uses a negative height — and unlike width,
it’s not rejected, so it can be INT_MIN.
Cause: decode_bmp_pixel_data() took the magnitude of the i32 width and
height with abs(). Negating INT_MIN is undefined behavior.
Fix: Widen to i64 before taking the absolute value — so the magnitude of
INT_MIN is representable. The resulting out-of-range dimension is still
rejected by Bitmap::create — so only the undefined behavior changes.
Fixes: https://github.com/LadybirdBrowser/ladybird/issues/9994
Problem: Decoding a non-square image whose Exif orientation is 5
(transpose) writes one pixel past the end of the destination bitmap —
an ASan heap-buffer-overflow in ExifOrientedBitmap::set_pixel.
Cause: oriented_position() mapped orientation 5 by composing the
“flip-horizontally” and “rotate-90-clockwise” helpers. Each helper
mirrors using the source width. But after the rotate, the point is
already in the transposed coordinate space — where the relevant
dimension is the source height. For a non-square image, that composition
produces x-coordinates past the destination width. Only square images
happened to stay in bounds. The destination bitmap is the transposed
size — so the out-of-range column wrote past its allocation.
Fix: Map orientation 5 directly as a transpose across the main diagonal:
source (x, y) to destination (y, x).
Fixes https://github.com/LadybirdBrowser/ladybird/issues/10102
The compositor already used a GPU surface with bitmap readback when a
Vulkan Skia context existed but no native shared surface path was
available. The new Windows Direct3D backend has the same constraint for
now: it can render with Skia on the GPU, while publication still uses
shareable bitmaps.
With Windows backend selection fixed to Direct3D, LibGfx needs a native
GPU context that can feed Skia directly. Add a Direct3DContext helper
that owns the DXGI adapter, D3D12 device, and direct command queue, then
pass that state to Skia through GrDirectContext::MakeDirect3D..
Windows has a platform-native GPU stack based around Direct3D and DXGI.
The browser's Windows integration will need to share GPU resources with
Skia, ANGLE, and the UI process through that stack, especially when
backing stores move from bitmap readback to shared handles. Keeping
Vulkan as the Windows backend would put painting on a parallel API that
does not match the resources the rest of the native Windows pipeline
needs to exchange.
The Rust bytecode generator only passes local variable names to C++ now,
and no C++ code observes declaration kind metadata from LocalVariable.
Store local names directly as Utf16FlyString values and remove the stale
C++ wrapper type.
InstructionStreamIterator no longer has any C++ users now that bytecode
block collection has moved to Rust. Remove the iterator and include the
bytecode field types needed by generated C++ instruction definitions
directly in Instruction.h.
Use the Rust bytecode dumper's basic block collection logic for the
metadata block count. This removes the last C++ bytecode label walk and
lets us delete the generated C++ label and operand visitor helpers.
The Rust bytecode dumper now formats exception handler labels, raw
operands, builtins, labels, and registers. Remove the C++ dump-only
formatters and flatten Operand to expose only the runtime value-array
layout that C++ still observes.
Generate Rust bytecode dump helpers from Bytecode.def and route
Executable::dump() through them for instruction stream formatting.
Add a small Rust runtime::value helper for decoding encoded LibJS
Values so immediate Value operands are formatted on the Rust side. C++
callbacks remain only for local names and GC-backed Value payloads that
still need LibJS object access.
Remove the generated C++ to_byte_string_impl() methods and the old
Instruction::to_byte_string() dispatch. The bytecode dump tests cover
output compatibility.