9444 Commits
Author SHA1 Message Date
Pierre Tachoire b3c327970f Merge pull request #3335 from lightpanda-io/nikneym/lax-exception-RFC6265bis
Apply `SameSite=Lax` exception only on safe methods
2026-09-14 18:09:01 +02:00
Adrià Arrufat 7f451a5c34 Merge pull request #3513 from lightpanda-io/docker-orderfile
mem: apply the orderfile to the Docker image build
2026-09-14 17:33:31 +02:00
Adrià Arrufat 21afa0948e mem: apply the orderfile to the Docker image build
The Linux release artifacts and the e2e bench build link with
-Dorderfile=orderfile/lightpanda.ld, but the Dockerfile runs its own
zig build without it, so the published image shipped an unordered binary.

CDP bench (demo/puppeteer/cdp.js, RUNS=100), release builds with and
without the flag, 3 alternating reps each:

  tmpfs: VmHWM 28.8MB -> 24.2MB
  ext4:  VmHWM 38.6MB -> 30.1MB

RssAnon is unchanged (~3.0MB) and avg run duration is within noise, as
expected from a link-order-only change. Cost is ~2s of link time in the
builder stage.
2026-09-14 13:34:23 +02:00
Halil Durak 93381010f1 Merge branch 'main' into nikneym/lax-exception-RFC6265bis 2026-09-14 14:27:55 +03:00
Karl Seguin 72a05c7efa Merge pull request #3510 from lightpanda-io/node-document
internal: store document in every node
2026-09-14 17:41:55 +08:00
Karl Seguin fbbfae2d29 assert node has a non-null document 2026-09-14 17:23:13 +08:00
Karl Seguin eb82e71403 Merge pull request #3511 from lightpanda-io/parser-chunked-microtasks
internal: run microtasks during parsing
2026-09-14 16:05:10 +08:00
Karl Seguin fa2e21cb57 Minor cleanup
?*Element -> *Element to getAttribute, getOrCreateAttribute, and toAttribute
which never need the option.

Assert that document._page == page.

Text use asCData() instead of constantly calling Factory.protoOf.
2026-09-14 14:53:16 +08:00
Karl Seguin 5068f6c811 Merge pull request #3509 from lightpanda-io/css-class-name
webapi: (minor) CSS WebApi should be called "CSS" not "Css"
2026-09-14 14:35:37 +08:00
Karl Seguin 2927799cd3 Merge pull request #3508 from lightpanda-io/improve-js-memory-metric
ops: improve v8 memory metric reporting
2026-09-14 14:35:12 +08:00
Karl Seguin 7382efe688 Merge pull request #3501 from lightpanda-io/frameless-documents
webapi: better handling of frameless documents
2026-09-14 14:35:00 +08:00
Karl Seguin 729051b279 internal: run microtasks during parsing
Runs microtasks during parsing, rather than waiting for parsing to complete.

We see some sites that setup MutationObserver on the root, early in a document's
code. These then get thousands (4K-10K) of MutationRecords for every added node.
We deliver these as a single batch at the end of parsing. Chrome delivers it
based on some elapsed time calculation (I think).

Both are correct, as far as I can tell. And, I don't really expect this to
change anything. But, because the batch size is fixed:

1 - the inflight metric should be flatter
2 - there could be some small reduction in retained memory (e.g. MO's
    `_pending_records` might not grow so much)
3 - `call_arena` doesn't need to dupe such a large array

Related to (1), when we do special builds that log arena usage and eliminating /
reducing this known OK behavior helps remove some noise (Or, put it this way:
I spent some time debugging this, it's more or less nothing, but it still
looks like something that needs fixing, this commit reduces that noise).
2026-09-14 13:38:05 +08:00
Karl Seguin ef18e0e1cd internal: store document in every node
Follow up to https://github.com/lightpanda-io/browser/pull/3501.

To recap: many apis use the injected-frame, when, for cross-frame operations,
can be wrong. We've been moving to using the Node's Document's Frame on a case-
by-case basis. The goal with this series of PRs is to more holistically fix it.

