Add explicit IgnoreBOM and ErrorMode options to LibTextCodec decoders,
and thread them through TextDecoder and TextDecoderStream.
This lets Web-facing decoder APIs preserve BOMs when requested and use
fatal error handling without post-processing decoded output.
NB: RemoveBOM was renamed to IgnoreBOM as "RemoveBOM" is the name
used by encoding_rs and was previously an implementation detail.
The new name matches what is used by the encoding standard as it
is now also used in LibWeb.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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`
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.
Instead of operating within the (saturating) CSSPixels constraints,
calculate the expected value using a floating point calculation first
and then create the CSSPixels value.
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.
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 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.
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.
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.