Move partial interface, partial namespace, mixin, typedef, and overload
finalization into a context-wide post-parse resolve step.
This lets BindingsGenerator parse all declared IDL files first and then
finalize the shared IDL context before generating bindings.
Teach the bindings build to pass support IDL files alongside the regular
binding IDLs so BindingsGenerator parses the full declared IDL set into
a shared context.
Keep idl_files.cmake as the source of truth for parsed support IDLs, and
let Interface::will_generate_code() decide which parsed interfaces emit
generated bindings.
Semantic IDL lookups no longer rely on imported modules, so stop storing
that state on Interface and Module.
import directives are still resolved during parsing for now, but the
parsed imported-module lists are no longer needed.
Stop using imported modules to decide which binding headers to include.
Instead, collect dependencies from referenced interfaces, dictionaries,
enumerations, callbacks, and nested IDL types in the parsed context.
Derive C++ namespaces from each IDL module's location and use those
qualified names when generating binding code.
Also Teach dictionaries their owning IDL module path so dictionary C++
types can be qualified the same way as interfaces. This removes the need
for the generated `using namespace Web::*` hack and the hard-coded
namespace list.
Also fix DOMURL.idl to refer to the IDL interface name `URL`, not the
C++ implementation name `DOMURL`.
No other third layer folder in LibWeb has its own namespace which
makes this a special case for the IDLGenerator when determining
namespaces. Instead of adding a special case, simply remove the
namespace.
Teach BindingsGenerator to parse and generate bindings for the full
LibWeb IDL set in one invocation, and collapse the CMake bindings
rules from one custom command per IDL file to a single batched codegen
step.
This has the downsides of:
* Any single IDL change now reruns the whole bindings generator
* Per-IDL parallelism at the custom-command level is lost
However, I still feel that this is a worthy trade off as:
* Generated files are written with write_if_changed(), so rebuilds
of generated files should not be significantly impacted.
* It is not a common task to be modifying IDL files
* Most importantly, giving the IDL generator full knowledge of _all_
IDL will allow for some simplifications of the bindings generator as
it has knowledge of all types.
HTMLImageElement's update-the-image-data step 16 queues its state
transition and load event dispatch via a 1 ms BatchingDispatcher, so
the current request does not become CompletelyAvailable synchronously
when the fetch finishes. decode()'s on_finish callback, however, was
queuing its resolve task directly on the event loop, bypassing the
batch. That race meant decode() could resolve while the image request
was still in Unavailable state, so any .then() handler inspecting
img.width / img.height (or anything derived from the bitmap) would see
zeros.
Google Maps hits this on its .9.png road shield icons: after awaiting
img.decode() it reads a.width / a.height and calls
ctx.getImageData(0, 0, 0, 0), which throws IndexSizeError and aborts
the tile rendering pipeline.
Route decode()'s on_finish through the same BatchingDispatcher so both
are processed in the same batch, with the decode resolution queued
after step 16's element task.
During intrinsic sizing, compute_width() ran on block descendants with
an intrinsic-sizing available space. For a non-FC-establishing block
with auto width, used_width stayed auto, and the min-width clamp then
compared AvailableSize::min-content against min-width via operator<,
which always returns true when the left side is min-content. The clamp
fired with min-width: 0 and set content_width to 0 permanently.
Skip the min-width clamp when used_width is still auto, mirroring the
max-width clamp a few lines above which already no-ops via
to_px_or_zero. The real width is then set by the IntrinsicSizing branch
in layout_block_level_children.
invalidate_all_prototype_chains_leading_to_this used to scan every
prototype shape in the realm and walk each one's chain looking for
the mutated shape. That was O(N_prototype_shapes x chain_depth) per
mutation and showed up hot in real profiles when a page churned a
lot of prototype state during startup.
Each prototype shape now keeps a weak list of the prototype shapes
whose immediate [[Prototype]] points at the object that owns this
shape. The list is registered on prototype-shape creation
(clone_for_prototype, set_prototype_shape) and migrated to the new
prototype shape when the owning prototype object transitions to a
new shape. Invalidation is then a recursive walk over this direct-
child registry, costing O(transitive descendants).
Saves ~300 ms of main thread time when loading https://youtube.com/
on my Linux machine. :^)
notify_about_rejected_promises() is called for every related environment
settings object at the end of every microtask checkpoint. It was
unconditionally reading the realm up front, which showed up at 3.0% self
time in a YouTube playback profile.
This patch moves the realm lookup into the queued task callback, which
happens way less often.
Document::is_decoded_svg() was reached through two pointer hops and a
virtual call into PageClient on every invocation. It showed up at 1.9%
self time in a YouTube playback profile, and it's also called for every
document in the hot documents_in_this_event_loop_matching() loop that
runs on every rendering update.
The page's client is fixed for the lifetime of a document, so we can
cache the answer at construction time and serve future calls from a
plain member load.
No other engine defines this function, so it is an observable difference
of our engine. This traces back to the earliest days of LibJS.
We now define `gc` in just the test-js and test262 runners.
Previously, we were accidentally creating temporary copies of custom
property maps on both sides of a ternary in `compute_style_impl()`. We
now bind to a static empty sentinel instead so the reference binds
directly to `own_values()` without copying.
font_for_code_point() was the heaviest function in layout profiles
of a YouTube page (216ms CPU out of 2900ms total). Every call walked
the full cascade and ran a virtual contains_glyph() against each
entry, even though the result is the same for most ASCII code points
across a document.
Add a 128-entry direct-mapped cache keyed by code point that stores
the resolved Font pointer on first lookup. Subsequent ASCII lookups
become a null check plus a load.
No invalidation is needed: m_fonts is append-only, and the cascade
returns the first matching font, so once an entry claims a code
point, later appends cannot change the answer.
Currently there are multiple style values which are essentially the same
thing, a function holding a value, just with different names. This
commit adds a generic style value to replace them with and the following
commits will do so.
These handlers crashed on several kinds of JS-dispatched input:
zero-width range (divide by zero in the slider mouse handler),
step="any" (MUST(step_up) throws InvalidStateError), plain Event
without clientX/deltaY/key (unchecked as_foo() asserts on
undefined), min > max (trips clamp()'s VERIFY), and input.type
changes leaving the range listeners attached to dereference empty
Optionals from the range-only min()/max() accessors.
Gate each handler on its expected type_state() and on
allowed_value_step() having a value, validate event property types
before converting, and bail out on zero-width rects or min > max.
Six crash tests cover the new paths.
Hit on a Cloudflare challenge page.
Until the upstream vcpkg port is updated, this will patch in the new
release of highway. The new release includes fixes for EVEX512 feature
detection in gcc>=15 and clang>=22, and unblocks using those compilers.
HTMLLinkElement::removed_from() used `old_root` to find the
StyleSheetList to remove the link's stylesheet from. That's wrong
when the link element lives inside a shadow tree that is itself
nested within a larger removed subtree: Node::remove() hands every
shadow-including descendant the outer subtree's root as `old_root`,
not the descendant's own containing root. So we'd look in the
document's list while the sheet was actually in the shadow root's
list, failing the did_remove VERIFY in StyleSheetList::remove_sheet.
Fix by using the sheet's own owning-root tracking. A link-owned sheet
always has exactly one owning document or shadow root (only constructed
stylesheets can be adopted, and link sheets are never constructed), so
we can just read that entry.
Also make owning_documents_or_shadow_roots() return by const reference
instead of copying the HashTable on every call, which benefits existing
iterating callers too.
Fixes a crash on https://nytimes.com/.
Use the shared IDL context when checking whether a type is a JSON type
instead of walking imported modules from the current interface. This
removes another dependency on explicit #import directives when resolving
interface inheritance for toJSON.
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.