The main goal of this commit is to make Node.getDocument fast. Previously,
Node.nodeDocument (renamed to Node.getDocument) and Node.ownerFrame had to walk
up the _parent tree to find the document. Now there's a DocumentRegistry on
the Browser, and every Node has a `_document: u32` index. Thus, Node -> Document
is O(1). This makes it so we can have correctness without a performance cost.

The DocumentRegistry is held on the Browser because of shared CDP nodes. A
follow up should be able to move this to the Page and change the index to u16.

Adding a DocumentRegistry to Browser, and a document: u32 to Document is easy.
The reason this PR is so big is because every `node_factory` and many Factory
methods need to be aware of all of this. You can't create an HTMLDivElement
without the document it belongs to. I incorporated more expected future changes
into the node_factory/factory mechanism so that  [hopefully] the next PRs
don't have to touch any of this code (e.g. I removed Frame as a parameter and
added a *Page to every Document so that documents have access to factory/arena/
etc..because a `*Document` might not have a `*Frame`, but it will always have a
`*Page`.

Give every document an index (u32), and store that index in Node.
2026-09-14 12:05:52 +08:00
Karl Seguin 5163c6d385 Merge pull request #3506 from lightpanda-io/silence-test-logs
chore: silence expected error log in MO test
2026-09-14 08:57:19 +08:00
Karl Seguin ea1ce09753 webapi: (minor) CSS WebApi should be called "CSS" not "Css" 2026-09-14 08:56:24 +08:00
Karl Seguin 19b165d099 Merge pull request #3502 from lightpanda-io/selected-options
chore: cleanup selected / selected options
2026-09-14 08:19:34 +08:00
Karl Seguin 3adacb6900 Merge pull request #3505 from staylor/fix/window-tostringtag
Fix Symbol.toStringTag inheritance for globals and elements
2026-09-14 08:19:09 +08:00
Karl Seguin f2abadbb0e Merge pull request #3484 from lightpanda-io/xhr_cors
XHR changes for CORS WPT tests
2026-09-14 08:18:44 +08:00
Karl Seguin dcd5c796ed ops: improve v8 memory metric reporting
Report the v8 memory every 1 second during tick. The previous model only
reported at four points and would fail to correct capture memory growth between
those points.
2026-09-14 08:15:40 +08:00
Karl Seguin 27b16af770 zig fmt *eyeroll* 2026-09-14 08:05:12 +08:00
Karl Seguin 4316454979 Merge pull request #3504 from staylor/fix/link-reentrant-send
Fix CDP message lifetimes and console argument serialization
2026-09-14 08:01:39 +08:00
Karl Seguin adb2b21ccf chore: silence expected error log in MO test 2026-09-14 08:01:00 +08:00
Muki Kiboigo 22e095370b add unit test for XMLHttpRequest parseMethod 2026-09-14 07:58:57 +08:00
Muki Kiboigo 00c98313c2 use curl no body option for head requests 2026-09-14 07:58:56 +08:00
Muki Kiboigo b3655f5918 shortcut default/text getResponse instead of caching path 2026-09-14 07:58:07 +08:00
Muki Kiboigo 8bbe1907b0 parseMethod for HEAD requests in XHR 2026-09-14 07:58:07 +08:00
Muki Kiboigo 3a7b3f9e1f XHR response can return before done on text/default 2026-09-14 07:58:07 +08:00
Karl Seguin 47eb1b8581 make acquireArena names more consistent with each other 2026-09-14 07:44:21 +08:00
Scott Taylor f8183e2168 js: inherit Symbol.toStringTag from interface prototypes
Web IDL defines Symbol.toStringTag on interface prototype objects, not
instances. Setting it on instance templates left globals and internal
custom/generic element subclasses without a tag, so they reported
[object Object]. Interface prototypes also reported the wrong class string.

Deep-clone helpers that recurse into plain objects consequently traverse
host objects and their cyclic references. Locally, a storefront theme's
store cloner classified custom/generic elements as plain objects and
exhausted the JS heap, terminating the CDP connection.

