- DataTransferItem/DataTransferItemList/Iterator forward acquireRef/releaseRef
to the owning DataTransfer, so its pooled arena outlives any JS-held wrapper
(fixes a potential use-after-free when JS keeps an item/list/iterator alive
past the DataTransfer).
- addItem reads strings straight into the persistent arena via
toStringSliceWithAlloc, removing the intermediate call_arena copy.
- normalizeFormat uses std.ascii.allocLowerString instead of dupe + lowerString.
- setData: rename dup -> owned_data for clarity.
async_scripts is used to block the "load" event, and preload scripts (a)
shouldn't block this and (b) might never be used. The `preloaded_scripts` map
itself can be cleaned up on shutdown/reset.
Swap `_previous_states: Map(*Element, Bool)` to `_tracked: Set(*Element)` since
we only care about membership (the value was previous always bool). This better
captures the intent.
`checkIntersection` can be called a lot...on every domChanged. And it can call
a known expensive API: `getBoundingClientRect`.
This commit reworks the code to be able to exit from checkIntersection early.
`_previous_states` is now seeded with the target so that, once reported, the
lack of entry can be used to quickly exit.
When profiling airbnb, `checkIntersection` was reported 841 times. With this
change, it was reported twice.
Implement <link rel=preload as=script href=...>. The implementation is similar
to preloadImport / waitForImport. Consider a website that does something like:
```
// pseudo html
<head>
<link preload script1.js>
<link preload script2.js>
<link preload script3.js>
<link preload script4.js>
<script script1.js></script>
<script script2.js></script>
<script script3.js></script>
<script script4.js></script>
```
Then, without preloading, we hit <script script1.js></script> and block while we
load it + execute it. Repeat for 2, 3, 4 ...
With preloading, by the time we block on <script script1.js></script> all the
scripts are already being downloaded in the background.
I opted to remove the script on first use. If a script happens to be used twice
(we have seen this happen for imports, but I guess it's more rare on blocking
scripts), then it'll get re-downloaded the 2nd time, just like before (and just
like before, the http cache is a better mechanism to rely on here).
airbnb preloads 41 scripts.
The TestHTTPServer leaks a thread for every connection that shuts down. This
commit correctly releases it. It also closes a leaking CDP socket.
Full test went from 20s -> 10s for me
Rather than relying on the frame_arena, use a distinct arena for FormData. This
generally results in tighter memory usage, but more importantly it ensures that
if FormData outlives the frame, we don't get a UAF. This can happen if the
FormData is refernced in v8 and finalized late (e.g. after the frame would
appear to still be needed).
Also, in Frame.submitForm use the explicit acquireRef and releaseRef. This
FormData can [in theory] be passed to JS, via the `formdata` event that we fire.
DragEvent extends MouseEvent (new drag_event arm in the MouseEvent type union)
and exposes a nullable dataTransfer, so JS can build a DataTransfer and dispatch
a drop/dragover event whose .dataTransfer.files is readable. Also resolves the
InputEvent dataTransfer TODO with a real field + accessor.
Both events hold a refcount on the DataTransfer for their lifetime and release
it in deinit (via the leaf acquireRef/releaseRef/deinit trio, like MessageEvent
holds a Blob), so the store's arena can't be freed out from under an event whose
JS wrapper is collected first.
Refs #2043
Implements the constructible DataTransfer interface and its item views over a
single drag-data-store: string- and file-kind items, with .files (FileList) and
.types derived from the file items so items.add(file) is visible immediately.
File refs are acquired on add and released on remove/clear; the backing FileList
is frame-tracked and the DataTransfer arena is refcounted so the GC finalizer
reclaims it, leaving no leaks.
Refs #2043
newSession set self.session to undefined, but left it non-null if
Session.init failed. closeSession() then ran deinit() on garbage:
- double-released the arena
- read undefined fields on the never-assigned path
Add an errdefer to null the slot on failure.
I threw Claude at WPT's /WebCryptoAPI/ which represents a significant number of
failing cases.
There were a few very low-hanging fruit, like enabling CryptoKey on Worker and
supporting `{name: string}` in addition to just `string` for some functions.
Some of these "fixes" just handle the error case / validation, which tends to
represent a greater number of WPT cases, e.g. some of the importKey algorithms
aren't fully supported, but they still validate the input and return the correct
errors.
To that end, while there should be considerably more passing cases (~42K), some
of these changes merely unlocked more failing cases, so our total case count
should go up.
Claude's summary:
1. digest {name} object fix (16→80)
2. Symmetric importKey/exportKey/generateKey (AES, HMAC; raw + jwk) + CryptoKey
accessors + DataError
3. EC/OKP importKey usage validation (failure-path)
4. PBKDF2 + HKDF deriveBits, then deriveKey
5. ECDH generateKey + import (spki/pkcs8) + deriveBits/deriveKey
6. AES encrypt/decrypt (CBC/CTR/GCM)
Address review: replace the bare 0/1/2 button values in the
mousedown/release switch (Frame.zig) and the CDP button mapping
(input.zig) with named constants so the code self-documents.