This Week in WebKit — March 28 – April 3, 2026
Featured
When an HTMLMediaElement was routed through Web Audio, AVPlayer's varispeed mode shifted both rate and pitch, and the WebContent-side resampler couldn't undo the pitch on the cross-process path. The fix inserts a PitchShiftAudioUnit wrapping CoreAudio's AUTimePitch in the GPU process before samples cross IPC, and propagates playbackRate and preservesPitch as separate IPC messages into the WebContent-side resampler. The AudioUnit's C-style renderCallback captures object state, the two IPC messages can land out of order relative to an in-flight sample burst, and CARingBuffer::store was tweaked alongside. That's new surface in three areas that historically host audio CVEs.
GetByVal/PutByVal with undefined, null, true, or false used to coerce to strings or fall to the C++ slow path; this commit grows a parallel IndexedUndefinedKeyLoad/Miss/Transition family (and equivalents for null/true/false) that emits an isUndefined()-style identity check in front of normal structure-based property lookup. convertToNonStringPrimitiveKeyAccessType performs a late mapping inside the repatch path, and requiresIdentifierNameMatch/requiresInt32PropertyCheck now return false for the new types — affecting every downstream caller that branched on those predicates. canBeShared and canReplace govern IC eviction and stub reuse; if a stub built for one structure-key pair fires for a structurally different object under another, you have type-confused property dispatch on a freshly-introduced fast path. The change lands the same week as the butterfly-less JSObject restructuring, and any DFG/FTL consumer of butterfly that bypasses structure guards now coexists with a wider IC keyspace.
IDBIndex.getAllRecords() lands as new W3C-spec API surface, returning IDBRecord[] with key, primaryKey, and value. The semantic twist: in the existing Keys/Values paths, 'key' meant the object store's primary key — but in the new Records path, 'key' is the index key and primaryKey is the store key, and that inversion has to hold across every layer. Three parallel arrays now flow from SQLite through IPC serialization into JS bindings, and any path that fills two but not the third — early returns, MemoryIndex shortcuts, error edges — produces an IDBRecord whose fields belong to different rows. The value getter then pulls these through deserializeIDBValueWithKeyInjection at JS-property-access time, the same path that has historically been a bug source.
VMTraps freezes a target VM thread via mach thread_suspend() to install trap breakpoints or verify ownership, and any code inside the suspended-thread lambda must be effectively async-signal-safe. The fix replaces ownerThread() with ownerThreadUID(), because the former returns a RefPtr
PowerSort adopts a TimSort trick: when one run dominates, gallopLeft/gallopRight probe at 1, 2, 4, 8… positions to find the boundary, then bulk-copy the entire dominated region. The probe and follow-up binary search both invoke user comparators; the bulk copy that completes the merge trusts the len captured at function entry. A comparator callback that mutates array length, resizes the backing store, or detaches a TypedArray buffer between the probe's success and the bulk copy is never revalidated. Both JSArray and JSGenericTypedArrayView prototypes share the new code, on a sink that has historically yielded OOB reads and use-after-detach across every major engine.
Notable development
-
Non-sRGB Gradient Rendering via Sampled CGGradient
optimization
-
Butterfly-less JSObject for Wasm GC
refactor