Commit Graph

15746 Commits

Author SHA1 Message Date
Johan Dahlin
8c49029bb7 LibGfx+LibWeb: Only trigger @font-face loads from text shaping 2026-04-25 17:06:28 +02:00
Johan Dahlin
acabf765c1 LibWeb+LibGfx: Defer @font-face fetches until a codepoint renders 2026-04-25 17:06:28 +02:00
Aliaksandr Kalenik
f916b3e11f LibWeb: Skip layout update in handle_mousemove when not needed
Only run update_layout() during mousemove handling when the result is
actually consumed, i.e. when the middle-button autoscroll handler or
mouse selection is active. This avoids forcing a synchronous layout
on every mousemove event in the common case.
2026-04-25 15:45:12 +02:00
Aliaksandr Kalenik
4f54b16315 LibWeb: Preserve non-ASCII characters in canvas text preparation
The whitespace-normalization loop in prepare_text() called
StringBuilder::append() on each code point, which resolves to the
`char` overload and truncates non-ASCII characters. measureText("ó")
therefore returned a width of 0, despite fillText painting the glyph.

Use append_code_point() instead, and add a regression test for both
precomposed and decomposed accented text.
2026-04-25 14:54:18 +02:00
Andreas Kling
792a8c3a9c LibWeb: Accept image/avif as a supported image MIME type
The list previously omitted AVIF even though we ship a working
AVIFImageDecoderPlugin, which meant <picture><source type="image/avif">
candidates and image-set(... type("image/avif")) candidates were
unconditionally skipped.
2026-04-25 14:54:10 +02:00
Andreas Kling
a226d778e6 LibWeb: Visit abstract list-style images
List-style images can now be abstract image values such as image-set(),
not just plain URL images. Visit the stored abstract image directly so
GC-backed resources held by those image values stay reachable.
2026-04-25 14:54:10 +02:00
Andreas Kling
61d79a1e47 LibWeb: Parse CSS image-set()
Add an abstract image style value for image-set() and parse both the
standard and -webkit-prefixed spellings through the existing <image>
value path. The parser accepts URL and string image candidates,
optional resolution descriptors, and type() filters.

Track attr-taint through substituted component values so image-set()
candidates using attr()-derived URL-producing tokens are rejected when
resolved for URL-using properties.

Update the relevant WPT baselines now that image-set() parsing is
supported in additional value contexts.
2026-04-25 14:54:10 +02:00
Andreas Kling
30f37d691c LibWeb: Share supported image MIME type list
Extract the file-local is_supported_image_type() helper from
HTMLImageElement into a small standalone translation unit so other
parts of the engine can ask the same question. The next commit reuses
it for the image-set() type() filter.

The list is still hard-coded; deriving it from the registered image
decoders remains a FIXME.
2026-04-25 14:54:10 +02:00
Tim Ledbetter
30f8e7f80c LibWeb: Include non-auto cross margins in auto-margin resolution
When resolving cross-axis auto margins on a flex item, the outer cross
size calculation omitted all cross-axis margins. We now include
non-auto margins as part of the outer cross size treating auto margins
as zero.
2026-04-25 14:46:12 +02:00
Andreas Kling
b629914428 LibWeb: Make TransferArrayBuffer zero-copy
Move owned ArrayBuffer storage directly when transferring stream
buffers instead of copying the bytes before detaching the source.
WebAssembly memory continues to copy because its ArrayBuffer wraps
externally-owned storage.

Preserve the abrupt completion from DetachArrayBuffer before moving
storage so non-transferable buffers, such as WebAssembly.Memory-backed
views, still surface TypeError through stream operations instead of
aborting.

This saves ~130ms of main thread time when loading a YouTube video
on my Linux computer. :^)
2026-04-25 10:53:51 +02:00
Tim Ledbetter
59bf30f17f LibWeb: Add AVIF and WebP to the Accept header for images
This matches the behavior of other engines. Some CDNs that do content
negotiation will fall back to non alpha-preserving formats if these
values are not present.
2026-04-25 08:49:04 +02:00
Tim Ledbetter
eef11001ec LibWeb: Avoid ref-count churn when diffing computed styles 2026-04-25 08:47:47 +02:00
Yayoi-cs
0b9636fadf LibJS: Only cache TypedArray data pointers for owned buffers
WebAssembly.Memory-backed ArrayBuffers wrap external
ByteBuffer storage. When that memory grows,
ByteBuffer::try_resize() may realloc the backing storage while
old fixed-length buffer objects remain reachable from JS.

TypedArrayBase cached m_data for all fixed-length buffers, and
the asm interpreter fast path dereferenced that cached pointer
directly. For wasm memory views this could leave a stale
pointer behind across grow().

Restrict cached typed-array data pointers to fixed-length
ArrayBuffers that own stable ByteBuffer storage.
External/unowned buffers, including WebAssembly.Memory
buffers, now keep m_data == nullptr and fall back to code that
re-derives buffer().data() on each access.

Add regressions for both the original shared-memory grow case
and the second-grow stale-view case.
2026-04-25 06:11:18 +02:00
Tim Ledbetter
5d69c6d2b7 LibWeb: Filter by font width before weight in font matching
Implement the width filtering step of the font matching algorithm.
Without it, system font providers that group all widths under one
family could return a condensed variant for font-width: normal,
producing visibly narrower text.
2026-04-24 20:19:38 +02:00
Tim Ledbetter
2ff967fd6f LibWeb: Track declared font width on @font-face rules 2026-04-24 20:19:38 +02:00
Tim Ledbetter
5cefb14707 LibWeb: Switch font to loading in FontFace::load()
FontFace.load() set the face's status to "loading" but never switched
the containing FontFaceSets to "loading" or appended to their
`[[LoadingFonts]]` lists. The load-completion handler then found
`[[LoadingFonts]]` already empty and fired switch-to-loaded after the
first face finished, resolving `document.fonts.ready` while faces in
the same set were potentially still loading.
2026-04-24 20:19:38 +02:00
Timothy Flynn
fdbdb0ecd2 LibWebView+UI: Show an error page when we cannot sanitize a URL
Previously, if search was disabled, entering non-URL text would just
silently drop the search query (and on Qt, we would reload the current
URL). We now detect that the query did not result in a navigation and
load an error page instead, which directs the user to enable search.
2026-04-24 20:17:23 +02:00
Timothy Flynn
83eda625d5 LibWebView: Remove new tab page URL from sanitize_urls API
This used to come from UI-specific settings (e.g. QSettings). But it's
stored in LibWebView now, so we don't need callers to provide it.
2026-04-24 20:17:23 +02:00
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