Commit Graph

15728 Commits

Author SHA1 Message Date
Shannon Booth
cc6e048bd6 LibWeb+LibIDL: Remove support for #import directives during parsing
These no longer serve any purpose now that we run the IDLGenerator
on all of these files at once.
2026-04-24 20:08:29 +02:00
Shannon Booth
ba741994dd LibIDL: Resolve parsed interfaces post parsing
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.
2026-04-24 20:08:29 +02:00
Shannon Booth
a153732a64 LibWeb: Parse support IDL files when 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.
2026-04-24 20:08:29 +02:00
Shannon Booth
5f3812ecca LibIDL: Drop unused imported module state
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.
2026-04-24 20:08:29 +02:00
Shannon Booth
0e55b0f87e LibWeb/Bindings: Derive includes from IDL references
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.
2026-04-24 20:08:29 +02:00
Shannon Booth
535b8f5b9b LibWeb/Bindings: Qualify generated C++ type names
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`.
2026-04-24 20:08:29 +02:00
Shannon Booth
4178ec0af4 LibWeb/WebGL: Remove Extensions interface
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.
2026-04-24 20:08:29 +02:00
Aliaksandr Kalenik
1193409f64 LibWeb: Wait for CompletelyAvailable state before resolving img.decode()
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.
2026-04-24 19:27:26 +02:00
Aliaksandr Kalenik
cd6672eee0 LibWeb: Fix min-content collapsing to 0 with min-width: 0 descendant
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.
2026-04-24 19:08:28 +02:00
Andreas Kling
c0e520463e LibJS: Invalidate prototype chains via per-shape child lists
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. :^)
2026-04-24 18:59:01 +02:00
Andreas Kling
9a6f5f8640 LibWeb: Avoid realm lookup when rejected promises list is empty
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.
2026-04-24 18:58:48 +02:00
Andreas Kling
5a3845b330 LibWeb: Cache Document's decoded-SVG status in a bool member
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.
2026-04-24 18:58:48 +02:00
Timothy Flynn
12d9aaebb3 LibJS: Remove gc from the global object
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.
2026-04-24 18:36:23 +02:00
Tim Ledbetter
b1501dcb45 LibWeb: Avoid copying custom property maps in StyleComputer
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.
2026-04-24 17:25:29 +01:00
Tim Ledbetter
aa0aaddbde LibWeb: Avoid redundant matches() calls during hover invalidation 2026-04-24 16:42:15 +02:00
Andreas Kling
bfead0cb20 LibGfx: Cache ASCII font resolution in FontCascadeList
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.
2026-04-24 12:54:11 +02:00
Callum Law
6b7fb4b273 LibWeb: Replace ViewFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
6aeaf69cd3 LibWeb: Replace ScrollFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
4faab1aa57 LibWeb: Replace FitContentSV with KeywordSV and FunctionSV
The `fit-content` keyword is stored as a `KeywordStyleValue` and
`fit-content()` function is stored as `FunctionStyleValue`
2026-04-24 07:34:54 +01:00
Callum Law
3bfebf862b LibWeb: Replace AddFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
c846b289a9 LibWeb: Replace FontVariantAlternatesFunctionSV with FunctionSV 2026-04-24 07:34:54 +01:00
Callum Law
c558d67ba9 LibWeb: Add generic FunctionStyleValue
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.
2026-04-24 07:34:54 +01:00
Andreas Kling
5da72570b8 LibWeb: Harden UA event handlers on range and number inputs
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.
2026-04-24 07:58:34 +02:00
Andreas Kling
3cf24872c4 LibWeb: Fix crash removing link stylesheet nested in a shadow tree
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/.
2026-04-23 22:37:06 +02:00
Shannon Booth
bd4ef4b95a LibIDL: Resolve JSON interface types via context
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.
2026-04-23 22:12:13 +02:00
Shannon Booth
64736db9e4 LibWeb/EncryptedMediaExtensions: Move EME dictionaries out of Bindings
Define MediaKeySystemConfiguration and MediaKeySystemMediaCapability in
Web::EncryptedMediaExtensions instead of Web::Bindings. This matches
other dictionary definitions in the codebase.
2026-04-23 22:12:13 +02:00
Shannon Booth
0cf991e205 LibWeb/Crypto: Move WebCrypto dictionaries into Crypto namespace
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.
2026-04-23 22:12:13 +02:00
Aliaksandr Kalenik
bfbc3352b5 LibJS: Extend Array.prototype.shift() fast path to holey arrays
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!
2026-04-23 21:47:21 +02:00
Aliaksandr Kalenik
ad7177eccb LibJS: Use memmove in Object::indexed_take_first()
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.
2026-04-23 21:47:21 +02:00
Luke Wilde
3d3b02b9c0 LibJS: Use the real globalThis value
Previously it used `realm.[[GlobalObject]]` instead of
`realm.[[GlobalEnv]].[[GlobalThisValue]]`.

In LibWeb, that corresponds to Window and WindowProxy respectively.
2026-04-23 20:43:01 +01:00
Ali Mohammad Pur
107f55581d LibTest: Make the testrunner show a nice progress bar test-web-style
The old output format is preserved under non-tty.
2026-04-23 18:48:56 +02:00
Ali Mohammad Pur
909013b972 LibWeb+LibTest: Move the progress bar/status printing stuff to LibTest 2026-04-23 18:48:56 +02:00
Andreas Kling
928a5247ff LibWeb: Narrow stylesheet add/remove invalidation
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
cfa75e6eb4 LibWeb: Invalidate stylesheet owners when disabled state changes
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
a0dc0c61f4 LibWeb: Scope broad shadow-root stylesheet invalidation
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
4e92765211 LibWeb: Narrow @keyframes insertRule() invalidation
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
b6559d3846 LibWeb: Narrow inline stylesheet insertRule() invalidation
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
73aafc2ade LibWeb: Expose full-style-invalidation counter through Internals
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.
2026-04-23 16:45:22 +02:00
Andreas Kling
e29281893a LibWeb: Preserve pending style-update flags across document adoption
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.
2026-04-23 16:45:22 +02:00
Timothy Flynn
cfa24d3367 LibIPC: Return a StringView for IPC message names 2026-04-23 09:42:07 -04:00
Timothy Flynn
5c34c7f554 Meta: Move python code generators to a subdirectory
Let's have a bit of organization here, rather than an ever-growing Meta
folder.
2026-04-23 07:31:19 -04:00
Shannon Booth
b0acc18f9b LibWeb: Clamp limited grid item contributions by fixed max tracks
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.
2026-04-23 09:23:02 +01:00
Shannon Booth
64938e257a LibWeb: Move Document XPath methods to mixin
Document already includes XPathEvaluatorBase from XPathEvaluator.idl.
Import that IDL file and drop the duplicate XPath method declarations
from Document.idl.
2026-04-23 07:07:49 +02:00
Shannon Booth
5a97dba495 LibWeb: Model Function as a callback type
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.
2026-04-23 07:07:49 +02:00
Shannon Booth
23e30067a0 LibIDL: Avoid mutating typedefs during resolution
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.
2026-04-23 07:07:49 +02:00
Shannon Booth
f25bfd747a LibIDL: Make parser return modules instead of interfaces
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.
2026-04-23 07:07:49 +02:00
Andreas Kling
43aecd3f90 LibJS: Skip property-table key marking for non-dictionary shapes
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.
2026-04-23 02:14:01 +02:00
Aliaksandr Kalenik
d52080968d LibWeb: Initialize WebGL viewport on macOS IOSurface surface allocation
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.
2026-04-22 22:38:16 +02:00
Aliaksandr Kalenik
3c7b3a0fe2 LibWeb: Implement WEBGL_debug_renderer_info extension
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.
2026-04-22 22:38:16 +02:00
Andreas Kling
eb9432fcb8 LibJS: Preserve source positions in bytecode source maps
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).
2026-04-22 22:34:54 +02:00