Adds instructions on using `url` parameters with `markdown`, `tree`,
and `html` to avoid redundant `goto` calls. Also adds a section on
browsing efficiently to minimize page loads and triage search results.
- `/clear` forgets conversation history, usage, and node IDs while
keeping the loaded page and cookies.
- `/reset` does a full reset, starting a fresh browser session.
Updates documentation to clarify that script primitives use CSS
selectors instead of backendNodeIds. Explains how to use `/nodeDetails`
to get selectors. Clarifies the difference in `/save` behavior
between `--no-llm` and LLM modes.
Updates agent-related documentation to reflect recent changes:
- Removes obsolete `follow` option from `extract` schema.
- Documents automatic printing of the script's final expression.
- Adds details on Ollama auto-detection and new CLI flags.
- Documents the `/logout` command and updated tool list.
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
Queries Ollama's local catalog during initialization to ensure the
requested model is installed. Aborts one-shot runs if missing, or
warns during interactive sessions.
Introduces `SelectorPath` to generate a unique, minimal CSS selector
for a given element. This selector is now returned in `nodeDetails`
to simplify element targeting for drivers.
Wire the CDP Input.dispatchMouseEvent "mouseWheel" type to a new
Frame.triggerMouseWheel, which hit-tests the point via elementFromPoint
and dispatches a wheel event (with deltaX/deltaY) on the target. When
the wheel event is not cancelled, it applies the scroll offset and
fires a scroll event, mirroring the wheel handling in WebDriver.zig.
Previously mouseWheel was silently ignored. Follow-up to #2636.
It has somehow acquired (or always had?) multiple dangling pointers. Luckily
(or worse?) they aren't used anywhere. Remove mime.params (dangles off the
input) and the content_type `.other` values (dangles off the local mime var).
Also, remove double lowerCase.
Cache the xml document so that subsequent calls to getResponseXML return the
same document.
Fix testing.js harness - reject multiple testing.async() calls per block. This
isn't reliable since the runner can temporarily see the count reach 0 before
the next async block starts.
Replaces manual tool-by-tool argument parsing in the runtime with a
schema-driven `marshalArgs` function. This supports merging trailing
options objects with leading positional arguments (e.g., `goto(url,
options)`). Also unifies positional definitions in `Schema` for both
the runtime and the recorder.
These are needed for apple.com, completely broken without.
Obviously Claude wrote most of this code. The test that it wrote _does_ pass
in FireFox and a reasonable number of WPT *DOMMatrix* test pass.
A relatively small subset of this was needed for Apple, but there was plenty
of low-hanging fruit in the WPT tests.
Also, some of these WPT tests uncovered a name collision in our WPT reporter:
https://github.com/lightpanda-io/wpt/pull/70
Wire the CDP Input.dispatchMouseEvent "mouseReleased" type to a new
Frame.triggerMouseRelease, which hit-tests the point via
elementFromPoint and dispatches a mouseup event on the target,
mirroring triggerMouseClick / triggerMouseMove and the mouseup
semantics already used by WebDriver.zig.
Previously mouseReleased was silently ignored. Follow-up to #2636.
Until now Frame.submitForm performed no constraint validation: requestSubmit()
and interactive submits (Enter, clicking a submit button) fired the `submit`
event and proceeded regardless of control validity — a `<form><input required>`
would submit with the field empty.
Implement the HTML "submit a form element" interactive-validation step: in the
fire_event branch, run form.checkValidity() as a gate before dispatching
`submit`, abort submission when it fails (checkValidity fires `invalid` on the
offending controls), and skip the gate when the form is in the no-validate
state. form.submit() keeps bypassing validation, per spec.
To drive the no-validate state, add the HTMLFormElement.noValidate IDL
attribute reflecting the `novalidate` content attribute, and honor a submitter's
`formnovalidate`. noValidate gates submission only — checkValidity()/
reportValidity() still validate unconditionally.
Populate FileList with real File objects and expose them on
HTMLInputElement:
- input.files returns a live, identity-stable FileList for type=file
(null for other input types)
- input.value returns the spec "C:\fakepath\<name>" string
- required file inputs report value-missing only when empty
Implement CDP DOM.setFileInputFiles: load files from disk into a file
input (MIME sniffed by extension) and fire input + change events.
File backing arenas are reference counted via their Blob proto, so the
owning Frame now acquires/releases them and frees any still held at
teardown, preventing an ArenaPool leak for CDP-set files never read
from JS. A scoped errdefer frees partially-created files when one path
in a multi-file set fails to load.
Return an XMLHttpRequestUpload (inheriting XMLHttpRequestEventTarget) from
the lazily-cached `upload` attribute. Fixes htmx login flows that called
`xhr.upload.addEventListener(...)`.
Adds the overrideMimeType(mime) method.
Wires the final MIME type (override ?? response) into responseXML for
the default response type (""): when the final MIME is text/xml, the
body is lazily parsed into a Document and cached in a new
_response_xml field. Other XML MIME types (application/xml, image/svg+xml)
land in Mime.ContentType.other whose backing slices are unsafe to read
after Mime.parse returns; supporting them is left for a follow-up.
The override does not affect response / responseText today —
responseText charset decoding and the document responseType's
HTML-vs-XML parser choice are noted as follow-ups in the code.