In cases when we build only host tools we almost always want to install
all the targets that are there. Lock it down to only host tools as
installing when all targets are build will install a bunch of things
that are probably not intented to be installed.
Remove the missing overrides for wayland and wayland-protocols and
make sure all overrides follow the scheme of version#port_verison.
This will be verified after a later commit that adds a linter script.
DecodedImageFrame now owns decoded bitmap pixels directly, so the
separate ImmutableBitmap wrapper no longer carries useful semantics.
Remove the class and pass decoded image frames or bitmaps at the
boundaries where pixels are actually required.
The Skia image cache now keys off DecodedImageFrame, matching the
display-list commands that paint decoded images. Video frames stay
owned by LibMedia, with the explicit YUV-to-bitmap conversion living
at HTMLVideoElement's decoded-frame entry point for canvas and WebGL
callers.
Decoded image data should not continue to traffic in ImmutableBitmap now
that the bitmap wrapper is being retired. Introduce DecodedImageFrame as
the paintable decoded-image unit and store a Bitmap plus ColorSpace in
it directly.
Thread the new frame type through decoded image data, display-list
image commands, filters, canvas drawImage, patterns, WebGL texture
upload, and CSS/SVG image consumers. ImmutableBitmap remains only at
the legacy boundaries that still need it, such as HTML video snapshots
and callers that explicitly ask for a bitmap snapshot.
This keeps color-space ownership with the decoded frame while making
the expensive or legacy ImmutableBitmap path explicit at the few call
sites that still need it.
ImmutableBitmap still owned the helper that read pixels from a
PaintingSurface and wrapped the result as an ImmutableBitmap. That kept
a surface readback operation attached to the type we are trying to
remove, even though the snapshot is really a property of the painting
surface.
Add PaintingSurface::snapshot_bitmap() as the explicit readback path.
The remaining callers now wrap that bitmap in ImmutableBitmap only at
the places that still need the old abstraction. Canvas serialization
also uses the same helper, so the BGRA8888 premultiplied snapshot
policy has a single owner.
Bitmap export is pixel conversion, not an ImmutableBitmap-specific
operation. It needs a source bitmap and color space, and callers should
provide those explicitly instead of routing through the immutable
snapshot abstraction.
Move the export formats, flags, result type, and conversion
implementation into a new BitmapExport helper. Keep
BitmapExportResult.h as a forwarding header for existing includes
while making BitmapExport.h the new home for the public API.
Update WebGL and the LibGfx export test to use the standalone helper
directly.
After YUV data no longer lives behind ImmutableBitmap, the private Impl
wrapper only holds a bitmap and its color space. The extra allocation no
longer buys us a meaningful storage abstraction.
Store the bitmap and color space directly on ImmutableBitmap and remove
ImmutableBitmapImpl. This leaves ImmutableBitmap with one clear role
again: an immutable pixel snapshot with no hidden media-backed storage
mode.
A video element should record video as video, not as generic external
bitmap content. Add VideoFrameSource and a dedicated display-list
command so the display-list player receives the current
Media::VideoFrame directly.
The Skia player can now upload YUV pixmaps from the frame when a GPU
context is available, without teaching the ordinary ImmutableBitmap
image cache about media formats. If GPU upload is unavailable, the
fallback explicitly converts the frame through YUVData::to_bitmap().
This gives video painting a clear extension point for future frame
backends, such as hardware frames or other planar formats, while
keeping bitmap drawing focused on immutable pixel snapshots.
Decoded video frames should own their planar YUV data and color space
directly. Keeping that storage behind ImmutableBitmap gave a
still-image abstraction media-specific behavior and made calls like
bitmap() potentially allocate and convert a whole video frame.
Move YUV ownership into Media::VideoFrame, where the lifetime naturally
follows media playback, and remove the YUV-backed mode from
ImmutableBitmap. This commit intentionally keeps the visible Web paint
path on ExternalContentSource by converting the current frame back to
an ImmutableBitmap where Web still expects one.
Callers that need pixels now ask the frame to convert explicitly. That
preserves behavior for canvas and bitmap consumers while making the
expensive YUV-to-pixel path visible at the call site instead of
hiding it behind ImmutableBitmap::bitmap().
The display queue used TimedImage even though the media pipeline is
selecting decoded video frames. That naming hid the real object being
handed from LibMedia to Web and kept the queue interface coupled to
bitmap-style painting.
Rename the wrapper to TimedVideoFrame and pass ref-counted VideoFrame
objects through the provider and display sink. Web still reads the
ImmutableBitmap from the frame for painting in this commit, so rendered
output and conversion behavior stay the same while the playback-facing
interfaces become frame-shaped.
Video frames are about to be shared between the decoder, the data
provider, the display sink, and Web painting code. Passing them by
value keeps ownership tied to the old bitmap-shaped pipeline and makes
later lifetime changes harder to reason about.
Make VideoFrame ref-counted and return NonnullRefPtr from the decoder
and media queues. This changes ownership only: a VideoFrame still wraps
an ImmutableBitmap at this point, so playback behavior remains
unchanged while later commits can move storage and painting
independently.
This is a small prerequisite for moving decoded YUV frames from
ImmutableBitmap into Media::VideoFrame. Once the frame owns the planes
directly, CPU consumers and the Skia upload path will share the same
YUVData object.
Those consumers need different representations. Skia expects high bit
depth YUVA pixmaps to be full-range 16-bit samples, while CPU conversion
paths must see the decoder-native samples and the original bit depth.
Keep YUVData in decoder-native form, and make the Skia expansion a
temporary copy owned by SkYUVAPixmaps. Add a regression test that checks
10-bit pixmaps are expanded for Skia without mutating the source planes
or changing YUVData::to_bitmap() output.
This matches Firefox's behavior which allows up to 65535 for both width
and height. The total area is still constrained by the pitch overflow
check. It was changed to int32_t, so now it limits the maximum area to
~23kx23k, which is still less than the 65535x65535 area that Firefox
allows, but we would eat too much memory if we allowed that.
Start a single-shot timer when a websocket enters CLOSING and fail
the connection if the peer never answers with its close frame.
Without a bound here, a dropped or non-responsive peer can leave the
websocket stuck in the closing handshake forever, which is another
path to rare websocket timeouts during repeated test runs.
If establish_web_socket_connection() fails before a Requests::WebSocket
object exists, the DOM WebSocket used to ignore that error and remain
silently stuck with no message, error, or close events.
Dispatch error and close in that setup-failure path so callers see a
terminal websocket state instead of hanging until the harness timeout.
Detach live websocket wrappers from a dead RequestServer connection,
notify them with error and close events, and defer the
on_request_server_died callback out of RequestClient::die().
Without this, a dead RequestServer could leave existing websocket
objects stuck forever and future websocket construction could keep
using a stale RequestClient with no terminal events.
Add the same connect timeout used by regular requests to curl-backed
websockets, buffer unsent websocket bytes, and resume them from a write
notifier instead of retrying curl_easy_send() in a tight loop.
This prevents CPU spins on CURLE_AGAIN and keeps large websocket sends
from hanging when the peer applies backpressure.
Convert DNS failures and connecting-stage close requests into terminal
websocket state changes, and remove websocket entries once they close.
This keeps clients from getting stuck in CONNECTING and makes early
cancelled or failed websocket attempts terminate like the other close
paths.
Remove the client-side early return that skipped transport writes for
zero-length masked payloads.
This keeps empty text or binary messages and empty pong replies on the
wire instead of silently discarding them.
This essentially reverts 29078d4d53. The
crash that commit fixed (loading
https://www.linux.org.ru/news/opensource/16780786 and waiting) does not
occur with this change.
Route fatal protocol-level websocket failures through fail_connection(),
emit both error and close once a socket exists, and drop the old
fatal_error() helper.
This gives callers a terminal close event instead of leaving transport
failures as error-only state changes.
Add a Segmenter implementation that implements the UAX#14 line breaking
rules applicable to ASCII text. This avoids the need to build an ICU
BreakIterator for the majority of text on the web.
Broad shadow-root stylesheet changes already restyle the whole shadow
tree, but host-side fallout does not always need a document-wide
invalidation. Split the host-side reach classification so selectors
contained to the host subtree, such as `:host *` and `:host > *`,
invalidate the host, while sibling-escaping selectors such as
`:host + :has(*)` still invalidate the host's root.
Recognize sibling escapes through positive selector-list pseudos such as
`:is()` and `:where()` as well, including selectors like
`:is(:host) + :has(*)` and `:is(:host + .item)`.
This avoids turning host-contained shadow stylesheet changes into full
document style invalidations. On https://pomax.github.io/bezierinfo/,
this reduces the time to produce a layout tree from about 8.7s to 3.6s
on my machine.
Previously, we consulted `cascaded_properties()` in a couple of places
after the cascade pass for the relevant element had finished, forcing
`CascadedProperties` to outlive style resolution.
We now keep the small set of values these consumers need on
`ComputedProperties`. We keep hold of resolved specified values for
properties whose computation depends on inherited info, so they can be
re-resolved when an ancestor changes. We also keep the raw winning
cascaded font-size, as this is needed by the time-traveling monospace
font quirk implemented by `recascade_font_size_if_needed()`.
Remember the zoom level for each host so that returning to a site
restores the zoom the user previously chose, matching what other
browsers have done for years.
When the user zooms in, zooms out, or resets the zoom, the resulting
level is written to Settings keyed by the current page's host. On
navigation, when a view's URL host changes, the stored level for the
new host is applied (or the global default if there is no override).
Per-host zoom changes are broadcast through the SettingsObserver so
that two tabs viewing the same host stay in sync as soon as the user
adjusts zoom in either one. Zoom changes from within the page (such
as internals.setBrowserZoom hook) and the WebContent restart path do
not persist, only user-initiated zoom changes do.
The std default RandomState (SipHash) was using ~9 percentage points
of CPU on hash_one and write across the parse hot path, with the
string interner adding another ~3 pp on top. The cost was spread
across the interner, the scope collector's IndexMap<Utf16String, _>,
and several parser-side HashSet<Utf16String> declarations.
Use foldhash::quality::RandomState for the parser, scope collector,
and string interner via a new fast_hash module. Quality keeps
HashDoS resistance (keys are lexer tokens, attacker-controlled in a
browser context) while shedding SipHash's per-byte cost, and on this
workload it benchmarks slightly faster than foldhash::fast.
The Rust AST kept every scope in Rc<RefCell<ScopeData>>. The Rc made
the AST !Send (cross-thread codegen needed unsafe impl Send), and the
RefCell added a runtime borrow check on every hot-path read.
AST nodes (Block, FunctionBody, Program, SwitchStatement, SwitchCase)
now hold a ScopeId index into ScopeArena. The scope collector and
codegen take &mut/&ScopeArena, so the borrow checker enforces the
previously-implicit invariant that two phases never touch the same
scope at once.
ParsedProgram is now naturally Send. The unsafe impl Send and the
arc_with_non_send_sync allow go away. CompiledProgram keeps its
hand-rolled Send impl because it carries codegen-time state outside
the AST.
FunctionDeclarationData::is_hoisted was a Cell<bool> only because the
old &[ScopeRecord] traversal couldn't get &mut to the AST. It is now
a plain bool.
Identifier::name was SharedUtf16String (Rc<Utf16String>), so equality
checks against literals walked the slice and the Rc made the AST
!Send.
Replace it with a StringId (u32 index) backed by a StringInterner on
AstArena. Repeated names dedupe to the same id, so name comparisons
collapse to u32 == u32. The lexer's short/recent identifier caches
and the shared_identifier_value field on Token go away; the interner
already deduplicates everything.
Methods that previously took &mut IdentifierArena now also take
&StringInterner so they can resolve names from StringId during
analyze. Codegen helpers in bytecode/codegen.rs uniformly take
&AstArena. Generator gains intern_identifier_id, intern_property_key_id,
and intern_string_id helpers.
scope_collector now reaches Identifier through &mut IdentifierArena
indexing instead of through Rc<Identifier>'s shared reference, so the
Cell<> wrappers on local_type, local_index, is_global,
is_inside_scope_with_eval, and declaration_kind no longer earn their
keep.
Replace each Cell<T> with a plain T. The borrow checker now enforces
the existing "only scope_collector mutates these post-parse"
invariant. Shrinks Identifier and removes a layer of indirection on
hot-path field reads in codegen and ast_dump.
Replace per-AST-node Rc<Identifier> with a Copy IdentifierId index
into a Vec<Identifier> arena, plumbed through Parser, scope_collector,
codegen, ast_dump, and the FFI. The arena lives on the parser during
parse, ships out via Arc<AstArena> on ParsedProgram, and is shared by
each child Generator and FunctionPayload through Arc clones.
Eliminates the per-occurrence Rc::new in the parser: every identifier
reference, parameter binding, function name, class name, and
binding-pattern target lands in the arena's Vec instead of getting its
own malloc plus Rc control block. Identifier field reads in codegen
become direct array indexing.
Identifier still carries Cell<>-wrapped scope-analysis state, so
AstArena is not yet Send + Sync; the existing unsafe-impl-Send wrapper
on ParsedProgram covers cross-thread handoff. Removing the Cells is
the next step.
The regex literal handle is shared between AST clones (e.g. class
field initializers reuse the same compiled regex), so shared ownership
has to stay. Switch from Rc to Arc and from Cell<*mut c_void> to
AtomicPtr<c_void> so the regex can travel with a function payload to
a worker thread without UB on the non-atomic Rc refcount.
The POST_BUILD step that merges libjs_rust.a / libweb_rust.a into
liblagom-{js,web}.a has no inputs declared to ninja, so Rust-only
edits left the merged archive stale. bin/js then linked pre-change
Rust code: the stale merged archive shadows the directly-linked
fresh Rust .a on the link line.
Add an OBJECT_DEPENDS from RustIntegration.cpp / RustTokenizer.cpp
to the Rust archive so the FFI bridge recompiles, the C++ archive
re-builds, and POST_BUILD re-runs the merge. Shared-library builds
already track the Rust .a as a regular link input.
Our Flatpak builds currently start failing once a commit ID no longer
matches with the commit pointed to by the configured `branch`. This is
becoming pretty annoying for Skia, since the `chrome/mXYZ` branches are
effectively moving targets.
Change the `branch` key in the Flatpak config to `x-branch`, which
removes the constraint and allows Flatpak to work with just the pinned
commit IDs. Change the linter to still use `x-branch` as the reference
to compare with our vcpkg configuration, so we make sure to still keep
things consistent between vcpkg and Flatpak.
with_canvas() existed to wrap every canvas access with the
PaintingSurface lock/unlock pair. With the locking gone, the helper
now just hides a plain canvas() access — inline it at the call sites.
The rendering thread now uses its own SkiaBackendContext, and the main
thread no longer reaches into GPU-backed Skia objects directly. No Skia
context is shared between threads anymore, so the mutex on
SkiaBackendContext and the lock_context()/unlock_context() pair that
wrapped every PaintingSurface and ImmutableBitmap operation are all dead
weight and can go.
Sharing a single SkiaBackendContext between the main thread and the
rendering thread forces locking around every GPU operation. Now that
ImmutableBitmaps are context-neutral, the SkImage cache is per-painter,
and PaintingSurface accepts an explicit context, have the rendering
thread create its own GPU context on startup and use it for the
display-list player and backing store allocation.
This sets up the next commit to remove the cross-thread locking
machinery entirely.