8318 Commits

Author SHA1 Message Date
Karl Seguin
f72cba80a8 Merge pull request #3037 from Ppsoft1991/fix/cdp-network-events
cdp: complete Network events and include worker requests
0.3.6
2026-07-25 12:32:18 +08:00
Karl Seguin
5acfd38250 Improve CDP response event data
Give accurate connectionId, connectionReused, initialPriority and securityState
values.

Always set `referrerPolicy` to `unsafe-url` as the most honest answer (we should
implement proper referrer policy!).

For workers, track the underlying frame_id so that it can be used for the
`documentURL` field.
2026-07-25 11:36:13 +08:00
Karl Seguin
063906ed23 Merge pull request #3036 from lightpanda-io/xhr-xmlresponse
webapi: XHR uses XML parser for XML content-type responses
2026-07-25 10:50:38 +08:00
Karl Seguin
c917cadcd0 Merge pull request #3054 from navidemad/fix-a51-form-method-dialog
forms: close the ancestor dialog on method=dialog submission
2026-07-25 10:18:55 +08:00
Karl Seguin
705bc78b72 cleanup: Mechanical move various frame.parse* method
Follows the observer, user_input, etc.. pattern and relocates various
`Frame.parse*` methods into Frame.parse.* functions.

Goal is just to keep Frame neat.
2026-07-25 10:03:57 +08:00
Karl Seguin
dab5ff7779 webapi: XHR uses XML parser for XML content-type responses
Expand mime type to be aware of more XML types. DOMParser and XHR now use the
existing Parser.parseXML (via a Frame helper), rather than the HTML parser. Both
these APIs predate the addition of parseXML.
2026-07-25 10:01:53 +08:00
Karl Seguin
e8b58c5dd2 Merge pull request #3055 from lightpanda-io/fix-formdata-append-blob
Fix formdata append blob
2026-07-25 09:52:03 +08:00
Karl Seguin
c205e118eb Get submitter value from the IDL value
Move code that dialog block doesn't need under the dialog block (e.g. no need
to get an arena from the pool).
2026-07-25 09:50:54 +08:00
Karl Seguin
fda6cfcaf6 Merge pull request #3048 from lightpanda-io/scroll-with-content
derive scrollWidth/scrollHeight from element content
2026-07-25 09:34:08 +08:00
Karl Seguin
0e0d4418c3 Simplify FormData value union
Collapse File -> Blob, since a File is a blob. Code was already checking for
blob._type == .file.
2026-07-25 09:27:06 +08:00
Karl Seguin
d58cbc5440 explicit visibility check on root 2026-07-25 08:56:39 +08:00
Karl Seguin
e742d363c0 Merge pull request #3049 from navidemad/makefile-v8-cache-key-tag
make: key the prebuilt-V8 cache on the zig-v8 tag
2026-07-25 08:39:22 +08:00
Ppsoft1991
e3ec937f78 cdp: emit worker network requests
Keep worker requests visible on the page CDP session when their WorkerGlobalScope frame id is absent from the document frame tree. Add a regression fixture covering the worker script and a fetch from inside the worker.
2026-07-25 08:01:04 +08:00
Ppsoft1991
39981f2534 cdp: complete Network event payloads
Add required timestamp, type, request, response, and timing fields so strict CDP clients can deserialize emitted events.
2026-07-25 07:37:53 +08:00
Navid EMAD
77bbbf5e84 forms: close the ancestor dialog on method=dialog submission
`Form.normalizeMethod` already canonicalizes `method="dialog"`, but
`Frame.submitForm` only branched on `"post"`, so a dialog submission fell
through to the GET path: the document was reloaded, the dialog kept its
`open` attribute, `returnValue` was never set, and no `close` event fired.
A promise awaiting `close` — the idiomatic way to await a confirm dialog —
hung forever while the page reloaded underneath it.

Add the branch the HTML form-submission algorithm calls for: walk up from
the form to the nearest ancestor `<dialog>` and close it with the
submitter's value through the existing `Dialog.close()`, then return
without scheduling a navigation. With no ancestor dialog the submission is
dropped, which is also what the spec asks for — and still not a navigation.

Completes the `<dialog>` support started in #2435, which implemented
`show`/`showModal`/`close`/`returnValue` but left the form-submission
integration that drives them unwired.

