Commit Graph
7717 Commits
Author SHA1 Message Date
Karl Seguin 22fe4e993a Merge pull request #2849 from lightpanda-io/perf/selector-parse-cache
perf(css): cache parsed selectors per frame
2026-07-02 12:22:13 +08:00
Karl Seguin 7377a3a211 Merge pull request #2854 from lightpanda-io/remove-obey-robots-client
Stop storing `obey_robots` on HttpClient
2026-07-02 09:14:21 +08:00
Karl Seguin 5223d244a3 Merge pull request #2853 from lightpanda-io/BOM-parsing
webapi, fix: don't strip leading BOM for non-document parsing
2026-07-02 07:37:25 +08:00
Muki Kiboigo b3ee83f884 remove storing obey_robots on HttpClient 2026-07-01 08:12:53 -07:00
Karl Seguin bdb48ee02b webapi, fix: don't strip leading BOM for non-document parsing
On for the main document parsing should a leading BOM be stripped. When setting
innerHTML, it should be preserved (and becomes a text node).

Fixes react hydration issue with theverge.com
2026-07-01 21:15:00 +08:00
Adrià Arrufat 6f7bde9939 perf(css): tidy uncached-variants doc comment 2026-07-01 14:05:10 +02:00
Adrià Arrufat c04d316f8a perf(css): move selector cache to Browser with bounded eviction
Per review feedback: public querySelector doesn't guarantee reuse, so the cache
must be bounded regardless of the SelectorPath bypass. Move it off the Frame
(where it was wiped every navigation and unbounded) onto the Browser, since a
parsed selector references no Frame/Context — entries are now shared across the
browser's pages and survive navigation.