Set the tag on the member template: interface prototypes for inheritance,
while namespace objects keep their existing own-property behavior.
Test class strings, property descriptors, namespace ownership, and bounded
deep cloning that preserves host-object identity.
2026-09-13 19:27:18 -04:00
Scott Taylor c284fce11a cdp: preserve message storage across reentrant notifications
Runtime.consoleAPICalled serialized object arguments with JSON.stringify,
which can run getters and toJSON callbacks. A callback can log again or
navigate, emitting further CDP events while the outer event is being built.
Resetting send_arena after each send and notification_arena after each
handler then invalidates the outer message's buffers. This produces
use-after-free in debug builds or malformed JSON that disconnects clients.

Scope both arenas to the outermost send or notification handler, including
inspector messages and error paths. Also match Chrome's console argument
representation: objects remain remote handles, primitives carry value,
and non-JSON numbers and bigints use unserializableValue. Console logging
must not invoke object getters or toJSON as a serialization side effect.

Test complete nested WebSocket messages, failure recovery, nested legacy
Console notifications, and primitive/object protocol shapes.
2026-09-13 19:27:18 -04:00
Karl Seguin 7a60c6b947 Merge pull request #3503 from lightpanda-io/curl-pipewait
Enable CURLOPT_PIPEWAIT on every easy handle
2026-09-14 06:30:43 +08:00
Adrià Arrufat db9779a654 Enable CURLOPT_PIPEWAIT on every easy handle
Requests issued to an origin while its first connection is still
handshaking each opened their own socket, up to --http-max-host-open,
because curl only learns from ALPN whether the origin multiplexes. With
pipewait they wait for that answer and share one h2 connection.

Fixture: 12 fetch() calls to a fresh cdnjs (h2) origin, release build.

  new TCP+TLS connections   6 -> 1
  in-page time to last resp ~285 ms -> ~105-135 ms

H1-only origins are unchanged in connection count; their first burst
waits one handshake before fanning out.
2026-09-13 22:09:36 +02:00
Karl Seguin 0d9e5441f5 chore: cleanup selected / selected options
There's been recent work on improve select / options:
- https://github.com/lightpanda-io/browser/pull/3375
- https://github.com/lightpanda-io/browser/pull/3402
- https://github.com/lightpanda-io/browser/pull/3499

One of the main issues is that a select's options "selected" state wasn't always
kept in sync. Select.zig had a boolean flag to mark whether or not a
selectedIndex was explicit set and every read and update would need to dance
around it. Removing the selectedIndex option would not, for example, keep things
in sync.

This removes the flag and keeps the Option._selected in sync, i.e. the sync
happens on write, not on read and is thus naturally recorded in the state of
the Select and its Options.

The write path is more complicated, but the read path is simpler (though the
real win is always being correct).
2026-09-13 14:13:33 +08:00
Karl Seguin ab69f4c746 Merge pull request #3499 from staylor/fix/select-value-no-match
Fix select value assignment with no matching option
2026-09-13 14:06:46 +08:00
Scott Taylor 6d454529a5 Fix select value assignment with no matching option
An unmatched select.value assignment must leave every option unselected,
with value == "" and selectedIndex == -1. Reapplying the first-option
fallback on reads instead can keep change-driven select mirroring loops
from converging.

Preserve an explicitly cleared selection until the option list changes.
Restore the single-select default after insertion, removal, moves, and
fragment parsing, without inventing a selection for multiple selects or
listboxes. Select only the first matching option for value assignments.

Cover empty values, duplicate values, grouped options, structural resets,
and bounded select mirroring; compare the new assertions with Chromium.
2026-09-12 23:58:50 -04:00
Karl Seguin 436a666937 Merge pull request #3498 from staylor/fix/trycatch-stale-exception-copy
Regression test: exception relocated by GC while its TryCatch is read
2026-09-13 10:59:50 +08:00
Scott Taylor 02c4ef8777 test: exception object relocated by GC while its TryCatch is read
Regression page for eff118eb2 (don't copy TryCatch by value): a
MutationObserver callback throws an object whose `message` getter
allocates enough to force several scavenges while `TryCatch.caught` is
between `Exception()` and `StackTrace()`. On a copy of the TryCatch the
relocated exception's old address was read back; with a debug V8 this
failed 7/7 runs before that fix (unknown instance type, or a fault inside
the pointer-compression cage).
2026-09-12 22:32:21 -04:00
Karl Seguin d1ea326b44 webapi: better handling of frameless documents
We've auto-injected `*Frame` into WebApi since forever (used to be called *Page,
but then we split *Page / *Frame, but same same). And it worked wonderfully:
there was always a single *Frame, so the Frame/Context that the JS was being
executed in HAD to be the *Frame that a node belonged to.