Closes #3053
2026-07-24 21:23:52 +02:00
Navid EMAD
37aef53f5e net: accept Blob and File values in FormData.append/set
The JS-facing append() and set() took `value: []const u8`, so js.Bridge
coerced any object argument through toString(). Passing a Blob or File stored
the literal text "[object Blob]" / "[object File]" and the bytes never reached
the wire: a FormData carrying a 200 KB File produced a 209-byte request body,
with no filename parameter and no per-part Content-Type. The request still
succeeded with a 200, so nothing surfaced the loss.

Only the binding was string-typed. Entry.Value already carries a `file: *File`
variant with refcounting, and the multipart writer already emits filename= and
a per-part Content-Type for it — that is how <input type=file> submissions
work today.

Take a tagged union instead, following the shape body_init.zig already uses
for BodyInit (`bytes` last, so js.Bridge only falls back to a string once the
JsApi classes have been tried), and implement the entry-creation steps from
https://xhr.spec.whatwg.org/#create-an-entry: a Blob that is not a File
becomes a File named "blob", and an explicit filename produces a new File over
the same bytes rather than renaming the caller's object.

The internal append(name, value) helper keeps its string signature — it backs
form collection and the existing unit tests, neither of which goes through JS.

Closes #3050
2026-07-24 20:38:19 +02:00
Navid EMAD
862fc91f06 make: key the prebuilt-V8 cache on the zig-v8 tag
A zig-v8-fork release names its assets after the V8 version only, so the
same filename is reused across fork tags:

  v0.5.0  libc_v8_14.9.207.35_macos_aarch64.a  106944896 bytes
  v0.5.1  libc_v8_14.9.207.35_macos_aarch64.a  106945416 bytes
  v0.5.2  libc_v8_14.9.207.35_macos_aarch64.a  106945984 bytes

V8_CACHE was keyed on that filename alone, so a zig-v8 tag bump that
leaves V8_VERSION unchanged did not invalidate the cache: download-v8's
`test -f` guard saw the old archive and skipped the refresh. The build
then linked yesterday's bindings and failed on undefined symbols for
whatever the new tag added, e.g. after the v0.5.2 bump:

  error: undefined symbol: _v8__Value__IsFloat16Array
  error: undefined symbol: _v8__V8__SetFlagsFromString
  error: undefined symbol: _v8__Isolate__AddNearHeapLimitCallback

Putting the tag in the cache path gives each tag its own entry, so a bump
downloads and a repeat run still hits the cache. The URL keeps using the
bare asset name, which is what the release actually publishes.

Only affects local development; CI installs V8 through the install action
and never calls download-v8.
2026-07-24 19:29:07 +02:00
Karl Seguin
044d5c9186 Merge pull request #3047 from lightpanda-io/parse-terminate-guard
stability: Poll terminate state during large HTML parsing
2026-07-24 21:36:40 +08:00
Karl Seguin
3364de74af Merge pull request #3045 from lightpanda-io/notificaiton-reregister-safe
cdp: ignore duplicate enable calls
2026-07-24 19:24:32 +08:00
Pierre Tachoire
51c9115e9b derive scrollWidth/scrollHeight from element content
Both aliased clientWidth/clientHeight, computed from the element's own box
without looking at its children, so any "measure, mutate, re-measure" loop
never terminated. The marquee idiom on readthetrieb.com is the reported case:

    while (lane.scrollWidth < track.offsetWidth * 2)
        lane.innerHTML += lane.innerHTML;

Doubling the markup left scrollWidth unchanged, so the loop spun while
innerHTML grew the DOM exponentially and wedged the page in under a second.

Return max(clientSize, contentSize), summing the direct child elements.

- No layout-mode detection: getStyle() sees only the inline style attribute,
  so display:flex or white-space:nowrap from a stylesheet is invisible. Each
  axis assumes the arrangement that produces overflow — width lays children in
  a row, height stacks them — bounding content extent per axis rather than
  modelling one layout.
- Text children are not measured. Estimating a run from its length needs a
  per-character advance that tracks font-size, or shrink-to-fit loops stop
  converging, and it reports overflow for nearly every element holding text.
- Direct children only, so cost is O(fan-out), with a shared VisibilityCache
  collapsing N ancestor walks into one.
