Commit Graph
9593 Commits
Author SHA1 Message Date
Adrià Arrufat e56045f4bf Merge pull request #3560 from lightpanda-io/agent-repl-simplify
agent: REPL output fixes and command dispatch simplification
2026-09-18 09:51:49 +02:00
Karl Seguin e3aa36f83a Merge pull request #3563 from lightpanda-io/wpt-build-fix
fix build for wpt_extensions
2026-09-18 11:11:59 +08:00
Karl Seguin 50c4f52df8 fix build for wpt_extensions 2026-09-18 11:11:36 +08:00
Karl Seguin 4d1d5f1291 Merge pull request #3561 from lightpanda-io/orderfile-regen
orderfile: regenerate the hot-code profile
2026-09-18 09:25:35 +08:00
github-actions[bot] b275a3960c orderfile: regenerate the hot-code profile
hot set 15196KB resident at 4KB fault-around: 21450 text, 21408 rodata symbols
2026-09-18 01:05:13 +00:00
Karl Seguin 6db04f694a Merge pull request #3548 from lightpanda-io/img-srcset
webapi: img srcset support
2026-09-18 08:04:21 +08:00
Karl Seguin 790a6e5bb9 hook up child srcset when created via fragment parser 2026-09-18 06:49:58 +08:00
Adrià Arrufat 290e4f81d9 agent: simplify REPL command dispatch and provider bookkeeping
- `available_providers` holds the static enum tag names; drop the dupe
  loop, its errdefer and the per-string frees.
- `reconcileModel` returns `error.ModelNotAvailable` directly instead of
  a `use`/`abort` union the caller only mapped to that same error.
- `runCommand`/`printCommandResult` take `Command.ToolCall`; the caller
  already has it, so the unreachable "no tool mapping" branches go.
- `handleSave` reuses `rememberSavePath`, which now propagates its
  allocation failure so a first save under OOM warns instead of
  unwrapping a null `save_path`.
- `SlashCommand.all_names` is a comptime `++` of the three name lists.
- `buildUserMessageParts` reads the attachment once for both text and
  image, and prepends the text part instead of copying the list.
- `printSeverity`/`formatBulletLine` use `allocPrint` and the shared
  `emitStderr`; drop an unreachable `ends_ws` check in `renderMetaHint`.
2026-09-18 00:45:55 +02:00
Adrià Arrufat 9b7225b580 agent: don't drop REPL tool lines or cut result text mid-codepoint
In a REPL whose stderr isn't a tty the spinner is disabled, so
`agentToolDone`'s `emitAbove` returned false and the `● [tool: …]` line
was discarded. `printToolOutcome` already fell back to a raw stderr write
in that case; share that fallback through `emitStderr`.

