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.
Wire the CDP Input.dispatchMouseEvent "mouseMoved" type to a new
Frame.triggerMouseMove, which hit-tests the point via elementFromPoint
and dispatches mousemove, mouseover and mouseenter on the target,
mirroring the existing triggerMouseClick and the actions.zig hover()
event semantics.
Previously mouseMoved was silently ignored, so element.hover() over
Playwright/Puppeteer (CDP) fired no events. Addresses the hover gap
reported in #2043.
Only unwrap the `__root` sentinel injected for array schemas, ensuring
single-field object schemas retain their shape. Also update synthesis
prompt instructions for modern JS and tool fidelity.
Depends on: https://github.com/lightpanda-io/wpt/pull/69
WPT can send a list of JSON message to the browser in order to simulate user
interaction, e.g.:
{ type: "pointer", actions: [{type: "pointerMove", x, y, origin}, ...] }
While some of these aren't meaningful for us, many are. A lot of these are just:
1 - scroll to an element
2 - mouse down
3 - mouse up
With the main goal of generating trusted events.
This adds script compilation to the Runner.waitForScript loop. The goal here
isn't really to improve waitForScript - we generally expect these scripts to
be very simple. The goal is to introduce script caching so that it could be
used in more important places (e.g. ScriptManager).
Depends on https://github.com/lightpanda-io/zig-v8-fork/pull/181
Start adding support for "popover". This represents the single biggest non-
canvas grouping of failures in WPT `/html/**`. I suspect it has little real-
world impact, but it isn't impossible something blocks on a missing popup
event.
This essentially comes down to supporting various methods (e.g. showPopup) on
the HtmlElement as well as making the Input and Button popup-target aware.
The document keeps a list of opened popups which is used when we have to
determine if a popup is open (the new :popover-open pseudo selector) or when
toggling them.
The point of children.Children was to save 8 bytes. But it doesn't. I don't
know if it ever did. It's @sizeOf(Children) is 16 bytes, exactly what
@sizeOf(DoublyLinkedList) is.
Maybe this came from when DoublyLinkedList held the length, in which case it
would have been different.
Either way, storing ?*DoublyLinkedList in Node:
1 - Takes the same amount of memory
2 - Simplifies the code
3 - Removes 1 indirection and 1 extra allocation in the case of having more than
1 child (compared to how it was before).
Update agent.md and the LLM synthesis prompt to explicitly state
that agent primitives are synchronous and blocking, and should not
use async/await or Promises.
Replaces static left/right status strings with ranked segments.
When the terminal is too narrow, segments are dropped lowest-rank
first to prevent wrapping and layout corruption.
Implement the Node.lookupPrefix(namespace) method per the WHATWG DOM
"locate a namespace prefix" algorithm: match the element's own
namespace and prefix, then scan its xmlns: attribute declarations for
one whose value is the namespace, then recurse to the parent element.
This is the inverse of the existing lookupNamespaceURI and completes
the namespace-introspection trio alongside isDefaultNamespace.
- Use `std.meta.fieldNames` for the save mode prompt.
- Update terminal prompt functions to accept null-terminated strings.
- Respect the resolved save mode in `synthesizeSave`.
Removes the `-i`/`--interactive` CLI flag and live file-based
recording. Instead, the REPL now supports a `/load <path>` command
to run scripts from disk, and `/save` to export the in-memory
session recording.
The `Recorder` is simplified to be purely in-memory, and the script
runtime is moved to `src/script/Runtime.zig`.
BREAKING CHANGE: The `-i`/`--interactive` flag has been removed. Use
the `/save` and `/load` commands within the REPL instead.
Integrates isocline's top and bottom bar APIs to display the active
provider, model, and shortcut hints. Updates the status bar on
terminal resize and mode switches.
We still occasionally see release overflow errors. I think this is a UAF, since
I cannot find any mismatched acquire/releases. So this adds a canary value to
every RC and includes it in the crash dump.
If the canary value is different than what we set it at, then we have a UAF.
If the canary value is set to the poison we set on release, then we have a
double free.
If the canary value is unchanged, then it's inconclusive.
The first is that it can outlive the Transfer and thus has to dupe anything
it'll use (e.g. the frame_id off the request).
The second is that flushFrame is reentrant (flushFrame -> fire -> flushFrame)
and that results in the active list and list pointers becoming invalid.
Removes the redundant `Primitive` enum and uses `BrowserTool` directly,
filtering by `isRecorded()`. This reduces duplication and simplifies
the mapping between JS primitives and browser tools.