Allow having separate GC heaps and implement coordinated marking between
them; this is useful for keeping wasm and js GC heaps separated with a
clear boundary.
Previously, these were stored in a vector that was linearly scanned.
For large sites this vector could contain hundreds of entries, so
a HashMap gives a significant speedup.
Build a per-anchor Bloom-style filter for :has() argument matching
after an anchor sees a second check for the same traversal scope. The
filter stores salted tag, id, class, and attribute-name hashes from the
child or descendant scope and rejects arguments whose required
identifiers are absent.
This avoids repeatedly walking the same subtree for unrelated :has()
arguments while preserving the single-check case. More complex
direct-child arguments use the descendant scope so hashes from later
descendant compounds cannot cause false rejections.
Keep the filter conservative for quirks-mode class selectors and for
sibling-combinator relative selectors during invalidation metadata
collection. Text tests cover cache-primed misses for both cases.
Firefox exposes removal operations for IndexedDB databases, object
stores, and records from the Storage panel. Route those actor requests
to LibWeb so DevTools can delete the selected data and receive the
update or clear event needed to refresh the panel.
The Storage panel expects storesUpdate messages after watched storage
changes. Summarize committed IndexedDB mutation logs into DevTools paths
and forward them through WebContent so Firefox can refresh database,
object-store, and record rows without polling.
Firefox's behaviour is less than ideal here. A lot of things don't
update automatically even inspecting a page in Firefox. Some
things (like new databases) won't show up until you fully refresh the
page. So that makes it a bit hard to know that we're doing things
correctly. As far as I can tell, we are at least behaving as well as
Firefox requires.
We do have one workaround: Firefox doesn't display record updates
without a manual refresh, and in fact any change messages for them show
up as rows in the host's database table. So for now, we filter them out
to avoid visual weirdness in the inspector.
Firefox asks the storage watcher for an indexed-db resource before it
shows IndexedDB entries in the Storage panel. Add an IndexedDB actor and
serialize the live LibWeb database registry on demand, so WebContent can
return the host tree and table rows without duplicating database state.
Use the LibWeb inspection helpers to read IndexedDB internals, and keep
the Firefox protocol shape in LibDevTools. WebContent only forwards the
serialized response over the existing DevTools IPC path.
DevTools needs to inspect IndexedDB state from WebContent, but the
storage registry and object store data live behind LibWeb internals.
Expose a small inspection API that can enumerate IndexedDB hosts,
databases, object stores, and records without making DevTools know about
the internal database objects directly.
Storage actors all describe table columns with the same DevTools field
object shape. Extract a helper for constructing those objects so that
cookies, local storage, session storage, and soon IndexedDB, do not each
spell out the JSON fields by hand.
Problem: A borked ImageDecoder could send a BitmapSequence over IPC with
metadata for a (large) bitmap while shipping a too-small backing buffer.
Decoding it produced a Gfx::Bitmap that reported the (large) geometry
but pointed at the too-small buffer — making the first write go OOB.
Cause: BitmapSequence decode reads size_in_bytes and the bitmap geometry
as independent fields, and only checked if size_in_bytes matched the
transferred buffer size — never that either is consistent with the
geometry. The single-frame fast path then handed the buffer to
Bitmap::create_with_anonymous_buffer with no verification.
Fix: Make the two bitmap factories that take externally-provided storage
enforce that it covers the geometry. create_with_anonymous_buffer now
fails with buffers smaller than the minimum expected size_in_bytes — and
create_with_raw_data similarly rejects data too small for the geometry.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/10036
Previously passing a file path (rather than a directory) to
`add_landlock_path_if_exists` would grant access to the containing
directory - this was a bit of a footgun.
We now only grant access to exactly the path requested, which is in line
with how the MacOS implementation works as well.
None of the callers actually passed `ReadonlySpan<StringView>` so we can
achieve the same thing by giving a default argument to the
`ReadonlySpan<LandlockPath>` overload.
Problem: A borked process sending a Gfx::Bitmap (inside BitmapSequence)
or a Gfx::ShareableBitmap over IPC could set BitmapFormat::Invalid as
the format field. The receiving process then aborted while decoding the
message — an IPC-reachable crash.
Cause: The helper that both decoders use for validating the format read
off the wire accepts BitmapFormat::Invalid. The decoders go on to build
a bitmap with that format. But that triggers an assert — because
minimum_pitch only knows the four real pixel formats.
Fix: Drop BitmapFormat::Invalid from is_valid_bitmap_format. It’s an
indicator of an absent/unknown format, never one a real bitmap can have.
And any real bitmap is never encoded with it. So, both BitmapSequence
and ShareableBitmap decode now return a clean decode error for it.
The autoplay setting was binary and its default blocked all media,
including muted video, leaving sites that rely on muted autoplay
visibly broken. Replace it with a tri-state user-agent autoplay
policy (allow audio and video, block audio, or block audio and video)
defaulting to allowing only inaudible media to autoplay.
This is enforced through the media element's "allowed to play" check,
so unmuting a muted autoplay or calling `play()` cannot slip audio
past the policy; audible playback is permitted once the document has
been activated by the user. The policy lives in a dedicated
AutoplaySettings consulted from HTMLMediaElement instead of the
Permissions Policy "allowed to use feature" check it was previously
conflated with.
For selectors whose rightmost relation is an immediate child combinator,
collect hashes that must be present on the subject parent and reject the
rule early when the current parent cannot satisfy them. This avoids full
selector matching for common child-combinator tails.
Only shadow hosts can match :host selectors, so route them through the
subject pseudo-class bucket instead of the generic other bucket. Full
selector matching still validates the shadow-root context and any
arguments.
Split pseudo-element rules whose originating compound contains :is() or
:where() across the cheap buckets from their selector-list alternatives
when the rule would otherwise land in the pseudo-element other bucket.
Use per-collection generation stamps to suppress duplicate candidates
when more than one alternative bucket applies to the same originating
element. Add text coverage for class, attribute, and complex combinator
arguments reaching generated pseudo-element style.
Treat :heading as a subject pseudo-class bucket so heading rules are
only enumerated for h1 through h6 elements. The selector matcher still
checks functional :heading() levels, so the bucket only narrows rule
collection before normal matching.
Add a late rule-cache bucket for selectors that do not expose a
current-element bucket but do expose ancestor-filter metadata. These
rules previously stayed in the global other bucket and relied on the
ancestor filter after broad enumeration.
Visit the new buckets only when the current ancestor bloom filter may
contain the bucket hash. Keep pseudo-class invalidation caches opted
out because those caches answer targeted invalidation questions rather
than normal style collection.
On the GitHub profile, other-bucket candidates fell from about 5.50k
to about 1.07k per 1k rule collection calls.
Put rules whose rightmost compound has no id, class, tag, or
attribute bucket into a subject pseudo-class bucket when they have a
required cheap element-state pseudo-class such as :hover, :focus, or
:checked.
Also let :is() and :where() selector lists use a regular bucket when
every alternative exposes the same mandatory bucket key. Pseudo-class
invalidation rule caches keep using broad buckets because they must
consider rules while the matching state is changing.
The Mach transport always sent the serialized IPC bytes as an
out-of-line descriptor. That asks Mach to transfer the payload through a
VM region even when the message is only a small control message.
Use a second data message ID for inline bodies whose full Mach message
fits within 4 KiB. Inline messages carry the unpadded payload length
before the serialized bytes so the receiver can ignore Mach message-size
padding, while larger messages keep using the existing out-of-line
descriptor path.
When :is() or :where() appears in the subject compound, collect hashes
from ancestor compounds inside its argument selectors. Keep only hashes
shared by every alternative, and do not treat the argument selector
subject as an ancestor.
Separate ancestor filter hashes for tag names, ids, classes, and
attributes. This avoids false positives where identical strings in
different selector component kinds satisfy each other, while keeping the
filter conservative.
Collect hashes from :is() and :where() selector lists by keeping only
hashes present in every alternative. This lets the ancestor filter
reject more selectors without changing matching semantics.
Look through single-argument :is() and :where() pseudo-classes when
collecting ancestor-filter hashes. Reuse hashes from the rightmost
compound of the argument selector, including nested wrappers, because
that compound matches the same ancestor element.
Keep selector lists with multiple alternatives conservative, since no
single hash is mandatory for every branch.
Look through single-argument :is() and :where() selectors when
selecting a rule-cache bucket. Use mandatory ids, classes, tag names
or attributes from the rightmost compound of the argument selector,
including nested :is() and :where() wrappers.
Leave multi-argument lists in the broad bucket, since a single key
would not be mandatory for every alternative.
Keep pseudo-element style rules out of the normal element rule buckets
while preserving the same id, class, tag, attribute and root buckets
inside each known pseudo-element type. This avoids collecting pseudo
rules for normal element style, without broadening pseudo style
collection to every rule targeting the queried pseudo-element.
Update the has invalidator to walk both the normal rule buckets and
the pseudo-element bucket maps when deciding whether pending :has()
mutations may affect style.
2D canvas contexts started publishing partial frames after canvas
rasterization moved into the Compositor. WebContent still splits large
recorded command lists after 64 commands, but every split batch was sent
through the same compositor path as the end-of-frame flush. The
compositor replayed each batch into the DrawCanvas source surface,
so a pending present could sample a canvas after clear and before the
rest of the next frame had been drawn. Canvas-heavy pages such as
slither.com then flickered between partial and complete frames.
Carry an explicit commit bit with 2D canvas command updates. Non-commit
batches now update a hidden working canvas in the Compositor, while the
display-list-visible surface keeps the last committed canvas contents.
The end-of-frame canvas preparation sends the commit boundary, including
the empty-commit case needed when the auto-flush consumed all recorded
commands before prepare_for_compositing() runs.
Firefox can split a DevTools protocol packet across multiple TCP
reads. The previous reader only checked that some data was available,
then tried to synchronously read the whole length-prefixed packet from
the readiness callback. Once the socket was nonblocking this could make
startup flaky when Firefox opened the inspector.
Buffer incoming bytes instead, and only dispatch messages once a full
length-prefixed JSON payload has arrived. Add a protocol test that sends
a request in two fragments through the real DevTools server.
Most invalidation sets carry at most one selector feature. Storing
every set in a HashTable pays allocation and hashing costs while
constructing style invalidation data, even in the common case.
Store the first property inline and upgrade to HashTable only when a
second distinct property is inserted. Keep hash and equality checks
direct over the backing so those hot paths do not route through the
public callback iterator.
Selecting text without custom ::selection styling changed the
foreground color of the selected content. This was especially visible
for links, where the text changed color but the underline did not.
The default selection style supplied both a selection background and a
foreground color from the palette or HighlightText system color. That
made ordinary selections behave as if the page had explicitly styled
::selection color.
Only provide a default selection background, so selected content keeps
its own foreground color unless CSS overrides it. Remove the now-unused
SelectionText palette role.
Track the synthetic pseudo-elements that matched while computing an
originating element's normal style. Store the transient match set as a
bitfield, then copy those bits into ComputedProperties. Use them during
style invalidation to skip pseudo style recomputation when neither the
old nor new originating style matched pseudo rules and no pseudo style
already exists.
This shaves roughly 500 ms off loading the Ladybird GitHub repository.
Materialize synthetic pseudo styles on demand for CSSOM reads so
getComputedStyle(element, "::before") still computes skipped styles when
script asks for them. Add coverage for a universal pseudo selector, and
update style invalidation counter expectations for the reduced work.
Previously, math function serialization assumed the calculation tree
root was a numeric value or a calc-operator node, and otherwise emitted
the root's name followed by its comma-separated children.
A non-math function node such as `sibling-index()` or `anchor()` has no
children, so a `calc()` whose entire contents was such a function
serialized to an empty "calc()". We now serialize the function directly
instead.
Split StyleCache's rule matching data from its invalidation metadata.
Allow callers to build either payload independently. Style invalidation
queries no longer force a full rule cache rebuild, and rule matching
no longer builds invalidation metadata as a side effect.
Previously, callers often treated an absent rule cache as proof that no
style invalidation metadata existed. That made some invalidation paths
do nothing until something else had populated the rule cache first.
Build invalidation data before reading it instead, so these paths use
the metadata whenever stylesheet rules require it.
Rebaseline style invalidation counter expectations for the new lazy
build points. Flush setup style work in the structural :has() feature
filter test before measuring each mutation, so the recomputation
counters describe the mutation itself instead of leftover setup work.
Changing :active or :open used the broad style invalidation path, so
large subtrees were recomputed even when only the target element and
selector-matched relatives could be affected.
Reuse the :has() feature collector to keep conservative fallback for
observable :has() cases, then use pseudo-class property invalidation
for the common targeted path.
Problem: Crash when removing a <mask>, <clipPath>, or <pattern> element
that’s referenced via url(#id), and then GC’ing it.
Cause: <mask>, <clipPath>, and <pattern> are laid out as resource boxes
attached to the referencing element's layout subtree, rather than their
own. So those survive cleanup of their DOM ancestor. Once a removed
<mask>/<clipPath>/<pattern> is collected, its resource box’s weak DOM
node pointer is null. And dereferencing that trips an assert — both
while painting and while tearing down the layout tree.
Fix: When a <mask>, <clipPath>, or <pattern> is removed, request a full
layout-tree update while the element’s still alive. That drops the stale
resource boxes (whose url(#id) references no longer resolve) before the
node gets collected.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/10127
Keep regular rule cache invalidation from discarding the parsed user
style sheet. User style and content blocker source changes now use an
explicit invalidation path that clears the parsed sheet before the rule
cache is rebuilt.
This avoids reparsing user CSS for unrelated style changes, which was
very annoying when browsing with cosmetic CSS from content blockers.
Store MouseEvent's relatedTarget in the inherited Event field instead of
keeping a second slot on MouseEvent.
Event dispatch retargets and updates the inherited field while building
the event path. The second slot left JS listeners observing stale or
null relatedTarget values during mouse and pointer boundary events.
Add coverage for boundary events between sibling elements.
b583fd skipped display list invalidation for AVC only style changes
which also meant that we no longer marked the navigable as needing a
repaint or requested the next frame.
This commit updates `set_needs_accumulated_visual_contexts_update` to do
so.
This makes the animation when hovering icons on
https://chrede88.github.io/L1nkr paint intermediate frames not just the
first and last.
Previously we didn't support interpolating component values of `scale`
from a number to a percentage (or vice versa). This also caused
interpolation from `none` to a percentage value to fail since the
fallback value is number based.
This makes the transition run when hovering buttons on
https://chrede88.github.io/L1nkr/ rather than being discrete.
When applying style in `Layout::Node::apply_style` we previously ignored
null `rotate`, `translate`, and `scale` values which left the old values
in place in the case of nonnull -> null changes.
Fixes#10125
In 11b053b154 I accidentally changed the
behaviour of CookieJar::set_cookie() to not match what the RFC
requires, particularly when dealing with too-long paths. This commit
restores the original behaviour, now that the validation required by
DevTools happens elsewhere, before set_cookie() is called.
DevTools edits cookies as concrete fields rather than Set-Cookie
strings. Validate direct Cookie objects while converting them back to
ParsedCookie so invalid edits can be reported without making the RFC
storage algorithm stricter.
The Cookie to ParsedCookie conversion stripped a leading dot and
lowercased the cookie domain itself. Use the existing Domain attribute
parser instead, so edited cookies follow the same ASCII validation and
normalization as Set-Cookie parsing.
Problem: Drawing a cross-origin image onto a 2D canvas clears its
origin-clean flag, but toDataURL() and toBlob() ignored that flag and
serialized the bitmap regardless. So, a page could read back the
cross-origin pixels it shouldn't (per spec) be allowed to access.
Cause: The origin-clean checks in to_data_url() and to_blob() were left
as FIXMEs. Only getImageData() enforced the flag.
Fix: Throw a SecurityError exception from both serialization entry
points when the canvas isn't origin-clean — matching getImageData() and
the spec. The same check also implements the previously-stubbed
origin-clean step in the WebDriver canvas-encoding algorithm.
Fixes: https://github.com/LadybirdBrowser/ladybird/issues/10009
Previously animation ownership was a messy split between
`AnimatedBitmapDecodedImageData` and the consumers (i.e.
`ImageStyleValueResource`, `HTMLImageElement`, and `SVGImageElement`)
with `AnimatedBitmapDecodedImageData` owning the frames and a current
frame index, and the consumers owning the rest of the state (e.g. loop
count, timers to drive the animation forward, their own current index).
This had a couple of main issues:
- While `AnimatedDecodedImageData` partially synchronized animations by
dropping unexpected advancement notifications, this didn't apply to
other animation state which meant, for instance, that a later started
consumer could drive the animation of an earlier one past the max
loop count (albeit without invalidating the earlier consumer).
- Multiple consumers didn't share frame timings, meaning animations
could be up to a full frame out of sync visually.
- Animations were paused depending on whether there were any consumers,
this is different to the behavior in other browsers (where they
continue regardless of whether there are any consumers).
- It was an overgeneralization of how animations need to work - only
`AnimatedBitmapDecodedImageData` works with an indexed frame model,
with animated SVGs (although not yet implemented) relying on their
internal event loop to be driven forward.
Given the above the new approach implemented in this commit is:
- The API for `DecodedImageData` is animation system agnostic, only
exposing `default_frame`, `current_frame`, and `restart_animation`
methods not reliant on providing a specific frame index.
- `AnimatedBitmapDecodedImageData` owns its own timer, loop count,
etc. The animation starts when the first consumer registers and ends
when the document is hidden or becomes inactive (or completes in the
case of finite animations).
- Consumers are invalidated by `AnimatedBitmapDecodedImageData` when
required.
Tests have been added for:
- Animations being paused when the document becomes inactive and
restarted when it becomes active again.
- Frame timings being synchronized across consumers.
- Restarts triggered by `HTMLImageElement` applying to all consumers.
- Processing ending once a non-infinite animation plays to completion.
The tests to ensure animations are cancelled when consumers are removed
(e.g. `animated-background-image-timer-stops-when-hidden.html`) have
been updated to assert the inverse since animation state is now per
resource not per consumer.
To `AnimatedBitmapDecodedImageData`. This better reflects what it is and
frees up `AnimatedDecodedImageData` to be used as an abstract class in a
later commit.
In a future commit, ownership of animation will be transferred from
these clients to `AnimatedDecodedImageData` and we will need a way to
invalidate them for new frames.
This also revealed some `ImageProvider`s which don't yet support
animated images (e.g. `<input type="file">`, `<object>`, etc) but that
is left as a FIXME for now.
`ImageStyleValueResource` now holds a reference to a
`HTML::SharedResourceRequest` for it's entire lifetime.
We also `VERIFY` that we have created a resource (by registering a
client) before calling `ImageStyleValue::image_data` rather than
silently failing.
Previously image animations driven by `ImageStyleValueResource`
invalidated clients using `on_animate`, this was only implemented by the
`background` presentational attribute of `HTMLBodyElement`.
It now uses `notify_clients_did_update` which is implemented by all
clients.
All other callers of `SharedResourceRequest::fetch_resource` guard based
on `needs_fetching` so let's do that here as well (and add a `VERIFY` so
that future callers don't make the same mistake).
This allows us to remove the `frame_rect` accessor.
This also fixes a bug where we computed the scaling mode based on the
clipping rect rather than the draw rect for `ImagePaintable`
Previously we used the `frame_rect` size, this is the same as the
intrinsic size for bitmap images but is `OptionalNone` for SVG which
caused us to always fall back to the `image_rect` and thus not apply
any scaling for SVGs regardless of whether they had intrinsic sizing.
WebContent is about to stop using the broad GPU device sandbox
allowance. That also removes the accidental permission for every ioctl,
which exposes ordinary terminal attribute queries made while printing
diagnostics.
The media decode error path can ask stderr for TCGETS2 while reporting a
corrupt video frame. Allow that narrow fd metadata query alongside
TCGETS, FIONBIO, and FIONREAD so renderer processes can keep the GPU
ioctl denial without crashing the media decode-error coverage.
WebContent no longer chooses between CPU and GPU display list players,
and the remaining callers always use Skia raster playback. Remove the
PageClient virtual and now-single-value enum, then play SVG image and
cursor display lists directly.
Canvas and display list rasterization now run in the Compositor
process, so WebContent no longer needs its own Skia GPU backend. Drop
the WebContent --force-cpu-painting option and stop forwarding it when
launching the renderer. The flag remains available for Compositor.
Instead of operating within the (saturating) CSSPixels constraints,
calculate the expected value using a floating point calculation first
and then create the CSSPixels value.
Problem: Discarding a document that contains an SVG “use” element could
abort the process with a !is_in_list() verification failure in the
IntrusiveListNode destructor. That surfaced intermittently in our style-
invalidation stress tests, depending on GC sweep order.
Cause: A “use” element connected to a document registers itself in the
document’s list of “use” elements and unregisters during its removal
steps. A GC’ed “use” element is swept without running those removal
steps — so it stays linked. When it’s destroyed before its document,
its list node is still linked — and the destructor aborts.
Fix: Override finalize() to unregister the “use” element before
destruction. The collector finalizes every dying cell before destroying
any of them. So, the node is always unlinked in time — the same approach
DocumentObserver and NavigationObserver already use.
Nested navigables were represented through compositor surface ids owned
by the parent context. That forced CompositorState and ContextState to
maintain bidirectional attach/detach bookkeeping, publish child
snapshots into a surface map, and keep presentation mode variants just
to distinguish UI presentation from parent composition.
Record the child compositor context id directly in the display list and
let the compositor resolve it against the painting parent at playback
time. Child contexts now keep their parent context id and latest
rendered surface, while parents no longer track child maps or compositor
surface ids. UI presentation is represented separately from parent
composition, so closing a page only stops client presentation and nested
contexts keep using set_parent_context.
WebWorker control connections can transfer handles from the browser
process to the worker process, including RequestServer and ImageDecoder
transport handles sent during worker startup. On Windows, serializing
those attachments needs the destination process id so DuplicateHandle
and WSADuplicateSocketW can target the peer process. WebWorker was
excluded from the generic helper-process InitTransport handshake,
leaving the transport without a peer pid before any attachment-bearing
message was sent.
Add InitTransport to the WebWorker server endpoint, implement the
server-side peer pid exchange, expose the message type through
WebWorkerClient, and let the shared helper launcher perform the
handshake for workers as it does for other Windows IPC clients.
Let style changes that only rebuild compatible accumulated visual
contexts avoid marking the display list dirty. This lets transform
and nonzero opacity updates send visual context tree updates without
recording a new display list.
Keep repainting changes that affect display-list contents or can change
visual context tree compatibility, including zero-crossing opacity,
transform invertibility crossings, background-attachment, clipping,
mix-blend-mode, and perspective. Schedule accumulated visual context
updates for animations independently of repaint so animated
transform/effect updates keep reaching the document.
Cover compatible visual context reuse, incompatible tree shapes, and the
display-list invalidation cases with focused LibWeb tests.
Let accumulated visual context updates keep the previous tree version
when rebuilt with the same shape. Display lists reference visual
context tree versions, so keep compositor-only updates on the old
version unless the tree structure changes.
Add coverage for version reuse and incompatible tree shapes.
The interpreter's fast path for PutByValue on a typed array treated an
out-of-bounds index as a silent no-op and returned without touching the
value. That is observably wrong: TypedArraySetElement evaluates
ToNumber(value) before checking the index, so a value with a valueOf
side effect must still have that side effect run even when the store is
ultimately discarded.
Fall back to the slow path on an out-of-bounds or otherwise invalid
index instead of reporting success. The slow path runs the full
TypedArraySetElement algorithm, which performs the coercion and then
discards the write. Direct assignment now matches Reflect.set, which
already went through the slow path.
Fixes the staging/sm typed array out-of-bounds ToNumber test262 case
and adds a test-js regression covering direct assignment, Reflect.set,
and Reflect.defineProperty.
The JSON.parse-with-source proposal (now part of ES2026) gives a
reviver a third "context" argument. For a primitive value that was
not modified by an earlier reviver call, the context has a "source"
property holding the matched JSON source text; for objects, arrays,
and forward-modified values it is an empty object.
We already had JSON.rawJSON and JSON.isRawJSON, but the reviver only
received two arguments. Implement the missing half by building a JSON
Parse Record snapshot while parsing: each primitive records the
trimmed raw token from simdjson, and arrays and objects record their
child records keyed by index and property name. InternalizeJSONProperty
threads the matching record down the tree, creates the context object,
and only attaches "source" when the record's stored value still equals
the live value (SameValue), which suppresses source for values a
reviver replaced or appended.
The record values live in heap storage the GC does not scan, and a
reviver can detach the originals from the object graph mid-walk, so
the snapshot's values are kept rooted for the duration of the walk.
Closes the six json-parse-with-source test262 failures and adds
test-js coverage for primitive source text and forward modification.
Compositor surfaces are only used for nested navigables now. The
display list command already carries the CompositorSurfaceId, but
playback still resolved that id through DisplayListResourceStorage and
WebContent exposed IPC for direct surface updates and clears.
Keep published child surfaces as PaintingSurface entries on the
compositor ContextState and pass that map into Skia display list
playback. Publishing and detaching nested contexts now update the parent
cache entirely inside the compositor, so WebContent no longer needs
update_compositor_surface or clear_compositor_surface messages.
DisplayListPlayerSkia kept a separate DecodedImageFrameSkiaImageCache
that was pruned during flushes. That made Skia image lifetime
independent of display list resource lifetime, even though resource
storage is what knows when image frames and compositor surfaces are no
longer needed.
Make DisplayListResourceStorage own an opaque stored image-frame
resource that holds the decoded frame and its lazily-created SkImage.
Removing image frames or compositor surfaces now drops the decoded frame
and Skia image together, while transactions still carry only Skia-free
decoded frames.
LibWeb still needs ANGLE GLES headers for WebGL constants and
types, but the host GL entry points now live in the Compositor. Copy
ANGLE compile interface onto LibWeb and stop linking ANGLE through
LibWeb so WebContent no longer inherits that dependency.
2D canvas rendering now lives in the compositor, but drawing one canvas
into another still converted the source HTMLCanvasElement into a
DecodedImageFrame in WebContent. That forced a compositor readback for
every drawImage(canvas, ...) call before sending the destination canvas
commands back to the compositor.
Teach the canvas command stream to carry a DrawCanvas command that names
the source canvas surface. The destination 2D context now flushes the
source canvas, records that command, and immediately flushes the
destination command list so the compositor copies the source surface at
the drawImage call boundary. Bitmap sources continue to use DrawBitmap,
and true readback APIs still read pixels explicitly.
Compositor-backed canvas contexts keep their transports tied to a single
Compositor connection. When that connection dies, the 2D backing
storage and WebGL GL objects disappear with it, but WebContent does not
surface the loss to canvas contexts or create fresh host contexts after
reconnect.
Track compositor loss through the WebContent connection, mark WebGL
contexts lost, dispatch the standard context events, and rebuild the
remote proxy when the page opts into restoration. For 2D canvas, queue
the canvas context loss steps, discard the dead backing storage, and
create new storage before firing contextrestored.
Canvas rendering is a major remaining path where WebContent directly
owns GPU-facing drawing state. Back 2D and WebGL canvas contexts with
remote Compositor transports, so WebContent talks to canvas surfaces
through IPC while the Compositor owns the rasterization resources.
This is a large step toward GPU sandboxing because canvas GPU work now
lives behind the Compositor boundary. It also gives OffscreenCanvas the
process-independent canvas plumbing that HTMLCanvasElement now uses,
making worker-owned canvases possible without another WebContent-local
rendering path.
The display list can now refer to canvas ids, but WebContent still had
no channel for creating or updating those canvas resources in the
Compositor. Both 2D and WebGL canvases would have had to grow the IPC
plumbing in the same commit that changes the rendering contexts.
This adds the Compositor-side CanvasHost, WebContent transport objects,
and the IPC/CMake pieces needed to allocate, update, read back, and
destroy remote canvas contexts. The rendering contexts are not switched
over yet, keeping this as plumbing for later commits.
Moving WebGL execution into the Compositor needs a serializable command
stream and a client-side proxy that can queue commands before sending
them over IPC. The existing generator metadata only described direct GL
wrappers, so generated code could not distinguish async commands from
sync calls or object factory methods.
This teaches the WebGL metadata and generators about command streams and
adds the unused LibWeb proxy/list types. No rendering behavior changes
yet; the later host wiring can build on these generated interfaces
without mixing the metadata churn into that commit.
Canvas contexts need a compositor-owned surface path that can be shared
by 2D canvas and WebGL. Add CanvasId and a CanvasSurfaceRegistry, pass
the registry into display-list playback, and teach Skia playback how to
resolve and draw a registered canvas surface.
This only adds the shared display-list command and registry plumbing.
Existing canvas elements still publish their old compositor surfaces, so
the behavior change is left for the later canvas-host commits.
Problem: Message::to_raw could only encode queries: It asserted the
answer and authority counts were zero and never wrote those. So, a
response — which carries answers — couldn’t be serialized at all.
Fix: Write the answer and authority records as well — alongside the
existing question and additional sections, and using the existing
ResourceRecord::to_raw. Then drop the query-only assertions. The order
matches the wire format: question, answer, authority, additional.