Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
Produce JS-visible string results as UTF-16 at their source, including
numeric formatting, BigInt and BigFraction formatting, URI encoding,
console formatting, parser errors, regular expression errors, Intl and
Temporal records, LibUnicode locale boundaries, and LibWeb bindings.
Handle fractional radix formatting through the UTF-16 builder view.
Thread UTF-16 string input through JSON, script parsing, Date parsing,
Intl option parsing, Temporal parsing, and the helper library boundaries
that feed those parsers. Preserve ASCII fast paths where the source data
is known to be ASCII.
Move the remaining LibJS primitive string users to UTF-16 views and
strings. Remove the primitive string UTF-8 accessors and byte-string
coercion paths so new callers cannot rely on the old storage model.
Keep primitive string storage in Utf16String and remove the UTF-8
storage path from PrimitiveString. ASCII strings still use compact
Utf16String ASCII storage, while UTF-16 becomes the only owned
representation.
Row subgrid contribution sizing reused the parent grid's available
width, so descendants in flexible columns could contribute an unwrapped
height and then wrap during layout.
Use the resolved subgrid item width when measuring row contributions.
When collecting row-axis contributions from subgrid descendants,
resolve the subgrid column metrics first so intrinsic height measurement
uses the correct inline size. This prevents text from being measured as
if it had no usable width, which inflated auto row sizes.
Problem: CSSPixels::abs() invokes UB for the i32 minimum raw value.
Cause: abs() returned from_raw(::abs(m_value)) — and ::abs() of the i32
minimum isn’t representable as an int.
Fix: Compute the magnitude with saturating_sub(0, raw_value()) for
negative inputs — so the i32 minimum saturates to the maximum.
Make CSS Typed OM to_string() implementations produce Utf16String
instead of building UTF-8 strings and converting at the JS binding
boundary. Use Utf16StringBuilder for numeric, math, and transform
serialization paths that already feed CSS Typed OM stringification.
Keep the older CSSOM StyleValue serializer on StringBuilder for now, and
convert only where Typed OM source values or parser re-entry still need
the existing UTF-8 representation.
Replace the remaining UTF-16-mode StringBuilder instances in LibWeb
with Utf16StringBuilder when they assemble Utf16String values or UTF-16
text views. This keeps those paths in UTF-16 throughout and uses
explicit ASCII append helpers for ASCII-only pieces.
Keep StringBuilder in place for JSON, markup, CSS serialization, and
other byte-oriented string construction paths.
Accept Utf16View patterns at the LibRegex compile boundary and pass
UTF-16 or ASCII storage directly into the Rust regex parser. This keeps
JavaScript regular expression construction from converting patterns
through UTF-8 when LibRegex can consume the same UTF-16 representation
used by LibJS.
Update RegExp construction, HTML pattern validation, the regex fuzzer,
and LibRegex tests to use the UTF-16 compile API.
Build JSON.stringify, Date ISO strings, and Temporal string results with
Utf16StringBuilder when the result is consumed as a JavaScript string.
Keep UTF-8 conversion only at callers that explicitly need bytes outside
LibJS.
Problem: UBSan crash when computing layout for an element with a giant
negative inset.
Cause: CSSPixels::operator-() returned from_raw(-raw_value()), and
negating the i32 minimum overflows int.
Fix: Negate with saturating_sub(0, raw_value()) — matching the
saturating arithmetic already used by the other CSSPixels operators.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9997
Problem: Crash when loading an ordered list whose numbering reaches the
i32 limit; e.g. <ol start="2147483647"> with two or more items.
Cause: Element::ordinal_value() kept its numbering in a Checked<i32>
and stepped it once per list item. When the numbering already sits at
the i32 maximum (or minimum, for a reversed list whose value attribute
pins it there), the increment overflowed the Checked value.
Fix: Keep the numbering in a plain i32 instead, and step it with
AK::saturating_add and AK::saturating_sub — so it clamps at the i32
bounds, rather than overflowing.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/10003
Problem: Crash when generating an RSA key — or serializing one —
whose publicExponent is a typed array whose backing ArrayBuffer has
been detached; for example, by calling transfer() on it.
Cause: Two places with big_integer_from_api_big_integer() reading the
bytes of the backing ArrayBuffer directly. But reading the bytes of a
detached buffer aborts.
Fix: Read the bytes with WebIDL get_buffer_source_copy() — which yields
an empty copy for a detached, or OOB resizable, buffer. The empty array
is already mapped to zero — so generation rejects the zero exponent with
an error, rather than crashing.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9991
Previously we assumed that if the calculation had been simplified to a
single `NumericCalculationNode` that we could resolve it - this isn't
the case if we have a percentage that should be resolved to a different
type.
Fixes#10209
Previously, font selection ignored the Unicode emoji presentation of a
code point. Emoji-capable code points were always resolved through
pre-baked color emoji and symbol fonts. Text-default code points which
lacked the `Emoji_Presentation` property, were therefore rendered as
color emoji rather than text. We now classify each code point's default
presentation from its `Emoji_Presentation` property and any trailing
variation selector.
Move CSS animation values into a mutable overlay on computed properties
and make base computed style data immutable after construction. Base
style mutation now goes through a builder that is consumed on publish,
so installed styles no longer expose mutation APIs.
Build new base style data for inherited style updates instead of cloning
and mutating installed computed properties. Element-specific computed
style adjustments now run before publication, while animation and
transition updates continue to mutate only the animated overlay.
Replace the generated C++ legacy codec implementations with a
small Rust wrapper around encoding_rs.
This keeps the existing LibTextCodec API while moving label lookup,
legacy decode/encode, validation, and streaming decoder state to Rust.
The generated index data and generator are no longer needed.
It also fixes several TextDecoder EOF cases due to a more correct
implementation. encoding_rs finalizes decoders according to the
Encoding Standard, so incomplete UTF-8/Big5 tails and malformed
UTF-16 surrogate tails produce the required single replacement at
end-of-queue instead of being dropped, buffered, or double-counted
by our old hand-written decoders.
Pass an encoding label to StreamingDecoder instead of requiring
callers to pre-resolve a Decoder reference.
This removes redundant decoder_for() plumbing from callers and lets
StreamingDecoder hide how chunked decoder state is represented.
Use String::from_utf8_with_replacement_character() directly for Infra's
parse JSON bytes algorithm instead of constructing a more generic
TextCodec UTF-8 decoder.
UnresolvedStyleValue::create() serialized the component values into a
comparison string unconditionally, but the result is only used when the
caller supplied separate original source text. On the common path that
serialization (including float formatting of every numeric token) was
computed and immediately discarded.
Move the serialization into the branch that actually uses it. This is
hot during var() substitution, where many intermediate
UnresolvedStyleValues are created and never compared.
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.
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.
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.
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.
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.
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.