- Add support for triple-quoted strings (''' and """) in the tokenizer
and quote stripper.
- Avoid allocations in `stripQuotes` by returning slices.
- Optimize schema lookups using O(1) enum-based canonical resolution.
- Skip env var substitution if "$LP_" is not present in the string.
- Use `SlashCommand.globalSchemas()` instead of passing schemas around.
- Use `browser_tools.Action` enums for tool matching in Agent and Verifier.
- Optimize string substitution and quote stripping with early exits.
Unifies PandaScript syntax by replacing uppercase keywords with slash
commands (e.g., `/goto`, `/click`, `/fill`). Refactors the `Command`
representation to use a generic tool call structure backed by schemas.
BREAKING CHANGE: Uppercase PandaScript keywords (GOTO, CLICK, TYPE, etc.)
are no longer supported. All scripts must use slash commands.
Update `Recorder.init` to accept a `std.fs.Dir` and relative path,
decoupling it from the current working directory.
Also add a persistent arena allocator to `Recorder` to reuse
allocations when scrubbing environment variables on each write.
Walk std.c.environ dynamically and duplicate names into a provided
allocator instead of caching pointers to std.os.environ, which can
be invalidated by setenv.
Renames `Command.zig` to `command.zig` and moves associated functions
like `parse`, `toToolCall`, and `canHeal` directly into the `Command`
union. This simplifies usage from `Command.Command` to just `Command`.
Introduce an `Llm` struct to bundle the provider and API key together.
Add `UserError` to identify errors that have already printed human-
readable messages, preventing duplicate logging on exit.
Removes an assertion that can break with custom element callbacks.
https://github.com/lightpanda-io/browser/pull/2429 does not solve this issue
since it isn't a result of when reactions are executed, but just that they
happen.
(I should note, that I'm not 100% sure the above statement is correct. It's
possible that our CE reactions are (in some cases at least) too micro. Maybe
some operations, like setInnerHtml should operate within a more atomic
frameworks, vs an CE-reaction per internal step. But that's a pretty big change)
This adds help documentation for the --json flag. This is the only thing that
must be kept from this commit.
It uses our testing.zig to streamline the json testing (instead of string
probing). It removes the JsonEnvelope in favor of an anonymous struct (though,
I'm fine with adding it back in if it's needed to resolve something ambiguous).
Finally, I removed the last unit test as, at that point, it's really just
testing Zig's JSON stringifier (could arguably make the same case for the other
two, but there's some logic there about how nulls/empty might be handled).
Trim down a lot of the comments. Inline remarks in some cases rather than a
large function header.
Removing the `errdefer headers.deinit();` is unfortunate but currently
necessary to avoid a potential double-free if the request gets far enough that
http_client frees it and still returns an error. This is a known issue that
needs to be fixed separately and that impacts multiple call-sites. My "fix"
introduces a possible (very small) memory leak versus a possible crash.
Fold QueryWriter into Writer behind an Opts.filter. Tree mode is unchanged
(filter=null); query mode walks the full subtree (including AX-ignored
nodes per the queryAXTree spec) and emits the flat-match shape. Shared
resolveRole helper handles label-promotion for both paths so the two
can't drift.
Drop the "objectId not yet supported" carve-out: queryAXTree now reuses
dom.getNode, which already resolves nodeId/backendNodeId/objectId.
Move the UA `matchesUaDisplayNoneRule` short-circuit from BEFORE the
author rule walk in `isElementHidden` to AFTER it, gated on
`display_priority == 0` (no author display rule matched). Per CSS
Cascade §6.1 any normal-origin author rule beats UA origin regardless
of specificity, so `.x { display: flex }` on `<div class="x" hidden>`
must report visible. The author rule machinery
(`addRawRule`/`isRelevant`/`checkRules` + `id_rules`/`class_rules`/
`tag_rules`/`other_rules`) already produces the right answer; only the
check order in `isElementHidden` was wrong.
Refs #2496
Closes the DOMParser gap left as a follow-up in the previous review-fix
commit. DOMParser.parseFromString built its target Document via the
frame's parser without touching `_parse_mode`, so `Build.created` →
`linkAddedCallback` → `loadExternalStylesheet` saw `_parse_mode ==
.document` and fetched/registered sheets on the LIVE frame document for
every stylesheet link in the parsed string.
Bracket both the text/html and XML branches with the same fragment
parse-mode `parseHtmlAsChildren` uses. The existing gate in
`loadExternalStylesheet` already short-circuits on .fragment, so no
change is needed there. Side benefits: parser-emitted scripts in
DOMParser content stop reaching `scriptAddedCallback` against the live
frame, default-script injection skips DOMParser content, and mutation
observers on the live document no longer fan out for parsed nodes —
all of which match what DOMParser should do per spec.
Regression test extended to cover the DOMParser path alongside the
existing innerHTML case.
Refs #2343
Addresses 8 findings from ultrareview on the external stylesheet feature:
* UAF on CDP teardown during syncRequest. `loadExternalStylesheet`
pumps the CDP socket inline, so a `Target.closeTarget` arriving
mid-fetch could drive `Session.removePage` and free the frame
while we still held `self`. Set `_script_manager.base.is_evaluating`
around the call — the same bracket every other syncRequest caller
uses, which is what `Session.removePage`'s reentrancy guard checks.
* Disconnect leak. `link.remove()` left the sheet on
`document.styleSheets` and in the cascade forever; the disconnect
walker had a `<style>` branch but no `<link>` mirror. Common SPA
theme-switch pattern (append new sheet, remove old) was broken.
Added the parallel `else if` branch.
* Fragment-parsed links. `Build.created` fires for parser-instantiated
elements before attachment, including innerHTML / outerHTML /
insertAdjacentHTML / Range.createContextualFragment / <template>
content. Without a guard those fetched against the live document
and registered phantom sheets even when the fragment was never
attached. Added `_parse_mode == .fragment` early-return mirroring
the existing `nodeIsReady` short-circuit. DOMParser is a separate
case (parses with `.document` into a different Document) and is
left as a known follow-up.
* Missing Referer. Every other resource-fetch path
(ScriptManagerBase, XHR, Fetch, WorkerGlobalScope) routes through
`Frame.headersForRequest` to attach the cached `Referer` header.
Many CDNs gate stylesheet delivery on Referer; without it requests
returned 403/302 and the CSS silently failed. Added the call.
* Header OOM leak. `headers.add` between `newHeaders()` and
`syncRequest` (which takes ownership) leaked the initial 3-entry
slist on OOM. Added `errdefer headers.deinit()` mirroring
RobotsLayer.zig:121-122.
* `_href` mutated before parse could fail. On parse error the cached
sheet was left with the new URL but old rules dropped — violated
the "previous sheet intact on failure" invariant the PR description
promises. Moved the `_href` assignment to after `replaceSync`
succeeds. Full atomicity would require a scratch-list pattern in
`CSSStyleSheet.replaceSync` itself; documented as a known limit.
* `_sheet` cached before registration could OOM. If `sheets.add`
failed, `link._sheet` pointed at an unregistered sheet and every
future re-fetch short-circuited via the `orelse` branch, leaving
the sheet permanently unreachable through `document.styleSheets`.
Assign `link._sheet` only after `sheets.add` succeeds.
* Stale CLI help text claimed `--enable-external-stylesheets` was a
no-op surface. Removed the obsolete sentence.
New regression tests cover fragment-parse skip and disconnect
removal+re-add. Full suite 694/694 pass.
Refs #2343
Caught in code review: `loadExternalStylesheet` created a fresh
`CSSStyleSheet` and appended to `document.styleSheets` on every call, so
mutating `link.href` on a connected stylesheet element accumulated stale
sheets — the old rules kept cascading because the previous sheet was
never removed.
Cache the sheet on `Link._sheet` (mirroring `Style._sheet`) and reuse it
via `replaceSync` on re-fetch. First load creates + registers as before;
subsequent loads swap content in place, keeping `document.styleSheets`
length stable.
On fetch failure the cached sheet is untouched — matches browser
behavior where a broken href doesn't invalidate the previously loaded
sheet until the link itself is removed.
Refs #2343
Wires up --enable-external-stylesheets / LP.configureLoading.externalStylesheets
from the prior surface-only commit. When the flag is set, parser- and
JS-created <link rel=stylesheet> elements now synchronously fetch and parse
their href, register a CSSStyleSheet on document.styleSheets, and feed
StyleManager so checkVisibility() reflects external rules. Flag stays
default-off — scrapers that don't need accurate visibility pay nothing.
Frame.loadExternalStylesheet mirrors ScriptManager.addFromElement: same
HttpClient.syncRequest path, same arena ownership, same per-frame
notification + cookie wiring. Body is routed through CSSStyleSheet.replaceSync,
which already parses, populates cssRules, and calls sheetModified() — no
StyleManager changes needed. 2 MiB hard cap on a single sheet body, status
non-2xx and oversize both fire `error` on the link.
Link.Build.created is added so static head <link> elements reach
linkAddedCallback at all — void elements never trigger nodeComplete, which
is why static `<link>` had no observable effect before. Mirrors Image.
HttpClient.Request.ResourceType gains a `.stylesheet` variant so CDP Network
events report the right type; cdp.fetch.zig switches updated.
Refs #2343
Reserves the CLI flag and LP.configureLoading externalStylesheets field
so drivers can adopt the API before the fetch implementation lands in a
follow-up that depends on #2303.
The bool is intentionally unread in this PR. Mirrors the existing
--disable-subframes / --disable-workers plumbing; the CDP field extends
LP.configureLoading alongside subFrame and worker without breaking
existing callers.
Refs #2343