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.
Element.scrollIntoView and scrollIntoViewIfNeeded were no-op stubs.
Implement them to scroll the window so the element's position is
brought into the viewport, using the element's document position (the
same source getBoundingClientRect uses) and Window.scrollTo.
This lets automation reach below-the-fold elements before interacting
with them.
Input.dispatchMouseEvent ignored the button and clickCount params, and
mousePressed only fired a click event (never mousedown). Add them:
- mousePressed now fires mousedown carrying the pressed button.
- mouseReleased fires mouseup, then the button-appropriate activation
event: click for the main button, auxclick for the auxiliary button,
and contextmenu for the secondary (right) button.
- a clickCount of 2 additionally fires dblclick.
This unblocks right-click, middle-click and double-click interactions
for Playwright/Puppeteer scripts. Follows the mouse event work in
#2636, #2640 and #2641.
Passes WPT /html/webappapis/atob/base64.html
Two changes
1 - Use the forgiving decoder already in data_url
2 - Coerce input (3 => "3")
The 2nd change was more interesting. These take a js.String.OneByte as an
optimization, which doesn't coerce. To preserve this optimization a union was
used with a `raw: []const u8` fallback (and our bridge always coerces to
a `[]const u8`)
collectForm now emits File entries for <input type="file"> (one per
selected file, or a synthetic empty File with application/octet-stream
when none is selected, per WHATWG). multipartEncodeEntry writes the
filename, Content-Type, and file bytes per RFC 7578; Value.asString /
format fall back to the filename for urlencoded / text-plain encodings.
Just a random test I happen to see in WPT, 0/99 -> 99/99:
/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.worker.html
This limit isn't about correctness,it's just about making sure a test doesn't
block forever. 2 seconds does seem like plenty of time, but I'm thinking it's a
slow/busy CI. If it still happens after this bump, could mean there's an actual
issue.
The first is with WGS not flushing the deferring layer after its synchronous
importScripts call, see: https://github.com/lightpanda-io/browser/pull/2329#discussion_r3271068955
The second is from the ability of an XHR request to be re-used. This was gated
on a boolean, but the ordering means that the 1st requests' released gets
blocked by the gate, and thus we're always 1 release short. The solution is to
use a counter instead of a boolean.
A fetch() deferred behind a parser-blocking script keeps a DeferredContext
in the DeferringLayer whose forward.ctx points at the Fetch struct, which
lives in a page/session arena. If the transfer completes while deferred,
it is deinited and unlinked from the frame owner, so abortTransfers ->
abortOwner can't reach the now-orphaned context. It lingers in the active
list, and when the page is torn down its Fetch arena is freed; a later
flushFrame (e.g. the next page's parser-blocking script popping) replays
the buffered header callback into the freed Fetch -> use-after-free.
Add DeferringLayer.cancelFrame to drop these orphaned (terminal) contexts
during Frame.abortTransfers. Non-terminal contexts still have a live
transfer that cleans them up through its own callback path, so they are
left alone.
When a CDP client disconnects, through a series of step, we call `Isolate::
TerminateExecution`. The problem is that this only terminates the currently
executing script. If scripts are nested, or another script runs immediately
after the terminated one, the worker keeps running.
I was specifically trying to fix some 100% endless loop in a few WPT tests, e.g
Worker-terminate-forever-during-evaluation.html
The first is the issue described above. We potentially need to guard every entry
into script executing with `if (!env.is_terminated)`. AND/OR we need to bubble
an error when a script _is_ terminated (right now, most JS errors map to
error.JsException which makes this hard). For the specific WPT failures, I opted
for a more targeted fix directly in the Worker. But I think we need to keep an
eye out on other cases where this can happen.
I also discovered another issue, which is not addressed is this commit:
var worker = new Worker("endlessloop.js");
worker.terminate(); <-- never gets called
Given our current architecture, the only solution I can think of is a watcher
thread (or re-using the main/network thread).
A blocking <script src> loads via HttpClient.syncRequest, which spins on
tick(200, .sync_wait). That drain mode only dispatches Fetch-interception
methods (it can't free Page/Frame state mid-parse), so Target.closeTarget /
Target.disposeBrowserContext / Page.close sit undispatched until the blocking
fetch completes — i.e. up to the per-request timeout, per blocking script.
For storefronts with slow/never-completing third-party subresources this
stalls page.close()/context dispose for tens of seconds and collapses CDP
throughput under concurrency (the process stays up; other connections are
unaffected).
Fix: while waiting, peek the inbox; if a teardown/close command (or a
client close/disconnect) is queued, abort the in-flight blocking transfer.
syncRequest then returns the same way it already does on a fetch failure,
the parser unwinds to the next safe .all drain, and the queued command
runs there. No command is dispatched mid-parse, so no UAF.
Repro: 8 workers looping newContext->goto(commit)->page.close against a page
with 15 never-answered subresources went from 0.1 ctx/s (page.close stalling
~30-60s) to ~25 ctx/s; a no-hanging-subresource baseline is unchanged.
Fixes#2646