But with the addition of iframe and popups, that truth no longer holds. The
*Frame executing the JS (which is the frame that we auto-inject) isn't
necessarily the *Frame that owns a Node.

This is particularly problematic because the *Frame holds a bunch of node/
element data, e.g. `_element_datasets`. So now the DataSet that you get back
depends on the context in which its called..they don't have identity and can
fall out of sync. Some code calls node.ownerFrame() / node.ownerDocument(), but
not all and the hope is to address this throughout the codebase once and for
all.

This is the first in a series of commits meant to fix this long-standing
issue. All it does is change the node.ownerFrame() return value from *Frame to
?*Frame. It's up to each caller to decide how to handle a frameless node, e.g.
clicking a frameless link should not navigate.
2026-09-13 10:31:30 +08:00
Karl Seguin 799dc3e06d Merge pull request #3479 from lightpanda-io/wheel-scrolls-viewport
cdp: wheel events scroll the viewport unless over a scroll container
2026-09-13 09:06:29 +08:00
Karl Seguin cc64255b35 Merge pull request #3497 from lightpanda-io/try-catch-copy
crash: don't copy TryCatch by value
2026-09-13 09:06:06 +08:00
Karl Seguin 9b61b498a9 make css property compare case-insensitive 2026-09-13 08:41:31 +08:00
Karl Seguin ee735143b2 Merge pull request #3474 from lightpanda-io/prune-hidden-walks
Probe own visibility inside walks that prune hidden subtrees
2026-09-13 08:30:26 +08:00
Karl Seguin 128233c4ff Merge pull request #3494 from lightpanda-io/robot-store-leak
mem: fix robot store leak
2026-09-13 08:29:51 +08:00
Karl Seguin eff118eb2e crash: don't copy TryCatch by value
The v8.Handle is initialized / registered with v8 at that address and can't
be copied.
2026-09-13 08:23:57 +08:00
Adrià Arrufat 91cdb52829 cdp: prune accessibility tree children when node is hidden
💘 Generated with Crush

Assisted-by: Crush:gemini-3.8-flash
2026-09-12 14:28:22 +02:00
Adrià Arrufat 55dcad50d1 cdp: wheel scrolls per axis and lets scrollBy schedule the scroll events
Each wheel axis now looks for the nearest ancestor whose inline overflow
along that axis is auto or scroll, and scrolls it through Element.scrollBy,
or the viewport through Window.scrollBy, so the trusted scroll and
scrollend events are scheduled once instead of firing a second, bubbling
scroll inline. The lookup reads the inline style straight from the style
manager instead of building a computed-style object per ancestor.

Both scrollBy implementations saturate the addition, so an oversized delta
from CDP or from a script no longer overflows the i32 position.
2026-09-12 14:12:27 +02:00
Adrià Arrufat 365bc1e1d2 Merge remote-tracking branch 'origin/main' into wheel-scrolls-viewport 2026-09-12 14:07:06 +02:00
Karl Seguin e8aa759390 Merge pull request #3495 from lightpanda-io/HttpClient-Owner-scope
chore: dedupe HttpClient.Owner using new GlobalScope
2026-09-12 16:33:17 +08:00
Karl Seguin cb69054cf9 Merge pull request #3496 from lightpanda-io/adblock-log-level
logs: demote UrlBlocked errors from err -> warn
2026-09-12 16:33:00 +08:00
Karl Seguin 5e849c1bfe logs: demote UrlBlocked errors from err -> warn
Kept a `UrlBlocked` on the main navigation an error, since that seems something
worth raising to the user.
2026-09-12 12:29:54 +08:00