The non-REPL result line sliced `text` at a byte offset, which could
split a multi-byte codepoint and emit garbage. Truncate on a UTF-8
boundary instead.
2026-09-18 00:44:49 +02:00
Karl Seguin dbe6271479 Merge pull request #3559 from lightpanda-io/fix-preload-import-build
ScriptManagerBase: fix main build after #3485/#3543 merge
2026-09-18 06:31:24 +08:00
Karl Seguin 798d054699 Merge pull request #3542 from lightpanda-io/cdp-device-pixel-ratio-and-char-input
cdp: window.devicePixelRatio from viewport scale and Input char text insertion
2026-09-18 06:29:58 +08:00
Adrià Arrufat 80a526b813 ScriptManagerBase: pass headersForRequest options in preloadImport
#3485 added an options argument to headersForRequest and #3543 added
this call without it. Each merged cleanly on its own; main no longer
builds.
2026-09-18 00:19:04 +02:00
Karl Seguin bd34894996 Merge pull request #3552 from lightpanda-io/webdriver-apis
WebDriver: add various basic WebDriver APIs
2026-09-18 05:54:14 +08:00
Karl Seguin 437a9c27d6 Merge pull request #3543 from lightpanda-io/fix-import-crash
crash: fix a rare import crash
2026-09-18 05:53:27 +08:00
Karl Seguin 3c4e260274 Merge pull request #3557 from lightpanda-io/test-runner-unnamed-slowest
test_runner: keep unnamed tests out of the slowest list
2026-09-18 05:53:14 +08:00
Pierre Tachoire 66e15748a6 Merge pull request #3485 from lightpanda-io/fetch-referrer-policy
Fetch Referrer Policy + CorsGate referrer checking
2026-09-17 18:14:53 +02:00
Adrià Arrufat 4cd522c58a test_runner: no trailing blank line after the summary
Each optional block after the pass count (slowest list, metrics JSON,
failed-test summary) now prints its own leading separator instead of the
previous block printing a trailing one, so the output never ends with an
empty line. Printer.status places the color reset before a trailing
newline so the escape sequence no longer opens the next line.
2026-09-17 17:42:21 +02:00
Adrià Arrufat 5bcea2fc83 test_runner: skip the slowest-tests block when nothing was timed 2026-09-17 17:37:48 +02:00
Adrià Arrufat a39fa8fac6 test_runner: keep unnamed refAllDecls tests out of the slowest list
The Zig 0.16 port of SlowTracker.endTiming dropped the early return for
unnamed tests and discarded the flag, so the refAllDecls blocks were
timed and queued like real tests. With a filter that matches nothing they
were the only candidates and filled the "Slowest 5 tests" output.
2026-09-17 17:36:32 +02:00
Karl Seguin 0afa6aa47b Merge pull request #3554 from lightpanda-io/semantic-tree-frame-coverage
test: child-frame coverage for the JSON tree and the MCP tree tools
2026-09-17 21:15:20 +08:00
Adrià Arrufat 21c2d71e84 input: one key target resolver and shared activation predicates
Every key path resolves its target through user_input.focusedElement,
which is document.activeElement with the <body> fallback. WebDriver and
the MCP press action used to fall back to the document node instead, a
target no default action or char step handles.

The Enter and Space activation rules share isButton, and the text-entry
rule (no text goes into a checkbox or radio) lives on the TextEntry
mixin as acceptsTextEntry rather than as a type test inside the shared
insertion helper. pressKey takes its extra ref only when a keypress will
be built from the keydown.
2026-09-17 14:09:12 +02:00
Adrià Arrufat 7b30a7447e input: activate buttons on Enter's keypress, submit once
Chrome clicks a button on the keypress Enter produces, after the keypress
fires; only links follow Enter on the keydown. Clicking on the keydown put
the click before the keypress and, with the char step also submitting for
button-type inputs, submitted the form twice.

The MCP press action relied on the same char step for Enter, so its own
implicit submission is gone with the double submit it caused on buttons.
2026-09-17 12:45:49 +02:00
Karl Seguin 243ff0b8be Merge pull request #3553 from lightpanda-io/semantic-tree-init
SemanticTree: resolve the owner frame once in init
2026-09-17 18:29:46 +08:00
Adrià Arrufat a6e7767533 test: child-frame coverage for the JSON tree and the MCP tree tools
The #3549 test only covered LP.getSemanticTree's text format. Cover
the JSON format with interactiveOnly on the same fixture, and drive the
MCP tree and nodeDetails tools through call() with a child-frame
backendNodeId. Passing the main frame in either tool now fails the
suite instead of only tripping a debug assert.
2026-09-17 12:28:48 +02:00
Adrià Arrufat a160b8d552 SemanticTree: resolve the owner frame once in init
Every caller resolved node.ownerFrame(frame) before building a
SemanticTree or calling getNodeDetails, and every one made the same
decision on a frameless node. Move that into SemanticTree.init, which
returns error.FramelessNode, and turn getNodeDetails into a method so
it goes through the same constructor.