- <html>/<body> keep their synthetic defaults, so page-level overflow and
  infinite-scroll checks are unaffected; only inner containers change.

Tests in element/position.html cover growth per child, text contributing to
neither axis, hidden elements, the root carve-out, and both loops terminating.
2026-07-24 12:23:22 +02:00
Karl Seguin
0066cad4d9 Merge pull request #3041 from lightpanda-io/runner-tick-preload
perf, fix: Avoid tight tick loop before page loads
2026-07-24 17:28:16 +08:00
Karl Seguin
553a3bad08 Merge pull request #3046 from lightpanda-io/resize-observer-perf
perf: improve ResizeObserver performance
2026-07-24 17:28:00 +08:00
Karl Seguin
5a59743f13 stability: Poll terminate state during large HTML parsing
V8's terminate isn't pre-emptive. We can arm it, but it still hast to cross
a boundary to fire. JavaScript that triggers a near endless Zig loop won't be
able to interrupt the Zig code:

```
while (true) {
  el.innerHTML += el.innerHTML;
}
```

Can generate a huge HTML input that the Parser will work on for ages. I don't
have a general solution to this. I've made the Parser (and DOMParser) check the
terminate state every 1024 appends. This only covers one specific case, but it's
possibly common enough to guard again, specifically because we don't have
rendering/dimensions, and many websites will append text until certain
dimensions are reached.

But it wouldn't for example, catch a similar:

while (true) {
  el.appendChild(el.firstChild.cloneNode(true));
}
2026-07-24 17:17:33 +08:00
Karl Seguin
4565501aa8 Merge pull request #3034 from mh0pe/codex/svg-02-values
webapi: add live SVG scalar values
2026-07-24 14:13:41 +08:00
Karl Seguin
d75747fd20 Merge pull request #3035 from lightpanda-io/Float16Array
v8: Expose Float16Array
2026-07-24 11:54:38 +08:00
Karl Seguin
e2f80ace33 update v8 dep 2026-07-24 11:34:45 +08:00
Karl Seguin
122a4f1001 perf: improve ResizeObserver performance
https://github.com/lightpanda-io/browser/pull/3000 improved the correctness of
ResizeObserver. The main changes were (a) making sure an observe results in
an initial callback and (b) invoking the callback for cases that we can
identity (e.g. visibility change).

Like the other observers, we triggered a check on domChanged. But, unlike the
other observers, the check is relatively expensive, namely because it involves
style lookups.

This commit introduces a number of performance improvements to reduce the check
and dispatch frequency.

1 - Only trigger on an allow list of attribute changed (id, class, hidden, width
 ...)
2 - Pre-filter only on observed elements
3 - Leverage the visibility cache for more efficient delivery
2026-07-24 11:23:00 +08:00
Karl Seguin
ed9a25f470 fix test 2026-07-24 10:18:59 +08:00
Karl Seguin
a6af331ab2 zig fmt 2026-07-24 08:25:21 +08:00
Karl Seguin
2b945aa1f9 cdp: ignore duplicate enable calls
CDP driver can send multiple Network.enable which would register the same
listener multiple times. This commit makes it so that only one (the first)
callback registered for a listener+eventtype is used. Subsequent registration
for the same listener+eventtype are ignored. This is safe because all callbacks
are currently static. It's a mistake (enforced by a debug-only assertion) for
code to try to register a different callback for an already registered listener+
eventtype.

