When creating an inheritance stack, look up parent interfaces from the
parsed IDL context instead of the current interface's imported modules.
This removes a dependency on explicit #import directives during bindings
generation.
Define MediaKeySystemConfiguration and MediaKeySystemMediaCapability in
Web::EncryptedMediaExtensions instead of Web::Bindings. This matches
other dictionary definitions in the codebase.
Keep the JsonWebKey dictionary types in line with other dictionary
types in the codebase by putting them in the Crypto namespace
rather than under Web::Bindings.
indexed_take_first() already memmoves elements down for both Packed and
Holey storage, but the caller at ArrayPrototype::shift() only entered
the fast path for Packed arrays. Holey arrays fell through to the
spec-literal per-element loop (has_property / get / set /
delete_property_or_throw), which is substantially slower.
Add a separate Holey predicate with the additional safety checks the
spec semantics require: default_prototype_chain_intact() (so
HasProperty on a hole doesn't escape to a poisoned prototype) and
extensible() (so set() on a hole slot doesn't create a new own
property on a non-extensible object). The existing Packed predicate
is left unchanged -- packed arrays don't need these checks because
every index in [0, size) is already an own data property.
Allows us to fail at Cloudflare Turnstile way much faster!
The element-by-element loop compiled to scalar 8-byte moves that the
compiler could not vectorize: source and destination alias, and strict
aliasing prevented hoisting the m_indexed_elements pointer load out of
the loop body. memmove collapses the shift into a single vectorized
copy.
Previously it used `realm.[[GlobalObject]]` instead of
`realm.[[GlobalEnv]].[[GlobalThisValue]]`.
In LibWeb, that corresponds to Window and WindowProxy respectively.
The idea is that scripts directly under Meta are meant to be run by
people. Scripts that are only imported or run by other scripts are
moved to a subdirectory.
This script:
1. Hasn't get up with our build directory structure.
2. Only works on macOS due to the way it invokes sed.
3. Arguably takes longer to figure out how to run the script than it
does to just edit .clangd's build path.
Avoid broad document invalidation when adding or removing ordinary
document-owned or shadow-owned stylesheets. Reuse the targeted
StyleSheetInvalidation path for style rules, including shadow-host
escapes, pseudo-element-only selectors, and trailing-universal cases.
Keep the broad path for sheet contents whose effects are not captured
by selector invalidation alone, including @property, @font-face,
@font-feature-values, @keyframes, imported sheets, and top-level @layer
blocks. Broad-path shadow-root sheets still reach host-side consumers
through their active-scope effects.
Toggling CSSStyleSheet::disabled previously cleared the cached media
match bits and reloaded fonts, but never informed the owning documents
or shadow roots that style resolution was now stale. Worse, the IDL
binding for the disabled attribute dispatches through a non-virtual
setter on StyleSheet, so any override on CSSStyleSheet was bypassed
entirely.
Make set_disabled() virtual so the CSSStyleSheet override actually runs,
snapshot the pre-mutation shadow-root stylesheet effects before flipping
the flag, and hand them to invalidate_owners() so a disable that strips
the last host-reaching rule still tears down host-side style correctly.
When invalidate_owners() runs on a stylesheet scoped to a shadow root,
we previously dirtied the host and its light-DOM side too broadly. That
forced restyles on nodes the shadow-scoped stylesheet cannot match.
Inspect the sheet's effective selectors and dependent features up front.
Only dirty assigned nodes, the host, the host root, or host-side
animation consumers when the sheet can actually reach them, while
keeping purely shadow-local mutations inside the shadow tree.
Handle inline stylesheet @keyframes insertions without falling back to
broad owner invalidation. Recompute only elements whose computed
animation-name already references the inserted keyframes name.
Document-scoped insertions still walk the shadow-including tree so
existing shadow trees pick up inherited animations, and shadow-root
stylesheets fan out through the host root so :host combinators can
refresh host-side consumers as well. Also introduce the shared
ShadowRootStylesheetEffects analysis so later stylesheet mutation paths
can reuse the same per-scope escape classification.
Avoid forcing a full style update when a connected inline <style> sheet
inserts an ordinary style rule. Build a targeted invalidation set from
the inserted rule and walk only the affected roots instead.
Introduce the shared StyleSheetInvalidation helper so later stylesheet
mutation paths can reuse the same selector analysis and root application
logic. It handles trailing-universal selectors, pseudo-element-only
rightmost compounds, and shadow-host escapes through ::slotted(...) and
:host combinators.
Keep the broad invalidate_owners() path for constructed stylesheets and
other sheet kinds whose TreeScope interactions still require it.
Full-subtree style invalidations are cheap to count where they happen
(Node::invalidate_style on a document node) but previously invisible to
tests. Surface the counter as fullStyleInvalidations alongside the
existing style invalidation counters so text tests can assert that a
given mutation path stayed surgical instead of degrading to a broad
document restyle.
Adopting a node into another document preserves the node's dirty style
flags, but the destination ancestor chain never sees them propagate. If
a style update is already pending in the new document, it can skip the
adopted subtree entirely.
Snapshot the subtree and child dirty bits before set_document() updates
m_document, then walk the new ancestor chain and re-mark
child_needs_style_update so the pending restyle still descends into the
adopted subtree.
Note that the IPC lint step can be removed from the CI workflow because
we no longer need a build in order to perform the lint, and IPC linting
is now covered by lint-ci.sh.
This replaces the C++ IPC compiler and generator with a python3 port.
For validation, the parser is nearly a line-for-line port of the C++
parser. This patch includes lexer.py, which is a simple port of our
GenericLexer, as the mechanics actually felt pretty nice in python as
well.
The code generator does not include a port of our SourceGenerator, as
that felt less nice. Instead, we write directly to a TextIO instance.
The generated output is almost byte-for-byte identical with our C++
generator, with trivial whitespace differences:
* The C++ generator included extraneous newlines before and after all
switch statements, which is not kept here.
* The C++ generator did not include newlines between message handling
functions, which we now do.
* The C++ generator had some extraneous spaces at the end of some lines,
and incorrect tabbing (3 spaces) at the beginning of some lines.
When resolving grid track sizes, limited min/max-content contributions
should be capped by fixed max track sizing functions, including the
argument to fit-content(). We were instead falling back to the grid
container maximum size, which allowed a grid item with overflowing
contents in a fit-content(0) row to inflate the intrinsic block size of
a nested grid.
That bogus intrinsic height could then be used for the grid's second row
sizing pass, causing unrelated flexible rows to absorb the extra space.
Document already includes XPathEvaluatorBase from XPathEvaluator.idl.
Import that IDL file and drop the duplicate XPath method declarations
from Document.idl.
Import WebIDL/Function.idl where TimerHandler uses Function, and let the
bindings generator handle it through the normal callback-function path.
This removes the special C++ mapping for Function and makes TimerHandler
use GC::Root<CallbackType>, matching the generated binding type when IDL
files are parsed together.
When resolving a typedef, clone its stored type before applying the
nullability from the use site.
This keeps shared typedef definitions stable when multiple IDL files are
parsed in one generator run.
Change the IDL parser entry point to a static factory returning an
IDL::Module. Modules now own the parsed file identity and import graph,
with an optional reference to the file's real interface when one exists.
This is a step towards running the IDL generator on files without
an interface defined.
Shape::visit_edges used to walk every entry of m_property_table and
call PropertyKey::visit_edges on each key. For non-dictionary shapes
that work is redundant: m_property_table is a lazily built cache of
the transition chain, and every key it contains was originally
inserted as some ancestor shape's m_property_key, which is already
kept alive via m_previous.
Intrinsic shapes populated through add_property_without_transition()
in Intrinsics.cpp are not dictionaries and have no m_previous to
reach their keys through, but each of those keys is either a
vm.names.* string or a well-known symbol and is strongly rooted by
the VM for its whole lifetime, so skipping them here is safe too.
Measured on the main WebWorker used by https://www.maptiler.com/maps/
this cuts out ~98% of the PropertyKey::visit_edges calls made by
Shape::visit_edges each GC, reducing time spent in GC by ~1.3 seconds
on my Linux PC while initially loading the map.
eglMakeCurrent with EGL_NO_SURFACE leaves the GL viewport at its default
of (0, 0, 0, 0), which clips all rasterization to zero pixels until the
page explicitly calls gl.viewport(). The Vulkan path already sets the
viewport after binding the color buffer; do the same on the macOS
IOSurface path so WebGL content that does not manage viewport state
itself (e.g. feature-detection draws into a 1x1 canvas) still produces
visible pixels.
This extension lets pages query the underlying GPU vendor and renderer
strings via UNMASKED_VENDOR_WEBGL / UNMASKED_RENDERER_WEBGL. Some sites
(e.g. yandex.com/maps) use it to decide whether to render vector tiles
with WebGL or fall back to raster tiles.
Carry full source positions through the Rust bytecode source map so
stack traces and other bytecode-backed source lookups can use them
directly.
This keeps exception-heavy paths from reconstructing line and column
information through SourceCode::range_from_offsets(), which can spend a
lot of time building SourceCode's position cache on first use.
We're trading some space for time here, but I believe it's worth it at
this tag, as this saves ~250ms of main thread time while loading
https://x.com/ on my Linux machine. :^)
Reading the stored Position out of the source map directly also exposed
two things masked by the old range_from_offsets() path: a latent
off-by-one in Lexer::new_at_offset() (its consume() bumped line_column
past the character at offset; only synthesize_binding_pattern() hit it),
and a (1,1) fallback in range_from_offsets() that fired whenever the
queried range reached EOF. Fix the lexer, then rebaseline both the
bytecode dump tests (no more spurious "1:1") and the destructuring AST
tests (binding-pattern identifiers now report their real columns).
When inheriting custom-property data from a parent element, we were
copying the parent's full CustomPropertyData regardless of whether
each property was registered with `inherits: false`. That caused
non-inheriting registered properties to leak from the parent,
contrary to the @property spec.
Wrap the parent-side lookup so we strip any custom property whose
registration says it should not inherit, and only build a fresh
CustomPropertyData when at least one property was actually filtered.
Key the filtered view's cache on both the destination document's
identity and its custom-property registration generation. The
generation counter is local to each document, so a subtree adopted
into another document (or queried via getComputedStyle from another
window) could otherwise pick up a cached view computed under an
unrelated registration set and silently skip non-inheriting filtering
in the new document.
A @keyframes rule scoped to a shadow root was not reliably reached
from an animated slotted light-DOM element: the keyframes lookup
walked the element's own root first, then fell back to the document,
but slotted elements can pick up animation-name from a ::slotted(...)
rule that lives in an ancestor shadow root rather than in the
element's own tree.
Track the shadow-root scope that supplied each winning cascaded
declaration, and use that scope to resolve the matching @keyframes
when processing animation definitions. A shared constructable
stylesheet can be adopted into several scopes at once, so the
declaration object alone is too weak as a key; the per-entry
shadow-root pointer disambiguates which adoption actually contributed.
Also refresh running CSS animations' keyframe sets when style is
recomputed. Previously only the first animation creation path set a
keyframe set, so an existing animation never picked up newly inserted
@keyframes rules.
When CascadedProperties::set_property overwrites an existing entry for
the same origin and layer, it bumped cascade_index but kept the old
source pointer. That left source stale after a later declaration
overrode an earlier one at equal priority, so property_source() could
return a CSSStyleDeclaration that no longer supplied the winning
value.
Refresh source alongside the rewritten property and cascade_index so
the entry consistently describes its current contributor.
The @keyframes parser was storing the keyframes name via
Token::to_string(), which keeps a string token in its quoted,
serialized form. That meant @keyframes "foo" was stored as
"\"foo\"" while animation-name: "foo" resolved to "foo",
and the two never matched.
Store the unquoted string or identifier value so the @keyframes name
and the animation-name reference compare on the same string.
CSSOM's "add a CSS style sheet" steps bail out once the disabled flag
is set, so ownership alone should not make a disabled sheet observable
in the destination document. Delay CSS-connected font activation in
add_owning_document_or_shadow_root() until the sheet actually becomes
enabled, and refuse pending image-resource loads on a disabled sheet
for the same reason.
Also extend set_disabled() to drive the font/image lifecycle around the
transition: loading fonts and pending images when the sheet becomes
enabled, and unloading fonts when it goes back to disabled.
When inline layout emits a whitespace chunk, it previously selected the
surrounding text's font without checking whether that font actually
contains a glyph for the whitespace codepoint. On pages that use
`@font-face` rules sharded by `unicode-range` (e.g. a Roboto webfont
split across one file for Cyrillic letters and another for basic Latin),
the shard covering the letters is picked for an adjacent space even
though the space codepoint lives in a different shard. HarfBuzz then
shapes the space with a font that has no glyph for it and emits
`.notdef`, rendering spaces as tofu boxes.
Check `contains_glyph(space_code_point)` on each candidate in
`font_for_space()` and fall through to
`FontCascadeList::font_for_code_point()` for the whitespace codepoint
when no surrounding font has the glyph.
Fixes whitespace rendering on web.telegram.org/a.
CompareArrayElements was calling ToString(x) +
PrimitiveString::create(vm, ...) on every comparison, producing a
fresh PrimitiveString that wrapped the original's AK::String but
carried no cached UTF-16. The subsequent IsLessThan then hit
PrimitiveString::utf16_string_view() on that fresh object, which
re-ran simdutf UTF-8 validation + UTF-8 -> UTF-16 conversion for
both sides on every one of the N log N comparisons.
When x and y are already String Values, ToString(x) and
ToPrimitive(x, Number) are the identity per spec, so we can drop
the IsLessThan detour entirely and compare their Utf16Views
directly. The original PrimitiveString caches its UTF-16 on first
access, so subsequent comparisons against the same element hit
the cache; Utf16View::operator<=> additionally gives us a memcmp
fast path when both sides ended up with short-ASCII UTF-16 storage.
Microbenchmark:
```js
function makeStrings(n) {
let seed = 1234567;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed;
};
const out = new Array(n);
for (let i = 0; i < n; i++)
out[i] = "item_" + rand().toString(36)
+ "_" + rand().toString(36);
return out;
}
const base = makeStrings(100000);
const arr = base.slice();
arr.sort();
```
```
n before after speedup
1k 0.70ms 0.30ms 2.3x
10k 8.33ms 3.33ms 2.5x
50k 49.33ms 17.33ms 2.8x
100k 118.00ms 45.00ms 2.6x
```
The !has_ascii_storage() && !other.has_ascii_storage() branch did a
byte-wise __builtin_memcmp over a char16_t array, which on little-endian
does not give code-unit order: the low byte is compared first, so
0xD83D (bytes [0x3D, 0xD8]) spuriously compared less than 0x2764
(bytes [0x64, 0x27]) even though the code unit 0xD83D is greater.
No in-tree caller currently uses operator<=> for Utf16View ordering,
so this bug is dormant; the follow-up LibJS change exposes it.
Replace the memcmp branch with a per-code-unit loop, which the compiler
can auto-vectorize and which mirrors what is_code_unit_less_than already
does.