With init as the only entry point the per-method assertOwns is
redundant, so drop it along with its copy of StyleManager's helper.
2026-09-17 11:59:01 +02:00
Karl Seguin 18dcb8ac63 WebDriver: add various basic WebDriver APIs
Basic stuff that builds ontop of navigate support (https://github.com/lightpanda-io/browser/pull/3538)

The first two already existed. And I added a few more than the last 3, but the
last 3 demo better in a commit message:

```zig
$ curl -X POST "http://localhost:9222/session" --data '{}'
{"value":{"sessionId":"695a066e-4f79-4856-9af9-b92fdf18da7a",....

$ curl -X POST "http://localhost:9222/session/695a066e-4f79-4856-9af9-b92fdf18da7a/url" --data '{"url": "https://lightpanda.io"}'
{"value":null}

$ curl -X GET "http://localhost:9222/session/695a066e-4f79-4856-9af9-b92fdf18da7a/title"
{"value":"Lightpanda | The headless browser"}

$ curl -X GET "http://localhost:9222/session/695a066e-4f79-4856-9af9-b92fdf18da7a/source"
{"value":"<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewpor....

$ curl -X GET "http://localhost:9222/session/695a066e-4f79-4856-9af9-b92fdf18da7a/screenshot"
{"value":"iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAYAAADo08FDAAE/fUl
```
2026-09-17 17:35:07 +08:00
Karl Seguin 1631755f71 Merge pull request #3549 from lightpanda-io/semantic-tree-frame
internal: Always correct SemanticTree context
2026-09-17 17:26:19 +08:00
Karl Seguin 93230fd4aa Merge pull request #3551 from lightpanda-io/interactive-elements-frame
cdp: LP.getInteractiveElements reads the node's own frame
2026-09-17 16:24:23 +08:00
Adrià Arrufat f81d72c0dc cdp: LP.getInteractiveElements reads the node's own frame
A nodeId root inside a child frame was walked with the main frame. The
style checks already resolve the owner frame per element, but the
listener map was built from the main frame's event manager, so
listener-only elements in the iframe were reported non-interactive, and
relative hrefs resolved against the parent's base URL.

Resolve the root's owner frame before the walk, as getSemanticTree and
getNodeDetails do.
2026-09-17 10:05:50 +02:00
Adrià Arrufat 74bf43e732 input: type text in a char step, not in the keydown default action
Follow Chrome's model. A keydown only runs its own default action (Tab,
caret moves, Backspace, Enter activation). Text is typed by the char half
of the press, which fires keypress and then beforeinput/textInput, so
either can veto the edit.

A keyDown carrying `text` runs the char step inline (Puppeteer,
Playwright). A text-less keyDown followed by a `char` message runs it on
the char (chromedp), so each character is typed once. A cancelled
text-less keyDown drops the char that follows it, as Chrome does.

BiDi, WebDriver and the MCP press action go through the same pressKey,
deriving the text from the key. pressKey holds a ref on the keydown for
the keypress it builds, so nothing reads the event after dispatch.
Input.insertText fires beforeinput like a key press does.
2026-09-17 09:53:47 +02:00
Muki Kiboigo b776bad190 add option to headersForRequest 2026-09-16 22:49:49 -07:00
Karl Seguin 582df9a874 Merge pull request #3550 from lightpanda-io/fix-service-worker-log-msg-len
dumb: fix log message length
2026-09-17 13:04:13 +08:00
Karl Seguin 912e6e383e dumb: fix log message length 2026-09-17 12:41:32 +08:00
Karl Seguin 13638ff50b internal: Always correct SemanticTree context
Applies the frame-ownership pass to SemanticTree, copying what we did for
StyleManager (1). SemanticTree doesn't visit iframes, so the frame of the root
is the frame/frame._style_manager we need to target for all visited nodes.

Like #3536, it's up to the callers to (a) get the correct frame and (b) decide
what to do on a frameless-node.

(1) https://github.com/lightpanda-io/browser/pull/3536
2026-09-17 12:13:53 +08:00
Karl Seguin 6c92ec1d7f Merge pull request #3541 from lightpanda-io/add-openrouter-orcarouter
agent: add OpenRouter and OrcaRouter providers
2026-09-17 12:01:05 +08:00
Karl Seguin 24c9074022 fix foster parenting
TIL: foster parenting is apparently something that happens when a non-table
child element is placed inside of a table, e.g.

	<table><img src...></table>