Selector.Cache is a StringArrayHashMap with per-entry arenas (so eviction can
free an individual entry, which a shared arena can't) and FIFO eviction of the
oldest entry past a capacity. The SelectorPath *Uncached bypass stays.
2026-07-01 13:44:48 +02:00
Adrià Arrufat b90d0fb54d Merge pull request #2835 from lightpanda-io/agent-async-goto-parallel-navigation
script-runtime: support concurrent page navigations
0.3.4
2026-07-01 11:19:35 +02:00
Pierre Tachoire 0f5d47c0cc Merge pull request #2850 from staylor/feat/disable-core-dump-env
feat: add LIGHTPANDA_DISABLE_CORE_DUMP to suppress crash core dumps
2026-07-01 10:19:18 +02:00
Karl Seguin dfe1f91deb Merge pull request #2819 from lightpanda-io/telemetry_smaller_payload
Telemetry smaller payload
2026-07-01 15:27:35 +08:00
Pierre Tachoire b11eb35de7 add LIGHTPANDA_DISABLE_CORE_DUMP desc into README 2026-07-01 09:25:00 +02:00
Pierre Tachoire 2278616cb1 telemetry: always write headers on send messages 2026-07-01 09:16:17 +02:00
Adrià Arrufat 1ee8f8def7 perf(css): drop selector-cache cap; bypass cache for one-off selectors
Replace the arbitrary 1024-entry cap with an explicit split: the public
querySelector/querySelectorAll/matches/closest entry points cache (page scripts,
waitForSelector — selectors that recur), while SelectorPath's synthesized one-off
candidates use new *Uncached variants that parse into a transient arena. The
cache now only ever holds genuinely-reused selectors, so it needs no size bound.
2026-07-01 08:13:31 +02:00
Karl Seguin 09eb265703 Merge pull request #2843 from navidemad/fix-a10-stylesheet-deferred-flush
page: flush deferred transfers after a synchronous stylesheet fetch
2026-07-01 12:28:10 +08:00
Scott Taylor f7f149c9c0 feat: add LIGHTPANDA_DISABLE_CORE_DUMP to suppress crash core dumps
Lightpanda installs no SIGSEGV handler, so a segfault (or the abort() in
the panic path) falls through to the kernel and writes a core dump. When
many instances run under a shared core_pattern crash reporter -- e.g. a
containerized crawl fleet -- those cores become pure storage/alert noise,
and a browser core can capture the contents of arbitrary pages.

Crashes are already reported via telemetry, so this adds an opt-in
LIGHTPANDA_DISABLE_CORE_DUMP env var (mirroring LIGHTPANDA_DISABLE_TELEMETRY)
that zeroes the soft RLIMIT_CORE at startup. Default behavior is unchanged.
2026-06-30 21:58:05 -04:00
Karl Seguin a36874f688 Merge pull request #2848 from lightpanda-io/perf/skip-inline-style-alloc
perf(css): skip inline-style allocation for elements without inline styles
2026-07-01 08:21:21 +08:00
Karl Seguin a255a15350 zig fmt 2026-07-01 07:22:22 +08:00
Karl Seguin 96cfd2e84f perf: shrink header fields
mode/arch/os are all now short strings
2026-07-01 07:21:02 +08:00
Karl Seguin 091fb6be28 Tweak small telemetry
Remove sid. Include iid in every message. Booleans true/false => 1/0. Constant
string values => single letter. Example:

["8800df58-a5d5-4ca5-9a06-d7691a2a3780","H","fetch",0,"macos","aarch64","1.0.0-dev.7609+88b1bc671"]
["8800df58-a5d5-4ca5-9a06-d7691a2a3780","R"]
["8800df58-a5d5-4ca5-9a06-d7691a2a3780","N",1,"P"]

H => Header
R => Run
N => Navigate
B => Buffer Overflow / dropped
L => LLM

Navigate context are
P => Page
O => Open (popup)
I => Iframe
2026-07-01 06:33:31 +08:00
Karl Seguin 50eef611f4 telemetry: Add more aggressive batching
Telemetry worker will now wait up to 5 seconds, or for 16 events before
flushing. Shutdown still causes an immediate flush.
2026-07-01 06:33:31 +08:00
Karl Seguin 47cffb8819 telemetry: Optimize the payload size, add navigation type
First, this adds 1 small piece of data to the navigate event: whether the
navigate was a page, frame or popup.

It also adds a session id, but as far as I'm concerned, this isn't "new"
information, or any new tracking/insight into users. Between the iid and the
"run" event, a "session" was always trackable. By giving it an explicit value,
we can shrink the size of all other messes.

This change reduces the telemetry payload by ~70% (despite the extra nav field).
I'm hoping this might remove a reason some people would consider turning it off.

It hits a /v2/ endpoint. The changes:

1 - A header is the first message in a session and contains all of the static
    data, as well as a session id
2 - Every event is encoded as an array, [$SID, "event-type, params...]

```
{"sid":"92e98210a141f497","iid":"$UUID","mode":"fetch","os":"macos","arch":"aarch64","version":"$VERSION","proxy":false}
["92e98210a141f497","run"]
["92e98210a141f497","nav",false,"page"]
```

(the driver=cdp field was removed from nav, because it was always cdp).

Some notes for the server:

1 - The server can tell a header from an event based on the first character.
2 - The SID is 8 bytes, enough to be unique, but not globally unique. The
    iid + sid + time window is how a events for the same SID can be grouped.
3 - A valid event is always an array of 2+ items, index 0 = SID, index 1 = type

Although positional data isn't expressive, it's still extendable.

The 4 events:

run, no parameters (mostly just used to flush the header now)
["sid", "run"]

// nav, tls, page/popup/iframe
["sid","nav",true,"page"]

// bof, # of lost telemetry events
["sid","bof",42]

// llm, provider, model (nullable)
["sid","llm","anthropic","claude"]
2026-07-01 06:33:30 +08:00
Adrià Arrufat 7a2f4ff2da Merge branch 'main' into perf/skip-inline-style-alloc 2026-07-01 00:23:11 +02:00
Adrià Arrufat 05b6eaf82c perf(css): cache parsed selectors per frame
querySelector/querySelectorAll/matches/closest re-parsed the selector
string on every call. nodeDetails' SelectorPath fires dozens of these per
element while synthesizing a unique selector (often the same string twice
back-to-back), and page JS that queries in loops paid the same repeated parse.

Add a frame-lifetime cache (frame._selector_cache) keyed by the selector
string. The parsed AST borrows slices of its input, so on a miss the key is
duped into frame.arena and parsed against that owned copy; both share the
frame's lifetime and are bulk-freed on navigation. A 1024-entry cap bounds
growth from SelectorPath's one-off synthetic selectors, falling back to a
per-call arena parse beyond it. StyleManager rule parsing is left untouched
(parsed once per rebuild into its own arena).
2026-06-30 16:14:02 +02:00
Pierre Tachoire c843025380 Merge pull request #2836 from mvanhorn/fix/2733-fix-getcomputedstyle-resolves-inline-sty
fix: getComputedStyle resolves inline style= declarations
2026-06-30 15:30:40 +02:00
Adrià Arrufat 41cb1bc176 perf(css): skip inline-style allocation for elements without inline styles
The visibility predicate called getInlineStyleProperty -> getOrCreateStyle,
which always allocated a CSSStyleProperties + CSSStyleDeclaration and inserted
into frame._element_styles, even for elements with no style= attribute. Every
semantic-tree / interactiveElements walk checks visibility on every element, so
this was one wasted allocation per element per walk on the agent's hot path.

Only materialize the inline-style object when one already exists (JS-set styles)
or the element actually carries a style= attribute; otherwise return null
without allocating.
2026-06-30 15:29:43 +02:00
Pierre Tachoire afca6c4fec Merge pull request #2847 from lightpanda-io/test_ignore_http_error_write
minor: ignore test server error when writing internal server error
2026-06-30 15:07:10 +02:00
Pierre Tachoire fbc0176457 Merge pull request #2844 from lightpanda-io/inline_style_optimization
perf: Skip CSSStyleProperty creation if there is no style attribute
2026-06-30 15:02:46 +02:00
Pierre TachoireandAdrià Arrufat 056cd3261f Update src/browser/webapi/css/CSSStyleDeclaration.zig
Co-authored-by: Adrià Arrufat <1671644+arrufat@users.noreply.github.com>
2026-06-30 15:01:27 +02:00
Karl Seguin 87b93faf91 minor: ignore test server error when writing internal server error 2026-06-30 20:40:36 +08:00
Adrià Arrufat 1dd5f6548d agent: update script instructions for parallel pages
Updates the `script_skill` prompt to recommend and demonstrate parallel
page navigation using `Promise.all` and multiple `Page` instances,
rather than serial navigation on a single page.
2026-06-30 13:27:54 +02:00
Adrià Arrufat 3a01114745 CDP: evict only replaced page nodes on re-goto
Introduce `Registry.resetFrame` to selectively evict nodes owned by
the replaced page's frame. This prevents invalidating node IDs of
sibling pages during concurrent navigations.
2026-06-30 09:39:46 +02:00
Adrià Arrufat 8a2bf904c2 Merge branch 'main' into agent-async-goto-parallel-navigation 2026-06-30 09:29:15 +02:00
Pierre Tachoire 6f453e0949 Merge pull request #2846 from lightpanda-io/ci-read-pkg
ci: add read permission to release for docker
2026-06-30 08:53:45 +02:00
Pierre Tachoire 7aa8bac4ba ci: add read permission to release for docker 2026-06-30 08:31:15 +02:00
Karl Seguin 1f8076b8be Merge pull request #2834 from lightpanda-io/update-v8
Update v8
2026-06-30 13:58:58 +08:00
Karl Seguin d2f72dfd4b update v8 version 2026-06-30 13:39:13 +08:00
Karl Seguin 98157e18ab update zig-v8-fork dep 2026-06-30 13:36:02 +08:00
Karl Seguin fc4932508c Merge pull request #2845 from lightpanda-io/slience_test_log
minor: silence log in test that intentionally generates an error
2026-06-30 13:15:05 +08:00
Karl Seguin 459daecbea zig fmt 2026-06-30 13:05:51 +08:00
Karl Seguin c754c999a4 minor: silence log in test that intentionally generates an error 2026-06-30 13:04:24 +08:00
Karl Seguin 96e23f78e8 perf: Skip CSSStyleProperty creation if there is no style attribute
Was reviewing https://github.com/lightpanda-io/browser/pull/2836 and realized
the StyleManager's getInlineStyleProperty could be optimized  to avoid creating
the CSSStyleProperties in the case where there's no style attribute.
2026-06-30 12:50:50 +08:00
Navid EMAD 66fb6748bf page: flush deferred transfers after a synchronous stylesheet fetch
Frame.loadExternalStylesheet fetches an external <link rel=stylesheet>
synchronously, which registers a blocking request for the frame. While that
blocking request is active the DeferringLayer holds back the completion
callbacks of every other in-flight transfer for the frame, so they don't run
JS on the parser stack. The blocking-<script> path and the worker path flush
those deferred completions once their synchronous fetch returns, but the
external-stylesheet path did not. As a result a <script defer> that finished
loading during the stylesheet's blocking window stayed at complete == false:
the deferred-script queue never drained, so the deferred scripts never ran and
DOMContentLoaded / the load event / readyState -> "complete" never fired —
the document was stuck at readyState "loading" even though every request
completed with HTTP 200.

Flush the frame's deferring layer after the synchronous stylesheet fetch,
mirroring the other two synchronous-request call sites.

Closes #2842
2026-06-29 21:46:35 +02:00
Pierre Tachoire 0614795fc5 css: resolve computed inline styles via StyleManager
getComputedStyle now reads inline values from the element's parsed
el.style through StyleManager.inlineStyleValue instead of re-parsing the
style= attribute, so computed and inline values share one source of
truth. Move the !important cascade precedence to the shared parse path
(applyParsedDeclaration) so el.style resolves duplicate declarations
correctly too.
2026-06-29 14:37:54 +02:00
Karl Seguin fe3e53c34a Merge pull request #2839 from lightpanda-io/request_interception_redirect
cdp: follow redirects on request interception fulfillment
2026-06-29 16:16:30 +08:00
Karl Seguin 5a65fdbbec Merge pull request #2838 from lightpanda-io/document-cookie
webapi: Fix move cookie accessor from HTMLDocument to Document
2026-06-29 16:16:13 +08:00
Pierre Tachoire ac0a854830 Merge pull request #2840 from lightpanda-io/ci-release-again
ci: login on GH registry during release
2026-06-29 08:12:08 +00:00
Pierre Tachoire cf99ec2fbd Merge pull request #2826 from bebsworthy/fix/cdp-frameattached-double-params
cdp: Fix Page.frameAttached params double-wrapping that broke sub-frame interception
2026-06-29 07:50:25 +00:00
Pierre Tachoire 30eb23e0c5 ci: login on GH registry during release 2026-06-29 09:41:58 +02:00
Pierre Tachoire e396cbb90c ci: login on GH registry during release 2026-06-29 09:38:10 +02:00
Adrià ArrufatandKarl Seguin c6e4715032 script-runtime: use ArrayList
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-06-29 08:05:05 +02:00