This generalizes https://github.com/lightpanda-io/browser/pull/3038
2026-07-24 08:20:32 +08:00
Karl Seguin
de85a51dda Merge pull request #3039 from lightpanda-io/fix-help-pager
cli: fix help pager broken by lp.io's failing allocator
2026-07-24 08:10:45 +08:00
Karl Seguin
8fbcc7e5c1 Merge pull request #3015 from lightpanda-io/cdp-utf8-sanitization
cdp: Sanitize non-UTF 8 values
2026-07-24 08:03:38 +08:00
Karl Seguin
76e2e7e619 Merge pull request #2983 from lightpanda-io/layer-ordering
css: Respect @layer priority
2026-07-24 08:03:03 +08:00
Karl Seguin
05957a2eb9 Merge pull request #3044 from lightpanda-io/fix-report-error-crash
Fix Seg Fault on `window.reportError`
2026-07-24 08:01:53 +08:00
Muki Kiboigo
fccd6bf90b acquire ref on event before dispatchDirect on reportError 2026-07-23 12:57:59 -07:00
Madison Steiner
c40a2ccac6 webapi: add live SVG scalar values 2026-07-23 08:18:43 -07:00
Karl Seguin
40894a6bfd Merge pull request #3008 from lightpanda-io/zig-0.16-flake
Update Nix flake for Zig 0.16
2026-07-23 21:35:41 +08:00
Muki Kiboigo
791af8fded update Nix flake for Zig 0.16 2026-07-23 05:54:13 -07:00
Karl Seguin
542d9a333b perf, fix: Avoid tight tick loop before page loads
https://github.com/lightpanda-io/browser/pull/2999 fixed a spin loop when the
page had no i/o but had tasks, something we'd expect to see at page-load end.
But it introduced a similar spin loop as what it fixed on page-start, before the
page has anything to do.

Specifically, 2999 preventing i/o pollings when tasks were waiting. But, the
code doesn't run tasks until there's a "runnable" task. On Frame start, we
register a background task. Net result is that, for the first 200ms, everything
is fine, but then the task is due, so we don't poll, but we also don't run the
tasks, repeating a tick(0) loop.

The solution is simply to disable the 2999 optimization when there's no
runnable page.
2026-07-23 20:10:40 +08:00
Pierre Tachoire
3c925f740c Merge pull request #3040 from lightpanda-io/fix/lightpanda-request-blocking
Fix/lightpanda request blocking
2026-07-23 12:39:17 +02:00
Adrià Arrufat
4840a96645 cli: fix help pager broken by lp.io's failing allocator
std.Io.Threaded.init_single_threaded sets .allocator = .failing, which
spawnPosix uses to build the child's argv/env, so
std.process.spawn(lp.io, ...) always returns OutOfMemory and printPaged
silently fell back to plain output. Spawn the pager through a local
Threaded instance with a real allocator and the real environ (the
single-threaded one is empty, breaking PATH lookup of the less
fallback).
2026-07-23 12:16:19 +02:00
Pierre Tachoire
303a3666b6 don't count blocked redirects as followed
Move the http_redirects metric increment after the URL block check so a
redirect that gets blocked is recorded as an error, not a redirect.
2026-07-23 12:15:50 +02:00
Pierre Tachoire
43d17a78f2 skip empty URL block patterns consistently
Centralize empty-pattern filtering in UrlBlocklist.init/initPatterns so
the CDP setBlockedURLs path matches the CLI --block-urls behavior. Keep
blocks aligned with the filtered patterns.
2026-07-23 12:15:49 +02:00
Matt Van Horn
dee40075a0 feat(cdp): implement Network.setBlockedURLs via urlPatterns
Reimplement request blocking on the non-deprecated urlPatterns shape:
each pattern carries an explicit block/allow flag (first match wins),
UrlBlocklist owns the compiled patterns plus their block flags, and the
legacy setBlockedUrls path stays for back-compat. Tests updated.
2026-07-23 12:15:49 +02:00
Matt Van Horn
311ab3aaae feat(cdp): add Network.setBlockedURLs and --block-urls request blocking 2026-07-23 12:15:47 +02:00
Pierre Tachoire
1d3e9617c4 Merge pull request #2891 from lightpanda-io/nikneym/custom-cert-load
Support loading custom CAs (`--ca-cert`, `--ca-path`)
2026-07-23 11:33:30 +02:00
Pierre Tachoire
b90d9def84 remove dead condition 2026-07-23 11:12:50 +02:00
Pierre Tachoire
45c8f7499c adjust help for --ca-* options 2026-07-23 10:33:41 +02:00
Karl Seguin
a50c38b672 Merge pull request #3016 from Ppsoft1991/fix/cdp-frame-parent
cdp: expose child frame metadata
2026-07-23 12:43:55 +08:00
Karl Seguin
6374dab634 refactor: Use JSON writer to serialize CDP Frame
This has the benefit of avoiding a few arena dupes, but those were small. The
real benefit is consistency with the rest of the code.
2026-07-23 12:22:24 +08:00