Apparently, this was common enough that it requires special handling. html5ever
goes through a different path (_appendBeforeSiblingCallback) which would skip
our previous parseInserted.
2026-09-17 11:47:46 +08:00
Karl Seguin 7fa76258b4 webapi: img srcset support
The image src can now come from a srcset, including the source of a picture.
The (simple) Parser is all Claude.

There's some similarity here with the recently changed Select<->Option (1)
code in that, changes to a child (source/option) need impact their parent (
picture/select).

(1) https://github.com/lightpanda-io/browser/pull/3502
2026-09-17 11:17:21 +08:00
Karl Seguin 32a43c1c97 Merge pull request #3545 from lightpanda-io/orderfile-regen
orderfile: regenerate the hot-code profile
2026-09-17 10:27:56 +08:00
github-actions[bot] 981cf1127d orderfile: regenerate the hot-code profile
hot set 15112KB resident at 4KB fault-around: 21407 text, 21375 rodata symbols
2026-09-17 01:05:18 +00:00
Karl Seguin a5e6ad80bd Merge pull request #3544 from lightpanda-io/fix-webdriver-build
ci: fix webdriver build
2026-09-17 09:03:37 +08:00
Karl Seguin 8a8a08bc48 ci: fix webdriver build 2026-09-17 09:03:16 +08:00
Karl Seguin e13f10841f Merge pull request #3507 from R4m1r0qu41/refactor/shared-click-dispatch-v2
Share pointer/mouse click dispatch across click paths
2026-09-17 08:20:41 +08:00
Karl Seguin a4e1fa95b9 crash: fix a rare import crash
Currently, our waitForImport blocks the caller, but continues to process any
already-queued requests. This can result in new JavaScript running while v8
is linking modules and that JavaScript can itself import a module that is
part of the still-being-linked graph.

waitForImport now works like a syncRequest. While HttpClient will continue to
make progress on all transfers, all other transfers will gate behind the waiting
one (using the same infrastructure that exists for syncRequest).

This crash was seen on an unknown srape URL.
2026-09-17 08:17:13 +08:00
Karl Seguin f65f737617 update for main changes 2026-09-17 07:58:40 +08:00
Karl Seguin 063fc0ebb6 dedupe code and use PointerInput 2026-09-17 07:55:59 +08:00
Adrià Arrufat 082b1bd15a refactor(input): bundle dispatcher params into PointerInput
The two low-level trusted-event dispatchers took button (which button changed)
and buttons (the held mask) as adjacent, swappable positional args. Bundle the
shared event fields into a PointerInput struct with named fields, mirroring the
file's HoverContext, so a transposition is a field name rather than a silent bug.
2026-09-17 07:55:59 +08:00
Adrià Arrufat 4ecd38ece8 input: PointerButtons gesture struct, shared focus helper, trim comments
Fold the two loose Page fields (input_pressed_buttons, input_mousedown_suppressed)
into a PointerButtons struct in user_input.zig that owns the chord mask and the
press/release transitions, so triggerMousePress/Release keep only dispatch.

Add runMouseDownFocus so the three click callers stop repeating the
!suppress_mouse and !suppress_focus gate; a focus-error policy param keeps each
caller's warn-vs-propagate behavior.

Trim the verbose dispatch/trigger and CDP-test comments to a single sentence
each, moving behavior contracts onto the function doc-comments; also drop the
stale buttonsMask comment orphaned by the earlier buttonsBitmask reuse.
2026-09-17 07:55:59 +08:00
Adrià Arrufat 6be87585c7 webdriver: reuse buttonsBitmask from user_input 2026-09-17 07:55:59 +08:00
Ramiro_quaiandClaude Sonnet 5 51a92dd0fd fix(input): address review on the CDP/chord click dispatch
Round-1 review feedback from arrufat on #3507 (share pointer/mouse click
dispatch across click paths):

