Previously, the "View History" action always opened a new tab. We now
switch to an existing `about:history` tab when one exists in the active
window and fall back to opening a new tab otherwise.
Problem: While a document was concurrently loaded, a burst of sync same-
document history navigations (e.g. a pushState flood) could spin the UI
process at full CPU — and on slow/Sanitizer builds, intermittently time
out other tests (since one WebContent process is reused across tests).
Cause: The UI process keeps an authoritative session-history mirror, and
merges each WebContent snapshot into it. find_merge_anchor compares each
local entry against each incoming one. Every URL comparison serializes
both URLs. When a snapshot briefly diverges from the mirror, the anchor
is no longer near the end. So, the search degraded to a deep quadratic
walk — with a string serialization per-comparison. A flood compounded
that from both ends: Every pushState added a top-level entry — driving
the count each walk must cover into the hundreds — and also triggered a
history update. So, the merge ran again for every one of them.
Fix: Index the incoming entries by serialized URL once — keyed as URL
equality compares (full serialization, fragment included). So, the URL-
keyed anchor searches are linear, not quadratic.
Problem: A document could drop a load if it ran a stream of synchronous
same-document history navigations (say, a pushState flood) while it was
concurrently loaded again. The load never finished — so on sanitizer/
slow builds, this had been intermittently taking down unrelated tests in
CI — since test-web reuses one WebContent process, and the next test’s
load can arrive while the previous document’s history work is still
draining. A synchronous commit also claimed a session history step it
never retired — so claimed steps piled up without bound.
Cause: A sync same-document navigation committed immediately and could
jump the session-history-traversal queue while a queued apply-history-
step — such as a cross-document load — was still waiting behind it. The
queued run read the active session-history entry after the sync
navigation had installed it, but before its step number was assigned —
then judged itself stale against that still-pending step, and was
discarded. The shared step numbering was fragile under the same nesting:
A number computed from the current step alone could collide with an in-
flight one — and a stale run that completed later could write its own
step back over a newer one. 394312ab5a stopped the crash this used to
cause, but the races remained.
Fix: Treat a queued push whose displayed entry’s step is still pending
as live rather than stale — so the concurrent load isn’t dropped. Number
apply-history-step runs, and let a run commit its target step only if no
newer run has committed one — so a stale run can’t move the current step
backwards. Claim each new step past every claimed-but-uncommitted step —
rather than from the current step alone, and keep clearing the forward
session history from removing those entries. And retire the step a sync
commit claims, since it applies in the same task, and nothing else will.
See https://github.com/LadybirdBrowser/ladybird/issues/10028
This adds an internals.loadURL(url) that defers Page::load so it starts
outside the calling task and can land between session-history traversal-
queue steps — as a load requested by ConnectionFromClient::load_url in
the UI process can, but as a load started from script never does.
Use case: Some session-history races are reachable only when a load
request arrives from the UI process between event-loop pumps — while the
session history traversal queue is mid-drain. A load started from script
enters navigate() inside the calling task, and claims the ongoing
navigation up front — so it can never land in that window. And so,
without this function, we can’t write tests for those kinds of races.
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.
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.
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.
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
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..
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.
Normalize the target step again at the end of applying a history
step, since iframe removal can leave the originally computed target
step unused before the asynchronous application finishes. Let the UI
history snapshot use the same used-step mapping when it serializes a
current item for the UI process.
Handle late child-frame navigation commits whose original nested
history entry disappeared before finalization. Removed iframes have no
live nested history list to update, and initial about:blank still needs
its first real navigation to replace the remaining initial child entry.
Add coverage for iframe pushState before nested history readiness and
for removing and recreating an iframe after an initial same-document
history update.
The static position of an absolutely positioned inline child is the
in-flow insertion point on the line where it appears. Previously this
was reconstructed after layout by walking previous siblings for a
line-box fragment. That lookup could match a fragment from an earlier
line and collapse multiple abspos children onto the same position,
especially in white-space preserving content.
Instead, drop a zero-width static-position marker into the line box at
the insertion point when each abspos child is encountered. The marker is
carried through normal line post-processing, including float intrusion,
text-align, justification, trailing-whitespace trimming and ellipsis, so
the final static position is resolved from the line itself.
Resolve marker-only trailing lines before removing them, so they can
provide static position without contributing line height.
The bytecode dump path only writes directly to stderr now.
Remove the unused string-returning dump API.
Also remove the private helper mode that only existed for that API.
The Rust bytecode generator now owns basic block construction.
The old C++ BasicBlock class no longer has any users.
Label no longer needs to translate from BasicBlock.
Remove the now-empty Label.cpp from the build as well.
Move the execution context program counter update from ASM_TRY() to the
generated slow-path call boundary. Slow paths still enter C++ with the
current bytecode offset visible to stack and source location code, while
ASM_TRY() only handles completion unwrapping and exception dispatch.
Have generated AsmInt calls pass the current instruction pointer as a
third argument to slow-path handlers. This lets the C++ handlers use a
typed Op pointer directly instead of refetching bytecode from the VM and
recomputing the instruction address from the program counter.
Remove the optional slow path hit counters from AsmSlowPaths.cpp. This
also drops the registration call from the AsmInt entry path, leaving
slow paths focused on executing the out-of-line instruction behavior.
Remove the stale bytecode execution debug hook from Interpreter.cpp now
that bytecode dispatch always enters AsmInt directly. The remaining
bytecode dump flag is separate and still used by parser/codegen paths.
Remove the empty AsmInterpreter wrapper and the VM::run_bytecode()
trampoline now that the bytecode interpreter only enters AsmInt. Move
the stack-limit check and generated assembly entry call into
run_executable(), then drop the stale wrapper source file and includes.
Remove VM helpers that became unused after bytecode execution stopped
using the generic interpreter path. The AsmInt entry path now owns these
transitions directly.
Move the remaining simple SetLexicalEnvironment, IsCallable and
LeavePrivateEnvironment opcodes into the AsmInt DSL. These handlers do
not need C++ slow-path support.
Move the C++ slow paths used by AsmInt into their own translation unit.
This leaves Interpreter.cpp focused on VM entry and bytecode metadata
helpers instead of carrying the slow-path implementation body.
Move the remaining bytecode instruction implementations out of
execute_impl() and into AsmInt slow paths. Remove the execute_impl()
bodies once their only caller is gone, leaving instruction classes as
bytecode data containers.
Remove the generic fallback dispatch once every bytecode opcode has a
real AsmInt handler. Invalid dispatch table entries still route through
the fallback function as a defensive trap.
Move property access, iterator, object property iterator, import, class
and argument-array call opcodes out of the generic fallback path. Keep
the semantic work in C++ slow paths and dispatch to them from AsmInt.
Move the remaining control-flow, conversion, creation, delete, binding,
private-name and environment-related fallback handlers into AsmInt. This
keeps the generic fallback path shrinking while leaving complex behavior
in C++ slow paths.
Move simple fallback handlers into the AsmInt DSL or dedicated slow-path
calls. This covers straightforward allocation, environment setup,
argument creation, completion state, template object, async iterator and
function allocation opcodes.
Remove the C++ bytecode interpreter dispatch loop now that AsmInt is the
only bytecode execution engine. Keep the existing AsmInt fallback path
for instructions that have not yet been moved into assembly or C++ slow
path handlers.
Previously, SVG path hit geometry was recorded in the enclosing <svg>
element's local coordinate space rather than absolute page coordinates,
so SVG shapes were only hittable when the <svg> sat at the document
origin
Problem: Crash when dragging a text selection across an element with no
layout box (e.g., a display:contents element).
Cause: set_user_selection() looks for a user-select:contain ancestor by
walking up the tree via two while-loop conditions that called
layout_node()->user_select_used_value() for each element. But elements
without layout boxes have no layout nodes. So that can dereference null.
Fix: Check layout_node() in the tree-walking while conditions.
Fixes: https://github.com/LadybirdBrowser/ladybird/issues/10062
Complete pending WebDriver navigation waits when WebContent confirms
that a same-document history traversal step was applied. These
traversals do not always produce a load event, and waiting only for a
later session history snapshot could let the WebDriver command return
before the UI had observed the applied step.
This keeps the WebDriver session history test from racing into later
commands while a previous same-document traversal is still settling.
Clear key modifier bits through their underlying integer values when
releasing WebDriver keys. The enum bitwise complement can otherwise
materialize values outside the KeyModifier enumerators, which trips
UBSan when browser history shortcut actions release Alt or Meta.
The WebDriver session history test covers this through browser shortcut
back and forward actions.
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.
Use the LibWebView history mirror to preserve traversable session
history across WebContent process swaps. WebContent reports snapshots to
the UI process, and new renderers can be seeded from the mirror.
Browser back and forward now resolve through the UI-owned used history
steps. WebContent still runs the spec traversal path when the current
renderer has enough matching state to do so.
Handle canceled and no-op UI navigations without leaving speculative
history entries or pending WebDriver waits behind. Preserve traversal
precheck state across synchronous IPC shutdown, and avoid overwriting a
restored target entry's persisted scroll state before the document has
adopted that entry.
Add a browser-side model for top-level history entries and history step
coordinates. This gives the UI process a structure to mirror WebContent
history across process swaps.
Add debug dumping support alongside the model so traversal state can be
inspected while working on back and forward behavior.
Add structured helpers for the history data mirrored by LibWebView.
Cover POST resources, history state, navigation API state, scroll
positions, and entry metadata.
Keep this below the UI model so browser-side history code can move data
through typed objects instead of ad-hoc strings.
Block-in-inline splitting can create multiple layout nodes per DOM node,
only the last of which is tracked in DOM node's `m_layout_node`.
Previously `DOM::Node::removed_from` only cleared paintable caches for
the tracked layout node, leaving the other nodes to have their caches'
cleared during the next layout update.
This was fine prior to 9340d2d, when layout nodes kept the relevant DOM
nodes alive, however, layout nodes now only keep weak references so
these DOM nodes can be GC'd before the paintables' caches are removed
causing a crash.
We now clear the paintables for the tracked layout node and it's
continued nodes during `removed_from` while the relevant DOM node is
still alive.
This is only relevant for UA-internal shadow roots (specifically those
created by `HTMLInputElement` and `MediaControls`) since they are the
only ones which can be removed from their hosts.
Previously after removing a shadow root from it's host we left it's
descendants' paintables' caches to be cleared during the next layout
update.
This was fine prior to 9340d2d, when layout nodes kept the relevant DOM
nodes alive, however, layout nodes now only keep weak references so
these DOM nodes can be GC'd before the paintables' caches are removed
causing a crash.
We now run the `removed_from` steps for the shadow tree's elements
before removing the shadow root from its host in line with how we handle
removal of DOM nodes in other cases - this clears their paintables'
caches immediately while the DOM nodes are still alive.
Fixes an intermittent crash in Layout/input/pdf-viewer.pdf
Enable the asm interpreter on Windows for both x86_64 and ARM64. The
x86_64 backend now emits COFF assembly using the Win64 ABI. It handles
argument registers, non-volatile register saves, shadow space, SEH
unwind directives, and raw-native sret lowering. Its epilogue is left
as normal x64 instructions instead of ARM64-style SEH epilogue
directives, which older ClangCL assemblers reject.
The AArch64 backend now emits Windows ARM64 COFF assembly as well,
including COFF relocations, .rdata dispatch tables, SEH unwind metadata,
frame sizing, handler alignment rules, and raw-native sret lowering.
CMake selects COFF for Windows asmint output and enables generation for
both Windows architectures.
AsmIntGen coverage checks the Windows x64 epilogue output and the ARM64
COFF unwind output. test-js and test262 have no regressions with asmint
enabled compared with the C++ interpreter.
Keep the cached equality precheck, but compute the set hash with
order-independent aggregate values instead of materializing and sorting
the per-property hashes.
This avoids allocation and sorting in a hot equality path while leaving
correctness to the existing full property comparison after hash matches.
Store the getaddrinfo result pointer directly in AddressInfoVector and
free it with freeaddrinfo from the destructor. This keeps the special
cleanup logic local to LibCore instead of relying on OwnPtr custom
deleter support.
Store UsedValues separately from the sparse layout index pages. The
pages now hold pointers for O(1) lookup by layout index, while the
values are allocated from a uniform bump allocator owned by the paged
store.
This keeps pointer stability for containing-block links and avoids
placing large Optional<UsedValues> slots directly in every page.
The <resolution> of the chosen image-set() option overrides the image's
natural resolution, so the image-set()'s natural dimensions are the
selected image's pixel dimensions divided by that resolution.
After 2D canvas stops using Gfx::Painter as its drawing backend, the
base Painter interface only needs to describe bitmap compositing work
that still happens outside PainterSkia. Narrow the interface to
clear_rect, fill_rect and draw_bitmap, which covers GIF/APNG frame
compositing, CSS cursor bitmap painting, canvas readback and the Android
UI blit.
Move CanvasRenderingContext2D onto the same record-and-replay model that
the compositor-process path will use, but keep playback local for now.
Draw calls append CanvasCommandList entries and flush/readback paths
replay the commands into a local CanvasCommandPlayer-owned surface.
Once canvas commands can be sent to another process, the receiver needs
an endpoint that owns the persistent surface and validates command data
before it reaches Skia. Add CanvasCommandPlayer for that role.
The player replays CanvasCommandList deltas through concrete PainterSkia
APIs and keeps painter state across play() calls, matching the way a
compositor-hosted canvas surface will accumulate mutations over time.
Initialize ops allocate or resize the backing surface so creation,
resize and repaint all flow through the same command stream.
Moving 2D canvas rasterization into the Compositor process needs a wire
format for canvas mutations that does not depend on LibWeb state. Add
CanvasCommandList as an apply-once log of drawing operations whose
operands are Gfx value types and whose IPC encoders can be shared by
WebContent and the compositor side.
Add class-local allocation macros for operator new/delete through AK's
malloc helpers. The macros can optionally choose a HeapPartition.
Add Layout and Painting partitions, plus basic partition stats helpers.
Use the new partitions for LibWeb layout and painting object hierarchies
and layout-state side data.
Store CSS token payloads in a variant so each token only carries the
state needed by its type. Keep delimiter, number, hash, string, and
dimension data separate instead of storing every possible payload on
every token.
Use a smaller component-value token for function and block boundary
metadata. These component values only need token type, original source
text, and source positions, so avoid embedding full token payload
storage inside every Function and SimpleBlock.
Shrink CSS source positions to explicit 32-bit counters. Guard the C++
and Rust tokenizer paths against overflow. Add size assertions for the
hot Token and ComponentValue types so future growth is intentional.
Separate MediaQueryList change reporting from stylesheet media rule
invalidation. Creating matchMedia() objects evaluates their own baseline
state, but should not make the next style update walk all active
stylesheets when the media environment has not changed.
This avoids continuous stylesheet media query reevaluation during
YouTube video playback, where repeated matchMedia() creation can make
style flushes do unnecessary work.
Add Internals.deleteAllCookies(), backed by an async WebContent to
browser request and ack pair. CookieJar can now clear transient and
persisted cookies. Note that we only delete all cookies associated
with the current URL so that tests are able to run in parallel with
one another without impacting shared cookie state.
LibWeb's WebGL implementation currently reaches ANGLE by calling glFoo()
throughout the WebGL context and extension code. That ties the WebGL
spec layer to the concrete GL executor. A future backend that records
operations, sends them to another process, or executes them from the
Compositor would otherwise need to duplicate the WebGL logic or edit
every call site again.
Introduce GLFunctions as an explicit boundary between WebGL semantics
and GL execution. GLFunctions.json lists the GL entry points used by the
implementation, and the generator emits one forwarding method per entry
point. OpenGLContext implements those methods today, so the current
in-process ANGLE path keeps the same behavior while all callers go
through a single replaceable interface.
That boundary is needed before canvas/WebGL rendering can move to the
Compositor: the WebGL context code can keep doing validation, state
tracking, and spec-visible error handling in LibWeb, while a later
implementation can record the same GL calls and replay them where the
canvas surface is produced. The JSON source also gives the recorder and
replayer one shared description of argument shapes, avoiding two
hand-written views of the GL API drifting apart.
Mach transport already sends payloads as out-of-line virtual-copy
regions, but the receive path immediately copied each payload into a new
Vector and deallocated the kernel mapping. That made the IPC IO thread
touch every byte before the main thread could decode the message.
Add ReceivedMessageBytes as the raw-message byte storage and let the
Mach transport adopt the OOL region directly. The mapping now lives
until the raw message storage is destroyed, so invalid descriptor paths
and normal queue teardown both release it through the same destructor.
Socket transports keep their existing receive copy path by wrapping
vectors in the same storage type, and the direct raw-message consumers
now decode from its ReadonlyBytes view.
MessageBuffer::transfer_message() handed a MessageDataType to transport
APIs that accepted Vector<u8> const&. That forced the inline-capacity
vector to be materialized as a plain Vector<u8>, and the Mach transport
then copied the same payload again into its pending-send queue.
Make the transport API take MessageDataType by value and pass the
encoded buffer with take_data(). The Mach pending queue now stores the
same type so the payload can move directly to the IO thread. The socket
transport keeps queued messages as owned headers plus moved payloads
instead of copying the payload into an AllocatingMemoryStream, while
preserving the existing chunked send and fd acknowledgement behavior.
The pragma directives specification steps should only run when a meta
element is inserted into the document, meaning it is in a document tree
after the insertion steps have run. We previously ran the pragma
algorithms for any insertion, so inserting a meta element with
`http-equiv=content-language` into a detached subtree dereferenced a
null document element.
Keep the Linux DMABUF handle alongside the bitmap wrapper when imported
shared images reach the UI process. This lets consumers import the same
GPU backing store directly instead of only reading it through the mapped
bitmap.
Also require exported Vulkan shared images to be sampleable, since the
Qt Vulkan presentation path needs to sample compositor backing stores.
SDL enumerates connected input devices through libudev whenever a
device is added or removed, which probes per-device metadata using
`faccessat2` and `fstatfs`. These calls run after the seccomp filter is
installed, and neither syscall was permitted, so connecting a gamepad
killed the WebContent process with SIGSYS. We now permit `faccessat`
alongside `faccessat2` because glibc's `faccessat` wrapper falls back
to it on kernels without `faccessat2`.
Keep the non-subject :has() affected bit across element style
recomputation. This bit can be discovered while matching descendant
selectors, and recomputing the anchor itself may not revisit those
selectors before a later mutation needs the dependency for targeted
:has() invalidation.
This avoids stale descendant style after targeted :has() invalidation
when a previous recompute cleared the anchor-side dependency metadata.
Repeated style invalidation tests cover the previously flaky case.
Property invalidation inside :nth-child(... of ...) used to become a
whole-subtree invalidation plan. That is broader than needed for
property changes that only affect the filtered sibling list, such as
:has() becoming true or false for one sibling.
Add an invalidation plan bit that marks the element and structurally
affected siblings instead. Keep whole-subtree invalidation when a
stronger plan already requires it, and cover both :nth-child and
:nth-last-child filters with :has() regression tests.
Use structural-position pseudo-classes as subject match filters while
building style invalidation plans. Selectors such as `.menu:has(> .flag)
> :first-child` can then carry a concrete right-hand match set instead
of widening the `:has()` invalidation plan to the whole subtree.
Keep these pseudo-classes out of trigger-property sets since structural
topology mutations are handled separately. This avoids adding unrelated
cold topology recomputes while still letting the affected boundary
children be invalidated directly.
Add a regression test that mutates the child class used by `:has()` and
asserts that the old whole-subtree path does not produce excessive no-op
style recomputations.
Feature-filter :not() arguments in :has() when the same compound also
has a concrete tag, id, class, or attribute selector. Bare negations
still stay conservative, but anchored negations no longer make unrelated
subtree mutations walk every :has() candidate.
Also avoid installing the whole-subtree :has() fallback for rightmost
complex :is()/:where() arguments that only use descendant or child
combinators. Keep the fallback for non-rightmost and sibling-combinator
cases where the existing plan cannot represent the nested selector
context.
Add counter-based style invalidation coverage for both cases.
Record hover, focus, focus-visible, focus-within, and target pseudo
classes in :has() invalidation metadata. This lets the existing
property invalidation path schedule :has() ancestor invalidation only
for scopes whose :has() selectors mention the changed pseudo-class.
Interaction pseudo-class invalidation previously scheduled :has()
ancestor invalidation for every style scope containing any :has()
selector. That kept selectors like .wrapper:has(:focus) * correct, but
also caused unrelated hover and focus changes to fan out through broad
:has() descendant invalidation rules.
Update style invalidation coverage so unrelated hover changes avoid the
extra :has() walk, while a descendant :has(:focus) rule still restyles
its affected descendants when focus changes.
Effect culling can stop a visual context switch after applying
some ancestor contexts. The player left those contexts on the painter
stack even though the target command was skipped.
Restore the painter stack back to the common ancestor when effect
culling aborts the switch.
compute_transform() resolved transform-origin to a 2D point, so 3D
rotations pivoted around the element's own plane. Build the conjugation
T(0, 0, z) * M * T(0, 0, -z) into the matrix instead; this composes
with the 2D origin at paint time into the full 3D conjugation.
Now that Badge can have multiple types, and a Badge of a derived class
can convert into a Badge of the superclass, we can simplify a few method
signatures and overloads.
Frames are considered late if the time is ahead by half their duration.
The fudging is necessary because Matroska (and perhaps other formats)
store their frame durations in different time units than their
timestamps.
Skipping these should make it clear when decoding is running behind,
instead of displaying the video in slow motion while audio runs at a
normal rate.
To give an accurate counting for video playback quality when it is
implemented, we'll most likely want to count all pulled frames in an
update as dropped if the last frame is dropped. Otherwise the frame
drop count will only increase at the display rate when decoding is
continually running behind.
This is already handled by the seeking state enum later. We could end
up displaying nothing if MovedPosition isn't immediately followed by a
displayable frame.
Firefox sends the same storage actor mutation requests for Web Storage
that it uses for cookies. Handle addItem, editItem, removeItem, and
removeAll for localStorage and sessionStorage actors.
Apply the changes through the browser-process storage jar and emit the
matching store update packets so the Storage panel stays current after a
DevTools-initiated edit.
Firefox keeps the Storage panel current by sending store update packets
when localStorage or sessionStorage changes. Forward successful Web
Storage mutations to the storage actors and emit the matching update or
clear packet.
Use listener IDs for storage updates so the local and session storage
actors can subscribe independently.
Firefox exposes localStorage and sessionStorage through the same
storage actor protocol used by cookies. Add matching resources for the
current tab so the Storage panel can list key/value pairs.
Read the values through WebContent rather than directly from the
browser process. Session storage lives in LibWeb, and using the same
path for both stores keeps the actor independent of the backing store.
Propagate the CORS-cross-origin state from image fetch responses through
SharedResourceRequest, ImageRequest, and the available image cache.
Use that state when drawing HTML images to canvas so cross-origin image
data taints the canvas correctly.
Firefox enables the Storage panel cookie toolbar from the storage traits
and calls addItem, removeItem, removeAll, or removeAllSessionCookies
for the selected host.
Advertise those operations and route them through the existing cookie
mutation delegate. Deletions reuse the visible storage host matching so
bulk actions stay scoped to the cookie tree item Firefox selected.
Firefox sends editItem when a cookie table cell is changed. Handle
that request by locating the original cookie, applying the edited
field, and forwarding the replacement through the delegate mutation
hook.
Return Firefox-style errorString values so invalid edits can be
rejected without changing the table contents.
DevTools needs to parse the same cookie expiry timestamp syntax that
Set-Cookie accepts. Expose the existing RFC6265 cookie-date parser so
callers do not need to duplicate a stricter HTTP-date parser.
DevTools needs to edit and delete cookies from the browser process. Add
a small mutation surface to CookieJar and expose it through the DevTools
delegate while preserving the existing cookie notification path.
Firefox keeps the Storage table current through storesUpdate messages
from the storage actor. Forward browser cookie change notifications to
the cookies actor and classify visible rows against a fresh cookie
snapshot.
The existing cookie-change notifications report cookies that are
relevant to a specific page, whereas DevTools wants all cookies
relevant for a host, so we end up having to provide two different sets
of cookies to `notify_cookies_changed()`.
Firefox asks the cookies actor for rows after selecting a Storage host.
Read the browser cookie jar through the DevTools delegate and serialize
matching cookies with the fields Firefox expects.
This keeps mutation support disabled, but makes existing cookies visible
in the Storage panel.
Firefox discovers Storage panel data through watcher resources. This
adds a read-only cookies actor with the fields and empty store response
expected by the Storage panel.
Mutation traits are reported as unsupported for now.
DevTools storage resources all have a "host" key derived from the URL,
which we produce in `storage_host_for_url()`.
Cookie listeners may query the jar while handling a notification. For
example, DevTools does this when the Storage panel computes whether a
cookie was added, changed, or deleted.
Insert the cookie before sending the notification, so that listeners see
the new state, not the old one.
ParentNode::get_element_by_id() only used the cached id-to-element map
when called on a document or shadow root; on any element it fell back
to a linear walk of the subtree. The document element's inclusive
subtree contains every element in the document, so the document's
cache gives the same answer.
This matters because SVGSVGElement::children_changed() resolves the
document URL fragment with get_element_by_id() on itself whenever the
document URL has a fragment. SVG sprite sheets are typically fetched
via URLs like sprite.svg#icon-name, and the sprite's outermost svg
element is its document element, so every child appended during
parsing rescanned the growing document. These lookups were 2.3% of
all CPU samples when loading chatgpt.com.
Every SVG element insertion, removal, attribute change, and children
change walked the entire document looking for use elements to notify
about possible referenced-subtree changes. On pages with large SVG
documents this is quadratic: loading chatgpt.com spent 7% of all CPU
samples in these full-document scans, nearly all of it while parsing
an SVG icon sprite sheet.
Instead, keep every use element connected to a document's node tree in
an intrusive list owned by that document, and only iterate that list
(usually empty or tiny) when an SVG element changes.
Subtleties:
- A use element inserted by the same subtree insertion as its
referenced element, but after it in tree order, used to be found by
the document-wide scan from the referenced element's insertion
steps. Now SVGUseElement::inserted() re-resolves the reference if
the shadow tree is still unpopulated. A new test covers both tree
orders.
- Node.moveBefore() runs moving steps without insertion or removal
hooks. Now SVGUseElement::moved_from() updates list membership when
moving across document-tree and shadow-tree boundaries. A new test
covers both directions.
- Removal hooks run after the subtree has been detached, so use
elements being removed alongside the changed element may still be
registered. Filter them out structurally via root().is_document(),
since Node::is_connected() is a flag that is updated in hook order
and can still be stale at this point.
Instead of comparing the current time to the duration, the playback
manager now has an explicit Ended state that jumps to the duration. The
element simply reacts to that to trigger the ended event and attribute,
along with all the other steps involved.
This moves the ended event to fire after the seeked event, which
matches other browsers' behavior. The spec doesn't explicitly say which
order they should fire in.
Since the queued task callback is already capturing the media element
weakly, we may as well directly use that in the steps that need to
interact with the element, which is the majority of them.
Also, root the element in the callback. This ensures that any queued
tasks will fire events before the element gets collected.
Instead of tracking in-flight seeks across all the sinks in the seeking
state handler, move the logic to PlaybackManager to determine the
overall status and then notify the state of that status to potentially
trigger resumption.
The buffering state handler can then share essentially the same logic
instead of having the playback manager specifically track the blocked
tracks for it.
This has no effect on the user experience, and likely also produces the
same timestamps during a seek as before. However, it is needed in order
to ensure that the future Ended state is always at the duration.
This matches the mixer's behavior, it won't mix data once it reaches
any Pending status on any input.
Doing this will allow seeking to also rely on the combined status
function to determine when to resolve.
We don't need to proactively reset this anymore with the break out of
halting being determined by seek IDs instead. This will ensure that a
halting status upon a fast-path seek will still remain correct.
Checking for a seek within the error wait loop wasn't necessary, and
unlocking/relocking could actually lose a wake anyway. Callers always
break out of the decode loop into the thread loop, so this will still
seek immediately as before.
Otherwise, we can lose the signal if a seek is aborted after it has
moved the demuxer, making the sink not output until it reaches the time
of the last data it pulled.
When scrubbing, make the timeline progress match exactly to the cursor
position. Also, set the timestamp to match that progress. Both are not
allowed to change until the scrub completes.
This makes the UI stable while scrubbing after the end of the media
data in subsequent commits that jump the time to the duration at EOS.
Whenever the controls' timeline is clicked, it pauses the media element
before seeking, then play()s on mouseup. However, the play() returns a
promise that resolves when the media actually becomes playable at the
new position. If that takes long enough that a second click pauses the
element again, then that play() promise gets rejected, and an unhandled
rejection is logged.
To prevent that, mark all play promises as handled to silence them.
Keep decoded image resources alive while they are backing a CSS image
resource for the document. Pruning these entries can make background
images temporarily non-paintable during display-list recording, causing
visible blank frames until the resource is requested again.
Otherwise, the load event will block the original document until GC
runs.
Without this, media-load-task-after-adoption.html would wait for the
idle timeout to trigger a garbage collection, which could sometimes
cause the test to time out entirely.
Add Seatbelt-based macOS sandboxing for the browser service processes.
The shared profile builder grants only the filesystem, network, Mach,
and process execution permissions each service needs, with fatal sandbox
violation reporting enabled so denials are visible during development.
Wire sandbox profiles into WebContent, WebWorker, RequestServer,
ImageDecoder, and Compositor. Keep Landlock and Seatbelt APIs visible
only on the platforms that use them. Allow RequestServer resource
substitution files explicitly, preserve read access for read-write cache
paths, and only grant renderer process execution for an existing
Cranelift helper.
Replace the homegrown GIF parser and LZW decompressor with the wuffs
GIF decoder, which is memory-safe by construction and already used in
other engines via Skia.
One behavior change is that `loop_count()` now reports the correct
value, since the raw value stored in the file does not include the
first frane and should be incremented by 1 to be compatible with what
callers expect.
This currently just contains a menu item to open about:history. But in
the future, we can add a list of recently closed / recently visited
pages as well.
This adds a WebUI to view the local browsing history, with controls to
search and delete entries. The APIs used to search history are paginated
to prevent excessive query sizes.
Add a JSObjectStorage heap partition and route heap-backed property
storage (both named and indexed element buffers) through it.
These buffers are directly shaped by script-visible object and array
operations, so keeping them separate from the general heap makes a
corruption primitive less useful against unrelated allocations.
Reserve the wasm32 virtual address space when creating an i32 memory.
Crash if the reservation fails instead of using ByteBuffer storage.
This keeps wasm32 memory on the virtual path used for fault recovery.
Move owned ArrayBuffer and SharedArrayBuffer data blocks into the
ArrayBuffer heap partition. Keep unowned and host storage explicit, so
Wasm memory and external LibWeb buffers stay outside this partition.
Introduce DataBlock::OwnedBackingStore as the LibJS-owned byte storage
representation. Expose byte spans instead of a ByteBuffer object, giving
ArrayBuffer one allocation boundary that can later grow toward guarded
or caged storage.
Let callers that need ByteBuffer data copy from backing-store bytes.
Keep TransferArrayBuffer zero-copy by moving the DataBlock directly
instead of materializing a ByteBuffer in between.
Update the Wasm typed-array test helper to compare viewed byte ranges
after ArrayBuffer stops exposing ByteBuffer identity.
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.
Replace the generated public suffix table and custom matcher with a
direct LibURL PublicSuffixData implementation backed by libpsl. This
drops our PSL download/generator path and uses the same library already
used by libcurl.
Performance is comparable before and after, while LibURL binary size
is smaller.
Mesa uses F_DUPFD_QUERY during AMD winsys initialization to check if
two DRM file descriptors refer to the same open file description. This
happens while creating WebGL contexts through ANGLE/EGL, and was being
trapped by the renderer sandbox on sites such as YouTube.
Add opt-in Linux renderer sandbox support to WebContent and WebWorker.
Ladybird and test-web pass --enable-sandbox through when requested, and
the renderer services only install the shared sandbox when that flag is
present.
Share one renderer policy for both services. Allow resource, font,
shared library, WebGL, Wasm, audio, and local IPC paths needed at
runtime, while keeping renderer filesystem writes mediated by Landlock.
Allow Mesa and PulseAudio to probe their standard runtime state without
escaping the renderer sandbox. Return EPERM for scheduler and priority
changes so library initialization can fall back instead of crashing on a
seccomp violation.
Add opt-in Linux sandbox support to RequestServer. Ladybird and test-web
pass --enable-sandbox through when requested, and RequestServer only
installs the sandbox when that flag is present.
Allow reads for resolver and TLS configuration, plus the configured
certificate locations. Create and allow writes to the Ladybird cache
root, so libcurl alt-svc files and HTTP disk-cache files stay inside the
single writable tree. Also allow the systemd-resolved runtime directory
when present, since /etc/resolv.conf can point there.
Extend LibSandbox with owned Landlock paths, an add-if-exists helper,
read/write Landlock access, and reusable seccomp groups for filesystem
writes and network syscalls. Include POSIX file locks and socket byte
count ioctls needed by libc resolver and cache paths. Reuse the new
Landlock helper from Compositor as well, and allow its Mesa shader cache
directory so GPU startup can populate shader cache files after
sandboxing.
Compositor still needs to decode font resources from display-list IPC
after startup. Let its sandbox grant read-only access to configured font
directories and bundled resource fonts through Landlock, and allow
read-only open plus metadata syscalls in the seccomp policy.
Preload the same font directories before installing the sandbox so the
Compositor can handle display lists that reference system-backed fonts.
The full test-web suite completes without Compositor sandbox crashes.
Add opt-in Linux sandbox support to Compositor. Ladybird and test-web
pass --enable-sandbox through when requested, and Compositor only
installs the sandbox when that flag is present.
Install the sandbox after Compositor has initialized platform, font, and
GPU state so startup probing can complete before filesystem access is
removed. Compose the runtime seccomp policy from LibSandbox building
blocks and add an explicit GPU device operations group for driver IPC
through already-open descriptors.
Move the Linux no_new_privs, Landlock, and seccomp policy plumbing
into LibSandbox so individual services can describe the privileges they
need without copying the BPF and kernel feature detection machinery.
Keep ImageDecoder's sandbox policy service-local by composing the new
building blocks in SandboxLinux.cpp. This preserves the existing syscall
allowlist while making the policy easier to audit and reuse.
Add --enable-sandbox to Ladybird and test-web, pass it through to
ImageDecoder, and make ImageDecoder install its Linux sandbox only when
the option is present.
The Linux implementation enables no_new_privs, configures glibc malloc
to avoid late CPU-count probes in helper threads, applies an empty
Landlock ruleset when available, and installs a seccomp filter for the
helper IPC, shared memory, threading, and decoding syscalls.
Deny plain read-only filesystem probes without granting file access, so
common runtime feature checks can observe the sandbox instead of
terminating the helper during normal decoding.
The shaping cache previously stored HarfBuzz buffers keyed by string,
and `shape_text()` rebuilt a fresh `GlyphRun` on every call. Cache the
font-independent shape data instead, so repeated shaping of the same
input skips both the HarfBuzz call and the glyph-vector build.
We're going to implement the `contrast-color()` CSS function in the next
commit, and the spec advises to use the contrast ratio definition as
described in WCAG 2. So let's replace our `Color::contrast_ratio()`
implementation by that.
Co-authored-by: InvalidUsernameException
<InvalidUsernameException@users.noreply.github.com>
The spec doesn't say how to handle this so we just match Chrome's
behavior of throwing a `TypeError` (without clearing the existing
value).
Fixes#9969.
Fixes#9970.
The `CopyDataProperties` AO is used to implement object spread syntax.
The generic path calls `[[OwnPropertyKeys]]` to materialize a key list
and then performs a separate lookup through ``DescriptorArray::find()`
for every key.
For ordinary objects we now instead walk the shape in insertion order
and read each value directly by its storage offset, avoiding both the
key-list allocation and the per-key descriptor lookups. The fast path
is guarded to ordinary objects with no intrinsic accessors, no exotic
indexed access, packed-or-simpler indexed storage, and no excluded
values, falling back to the generic algorithm otherwise.
This is roughly 2x faster on a tight object spread microbenchmark.
Remove internals.loadTestVariants and the IPC forwarding that reported
variant metadata back to WebView. test-web now identifies WPT variants
during collection, so no loaded document needs to expose this test-only
hook.
Stop emitting every generated CSS property accessor as an IDL attribute.
Instead, generate a compact CSSStyleProperties initializer that installs
all property aliases from a table and dispatches through one native
function class carrying the UTF-16 property name.
This keeps the generated binding file focused on cssFloat and moves the
large property list into a simple generated table.
Remove the PropertyNameAndID::from_name overload that accepted
FlyString. Parser declarations still store their token names as
FlyString, but the conversion to UTF-16 now happens explicitly at those
boundaries.
Move DescriptorNameAndID to Utf16FlyString so CSS descriptor APIs can
keep using the CSSOM property-name string without round-tripping through
FlyString.
Keep parser declaration tokens and DevTools error payloads on their
existing FlyString types at their boundaries.
Move StylePropertyMap, StylePropertyMapReadOnly, and the CSS.supports
property-name overload to Utf16FlyString. Request the UTF-16 binding
conversion path for their IDL property-name arguments.
Move PropertyNameAndID, custom property data, registered custom
properties, and Typed OM associated property names to Utf16FlyString.
This removes the FlyString storage boundary from CSS property-name
handling and lets CSSStyleProperties keep the name it receives from
CSSOM instead of converting it back to UTF-8.
Move the remaining CSSStyleDeclaration property-name APIs to
Utf16FlyString. This lets CSSOM binding and generated accessor code
pass JS property names without first constructing FlyString values.
Keep internal custom-property and descriptor storage unchanged for now.
Those remaining FlyString conversions are at storage boundaries that
will be migrated in follow-up commits.
Change get_property_value() to take a Utf16FlyString so generated CSS
property accessors can pass their JS property names through without
constructing FlyString instances first.
Keep the existing internal descriptor and custom-property storage shape
for now, and convert at those boundaries while the remaining CSS
property APIs are migrated separately.