Generator::allocate_register used to scan the free pool to find the
lowest-numbered register and then Vec::remove it, making every
allocation O(n) in the size of the pool. When loading https://x.com/
on my Linux machine, we spent ~800ms in this function alone!
This logic only existed to match the C++ register allocation ordering
while transitioning from C++ to Rust in the LibJS compiler, so now
we can simply get rid of it and make it instant. :^)
So drop the "always hand out the lowest-numbered free register" policy
and use the pool as a plain LIFO stack. Pushing and popping the back
of the Vec are both O(1), and peak register usage is unchanged since
the policy only affects which specific register gets reused, not how
aggressively.
Passing the browser command line and executable path to every WebContent
process just in case we load about:version always felt a bit weird. We
now use the WebUI framework to load this information on demand.
After b1d708dd16, the Toolchain directory
was removed from the repository. Some documentation and scripts still
referenced it, so this commit removes those references. The only
remaining references are in the gitignore file, to prevent bisections
from being polluted by the presence of a Toolchain directory in the
working copy.
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.
This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.
Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
Globally linking to anything isn't a good thing to do and this doesn't
seem to be necessary. If it ends up breaking any platform it can be
readded as a specific link later.
This commit removes an unused but set global found with the new
-Wunused-but-set-globals warning on clang main. This warning is a
subgroup of -Wunused-but-set-variable so we already have it enabled.
Deferred warnings were originally intended to suppress output during
live display, ostensibly to avoid glitch scrolls.
Then, 1af74d1a7c added log capture to the
test-web process. Suddenly, deferred warnings became deadly because
they're flushed during a tiny window after the capture notifier has
stopped draining the tee pipe but before stderr is restored.
This caused a deadlock at exit. The fix is to remove this system and
call warnln directly because display integrity is now protected by
other means.
In commit b9c6263408, I had forgotten we
had this wrapper script to handle the eval magic for us. We just need
to forward the --clang-only argument to the underlying python script.
Mark elements reached by stepping through sibling combinators inside
:has() and use that breadcrumb during generic invalidation walks.
Keep the existing conservative sibling scans for mutations outside
those marked subtrees so nested :is(), :not(), and nesting cases
continue to invalidate correctly.
Also keep :has() eager within compounds that contain ::part(). Those
selectors retarget the remaining simple selectors to the part host, so
deferring :has() there changes which element the pseudo-class runs
against and can make ::part(foo):has(.match) spuriously match.
Add a counter-based sibling-scan test and a regression test covering
the ::part()/ :has() selector orderings.
A DOM mutation under a document that uses any :has() rule currently
walks every ancestor up to the root, invoking invalidate_style_if_
affected_by_has() on each. Most of those ancestors have nothing to
do with :has(), so the work scales linearly with DOM depth.
Introduce an in_has_scope flag on Element, set while evaluating :has()
arguments for invalidation metadata. StyleScope's upward invalidation
walk now terminates at the first element that is neither in :has()
scope nor a :has() anchor, so it only traverses the region where some
:has() rule might actually care about the change.
Keep the existing fast :has() matching paths for normal selector
matching, but bypass them while collecting per-element metadata so the
scope markers still get populated. Node insertion also schedules the
parent for the :has() walk so newly inserted nodes still reach the real
anchor.
The css-has-invalidation suite adds focused coverage for these shapes
and updates the expected counters to reflect the shorter walks.
Track whether any :has() relative selector in a style scope uses a
sibling combinator and let the generic ancestor walk consult that
before scanning ancestor siblings.
This keeps descendant-only :has() invalidations from walking unrelated
siblings while preserving the existing behavior for selectors that use
+ or ~. Add counter-based test coverage so the reduced sibling scans
stay visible through the invalidation counters.
Record per-feature :has() invalidation metadata instead of only tracking
whether some selector somewhere mentions a class, id, attribute, tag,
or pseudo-class. The new buckets preserve the relative selector and a
coarse scope classification for each :has() argument, which gives the
next invalidation step enough information to route mutations more
precisely.
Keep this commit behavior-preserving for mutation handling by only
switching the lookup path over to the new metadata buckets. Expose a
test-only counter for the number of candidate :has() metadata entries a
mutation matched, and add coverage showing that one feature can map to
one or multiple :has() buckets without forcing a document-wide yes/no
answer.
Treat structurally equivalent invalidation plans as equal even when
their descendant or sibling rules were accumulated in a different
order. This lets :has() invalidation merge more of the repeated
descendant-only payloads that still showed up after the earlier
structural dedup.
Add a :has() invalidation counter test that exercises equivalent
selector permutations so this shape stays covered.
Teach :has() matching to recognize the common case of a single
descendant compound made only of tag and class selectors. This lets us
stay on the cheap per-element matcher instead of recursing through the
full relative-selector machinery for each candidate descendant.
Keep the optimization limited to that simple selector shape and fall
back to the generic matcher for everything else.
Avoid the generic relative-selector matcher for child-only tag
selectors inside :has(). These selectors can be answered by walking the
anchor's direct children and checking the tag match directly, which
keeps a very hot path cheaper.
Preserve the existing ancestor cache behavior and fall back to the
generic matcher for all other selector shapes.
Compare invalidation sets, rules, and plans structurally so repeated
descendant and sibling invalidation entries can be merged even when
they were built as separate payload objects.
Also deduplicate pending and active descendant invalidations in the
style invalidator so equivalent rules are not re-applied as the DOM
walk descends. This reduces :has() invalidation fanout while keeping
behavior the same.
Introduce a small set of counters on Document that track the work done
while processing :has() invalidation: how often the upward walk runs,
how many elements it visits, how often matches_has_pseudo_class() is
invoked, how well the per-pass result cache performs, and how many
elements transition from clean to needs-style-update.
Expose the counters through internals so tests can assert precise bounds
on the invalidation work triggered by a mutation, which regular
reference tests cannot express.
Add a css-has-invalidation test suite that covers subject-position,
non-subject-position, sibling-combinator, and no-:has() cases. The
baseline tests share a helper script so later coverage can reuse the
same counter-printing path.
The counters are test-only observation; they do not affect style
computation itself.
GetCalleeAndThisFromEnvironment treated a binding as initialized when
its value slot was not <empty>. Declarative bindings do not encode TDZ
in that slot, though: uninitialized bindings keep a separate initialized
flag and their value starts as undefined.
That let the first slow-path TDZ failure populate the environment cache,
then a second call at the same site reused the cached coordinate and
turned the required ReferenceError into a TypeError from calling
undefined.
Check Binding.initialized in the asm fast path instead and cover the
cached second-hit case with a regression test.
WebAssembly.Memory({shared:true}).grow() reallocates the underlying
AK::ByteBuffer outline (kmalloc+kfree) but, per the threads proposal,
must not detach the associated SharedArrayBuffer.
ArrayBuffer::detach_buffer was the only path that walked m_cached_views
and cleared the cached raw m_data pointer on each TypedArrayBase, so
every existing view retained a dangling pointer into the freed outline.
The AsmInterpreter GetByValue / PutByValue fast paths dereference that
cached pointer directly, yielding a use-after-free triggerable from
JavaScript.
Add ArrayBuffer::refresh_cached_typed_array_view_data_pointers() which
re-derives m_data for each registered view from the current outline
base (and refreshes UnownedFixedLengthByteBuffer::size), and call it
from Memory::refresh_the_memory_buffer on the SAB-fixed-length path
where detach is spec-forbidden.
This patch updates the about:version page to use the shared Ladybird
WebUI styling and present version, build, and runtime details in cards.
It also adds the Ladybird logo and a page title to about:processes,
without otherwise changing the process table behavior.
Element::matches() and Element::closest() were re-parsing the selector
string on every call. The document already maintains a parsed-selector
cache for querySelector/querySelectorAll.
This patch folds that cache's lookup, parse, namespace filtering and
insertion behind a Document::parse_or_cache_selector_list(string)
and calls it from all four entry points. We also bump the cache's
limit to get more hits.
Saves 100ms of main thread time when loading the "insights" view on
our GitHub repo on my Linux machine. :^)
SharedResourceRequest was treating any URL ending in .svg as SVG, even
when the response Content-Type was some other format (like image/webp).
This could result in transformed CDN image URLs to fail decoding.
Only use the .svg URL suffix fallback when no MIME type was provided.
For private key JWK imports, after decoding both d and x, derive
the public key from d and verify it matches x. A private key that
doesn't correspond to the provided public key doesn't "contain the
private key" as required by RFC 8037 Section 2.
After base64url-decoding the x and d fields during JWK import,
verify the decoded byte length matches the expected key size for
the curve (32 for Ed25519/X25519, 57 for Ed448, 56 for X448).
A truncated value does not "contain the public/private key" as
required by RFC 8037 Section 2.
Ed448 public keys are 57 bytes (456 bits), not 56 bytes (448 bits).
The curve is named "Ed448" after its 448-bit prime field, but per
RFC 8032 Section 5.2.5, the parameter b=456 and both private and
public keys are 57 bytes. This caused importKey to reject valid raw
Ed448 public keys with a DataError.
Note: The spec incorrectly says "not 448" for this check.
See https://github.com/w3c/webcrypto/pull/425#discussion_r3070135408
Let's not have LibRegex be the home of LibUnicode FFI. Move these to
LibUnicode so that we can:
1. Use these helpers in other libraries more easily.
2. Swap out icu4c methods with icu4x methods all within LibUnicode.
A future commit will make LibJS and LibRegex depend on LibUnicode's rust
module. The global allocator overrides will cause a compliation error:
error: the `#[global_allocator]` in this crate conflicts with global
allocator in: libunicode_rust
This hides the allocator override behind a feature flag that is enabled
for the standalone LibUnicode shared library.
remove_entries_exceeding_cache_limit() is called after every network
response, but the cache is usually still under budget and nothing needs
to evict. Every one of those calls currently still runs the
window-function eviction SQL over the whole CacheIndex table just to
conclude there is nothing to do.
Short-circuit the call when the cache is already within its configured
size limit. To make that check cheap, maintain m_total_estimated_size
as a running total of the cache's estimated byte size, so the no-op
case becomes a single u64 compare and the DB is only touched when
there is real work.
Bookkeeping:
- Seed the total in CacheIndex::create() via a new
select_total_estimated_size statement (COALESCE(..., 0) so an empty
index returns 0 rather than NULL).
- Each Entry caches serialized_request_headers_size and
serialized_response_headers_size so we don't re-serialize to
recompute its footprint; Entry::estimated_size() centralizes the
arithmetic.
- create_entry() adds the new entry's size. Any row it displaces is
removed via DELETE ... RETURNING so the total stays accurate even
for entries that were never loaded into m_entries.
- remove_entry() and the bulk DELETE statements were extended with
the same RETURNING clause for the same reason.
- update_response_headers() shifts the total by the signed delta
between old and new serialized header size.
Also COALESCEs estimate_cache_size_accessed_since over an empty table
to 0 so callers don't have to special-case NULL.
create_entry() issues INSERT OR REPLACE in SQL, so the on-disk row is
correctly overwritten when a (cache_key, vary_key) pair is re-inserted.
But the in-memory m_entries vector was only appended to, leaving the
stale Entry alongside the new one. Subsequent find_entry() calls could
then return the old metadata even though the DB had moved on.
Add an off-thread preparation step for downloaded vector fonts so
WOFF2 resources can be decompressed before LibWeb tries to create a
typeface from them. This avoids doing the conversion work on the main
thread during @font-face loading.
Expose raw WOFF2-to-TTF conversion from LibGfx's WOFF2 loader and use
that from the new preparation path. Keeping the libwoff2 integration
in LibGfx preserves the layering between LibWeb and the third-party
decoder while still letting LibWeb schedule the work off-thread.