- CDP mousePressed now threads clickCount into mousedown's detail
  (mouse_detail), matching the MCP path (actions.click) instead of
  always firing detail 0. Threaded through BiDi's press call too, which
  was already tracking click_count for release but silently dropping it
  on press.
- dispatchClickAsPointer now carries the still-held buttons mask instead
  of hardcoding 0, so a primary click firing mid-chord (the primary
  button releasing while another button is still held) reports the
  correct PointerEvent.buttons.
- Fixed a regression caught while verifying the above against live
  Chrome: the first fix's fallback forced clickCount 0 (CDP's default
  when the field is omitted) to detail 1. Chrome and Firefox both
  preserve 0 there, and Chrome doesn't fire `click` at all in that case
  — the fallback now preserves 0 instead of forcing 1.

Left two pre-existing issues alone, flagged in code comments instead of
fixed, per arrufat's own scoping:
- triggerMouseRelease's parallel clickCount-0 fallback has the same
  mismatch on the release side; not introduced by this PR.
- The chord's activation-event ordering (contextmenu on the right press
  vs. this codebase's release-only contextmenu, and no auxclick) is a
  real, pre-existing deviation from both Chrome and Firefox, confirmed
  on Firefox too during this round, not Chrome-specific.

Test coverage: a parametrized CDP test over clickCount 0/1/2 asserting
mousedown's detail; a chord test asserting a mid-chord primary click's
buttons mask. Two more tests were added independently during review
audit and kept as-is: clickCount 2 on a press+release pair asserting
detail 2 on mousedown/mouseup/click plus dblclick, and a chorded press
asserting its own mousedown branch also carries the press message's
clickCount.

Confirmed against live Chrome (headless, raw CDP) and Firefox (headless,
WebDriver BiDi) for both the mousedown-detail and chord-buttons fixes.
zig fmt --check clean; cdp.input 20/20 and bidi.input 5/5 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 07:55:59 +08:00
Ramiro_quaiandClaude Sonnet 5 45e6cd47f9 fix(input): run the mousedown focus default action on a chorded press too
A second independent Codex audit on the rebased bdabfea42 found a real
regression the chord fix itself introduced: the chorded-press branch in
triggerMousePress dispatched the compatibility mousedown but discarded its
cancellation result and never called focusForMouseDown, unlike the
first-button path. Pressing a second button on a different element while
the first is still held (e.g. right-click a second field while holding
left on the first) left focus on the original element instead of moving
it to the new mousedown's target, diverging from real Chrome.

Independently verified before committing:
- Traced the diff: the chorded branch's `_ = try dispatchMouseEventOn(...)`
  discarded the return value entirely, so suppress_focus was never
  computed and focusForMouseDown was never reachable from that branch —
  confirmed this matches the first-button path's own
  `if (!press.suppress_mouse and !press.suppress_focus) try
  focusForMouseDown(...)` structure, which the chord branch should mirror
  but didn't.
- Reverted the one-line fix (kept the new test staged) and confirmed the
  new "chorded mousedown focuses its target unless pointerdown or
  mousedown was cancelled" test fails against the pre-fix code, then
  restored it.
- Ran the full suite: zig build test and -Dwpt_extensions both 1521/1521.
- Rebuilt the binary and ran the extended tools/shared-click-audit.mjs
  --assert-chord --assert-chord-focus against it: chord_focus_contract
  PASS, with the raw event trace confirming focus actually landed on the
  second element ("ticket").
- Ran the same harness with --chrome: chord_focus_contract PASS against
  real Chrome too, independently confirming this is the correct target
  behavior and not just the audit's claim.

Fix mirrors the existing first-button path exactly: capture the chorded
mousedown's own cancellation result and run focusForMouseDown when it
wasn't cancelled, still gated by the gesture-level suppression flag so a
cancelled initiating pointerdown still suppresses every mousedown in the
chord, as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XLXnBHBxQNskg2MAke3Lhv
2026-09-17 07:55:59 +08:00