Commit Graph
9542 Commits
Author SHA1 Message Date
Karl Seguin 37f31afec5 Merge pull request #3387 from lightpanda-io/idb-unique-cursor
indexeddb: support nextunique and prevunique cursor directions
2026-09-03 07:23:06 +08:00
Karl Seguin c189d72adf add safer shutdown 2026-09-03 07:11:26 +08:00
Karl Seguin 0758ba6b87 Merge pull request #3393 from lightpanda-io/fix-uaf-indexdb-fireupgrade
indexeddb: keep upgradeneeded event alive past dispatch
2026-09-03 06:49:59 +08:00
Karl Seguin 7f29bf6931 500 on write error and remove blocking writes 2026-09-03 06:47:53 +08:00
Karl Seguin 17c2aa7d1f Hardening
Give it one pass through some Claude fuzz testing. Add a max message size,
protect against weird interactions during a shutdown and we had some pending
accepts. Put a time limit on blocked writes.
2026-09-03 06:47:53 +08:00
Karl Seguin 116dab4c85 mac/bsd support (kqueue) 2026-09-03 06:47:53 +08:00
Karl Seguin e6241c1918 serve: Improve cdp/bidi server
Significant rework of the CDP/BiDi server. There are two main changes:

1 - poll replaced with EPoll/Kqueue (1)
2 - make http serving a first class citizen

The change from poll -> epoll/kqueue isn't performance driven, it's just about
tighter code. Both epoll and kqueue let you associate arbitrary data with a
socket, so we don't need to keep arrays in sync in order to associate a socket
with a CDP by index. They both provide some event/notification mechanism, which
is cleaner than the pipe required by poll.

The poll -> epoll/kqueue change could almost have been mechanical. Making HTTP
a first class citizen is the more significant of the two changes

In `main`, a new connection always spawns a thread and, until does its own
little read loop until the connection is upgraded. This is not efficient, it
uses up a connection slot, and it's inconsistent with the final WebSocket
connection which _is_ polled off the main loop. Using up a slot means that
keepalive isn't possible, else HTTP connections would quickly use up all
available slots/threads.

This commit parses and serves HTTP requests on the main thread (safe
because none of the processing is blocking). The approach is better streamlined
for HTTP requests which never upgrade (/metrics, WebDriver) without causing
any performance overhead for those that do. It simplifies some things (e.g. an
"http" socket or a "websocket" socket is monitored and read in a similar manner
(on the main loop)). It makes other things more complicated; the flow is no
longer accept -> spawn -> upgrade -> websocket loop. It's loop -> accept -> loop
-> process -> (http | ws).

This is built ontop of the BiDi branch because (a) WebDriver is what needs
better  HTTP support and (b) some of the more mechanical changes already exist
in that branch (e.g. src/cdp/, src/server.zig -> src/server/*)

(1) kqueue landing in 2 commits from now on this branch.
2026-09-03 06:47:49 +08:00
Pierre Tachoire 37a120cc77 event: keep keydown and worker error events alive past dispatch 2026-09-02 21:23:25 +02:00
Pierre Tachoire 3520065398 indexeddb: keep upgradeneeded event alive past dispatch 2026-09-02 21:13:06 +02:00
Scott Taylor 427960fd6c fix: avoid rearming heap termination during teardown
Assisted-By: devx/5efceed7-6cf0-4f8f-b1b4-5a1791ae8211
2026-09-02 09:23:11 -04:00
Karl Seguin 872fd7d5ff webapi: resource-timing
Adds resource timing, e.g. `performance.getEntriesByType("resource")`.

The `resource-timing` WPT category is currently at 4.6%, and this is a first
step at improving it. It also hopefully fixes https://github.com/lightpanda-io/browser/issues/3359

This is more complicated than I thought because there's a "Timing-Allow-Origin"
header that a server can include which hides some of the data if the request
doesn't come from the listed origin. And that, of course, interacts with
redirects.

(The DOMException change is seemingly random, but it came up in one of the WPT
cases I was looking at).
2026-09-02 20:39:51 +08:00
Adrià Arrufat 6b24f1e867 stability: bound intersection runaways by burst duration, not count
A delivery count cannot separate a runaway from a busy but healthy page:
airbnb legitimately needs up to ~50 delivery sessions per load, and the
storefront runaway had already exhausted the timer table by ~70.
Duration does: every healthy burst measured went quiet within 4.5s of
its first delivery, while the runaway never did.

Deliveries closer than 2s apart form a burst; a burst that runs past 10s
disconnects the frame's intersection observers. Measuring from the start
of the current burst rather than from the first delivery keeps lazy
loading alive on pages that live for minutes in serve or agent mode.

A chain that re-observes synchronously never leaves the microtask
checkpoint, so a per-burst ceiling of 1024 deliveries stops it from
spinning for the full limit.
2026-09-02 10:29:45 +02:00
Karl Seguin 91187e9483 Merge pull request #3381 from lightpanda-io/canvas-measure-text
webapi: canvas measureText and the drawing methods chart libraries probe
2026-09-02 15:15:41 +08:00
Karl Seguin 195e240c9d Add buffers to Context2D state to minimize allocations
RC TextMetrics

Share more state between CanvasRenderingContext2D and Offscreen
2026-09-02 14:51:15 +08:00
Karl Seguin 7d39a4fbaf http: handle redirects better
Our redirect handling was "optimized" to re-use the same easy connection, at the
cost of circumventing the entire pipeline. A redirect would not check the new
target's robots.txt, rate limit, CORS, Web Auth, ...

The new code simply restarts the pipeline (with a discriminator tag so that
things like CDP can tell the difference between a redirected request and a new
one).
2026-09-02 12:41:53 +08:00
Karl Seguin 235d10c6b5 Merge pull request #3373 from lightpanda-io/streams-queuing-strategies
webapi: CountQueuingStrategy and ByteLengthQueuingStrategy
2026-09-02 12:37:03 +08:00
Karl Seguin fceec923fc Merge pull request #3382 from lightpanda-io/http-keepalive-maxconnects
http: keep connections alive across navigations
2026-09-02 11:53:31 +08:00
Karl Seguin 10c6f553c8 fix copyright date, improve js parameter mapping 2026-09-02 11:00:22 +08:00
Adrià Arrufat a8ff1c6c91 webapi: CountQueuingStrategy and ByteLengthQueuingStrategy
Both globals were missing, so MDN-canonical stream setup code threw
ReferenceError. size() is a method rather than an accessor returning a
function; the streams don't consume it yet.
2026-09-02 11:00:22 +08:00
Karl Seguin c85208a3be zig fmt 2026-09-02 11:00:04 +08:00
Karl Seguin 1b50e235d5 Set max_connect based on config.httpMaxConcurrent()
This is per multi. So the original suggested 128 could result in 2048 idle
connection with 10 CDP connections.
2026-09-02 11:00:04 +08:00
Adrià Arrufat 39bd76e079 http: keep connections alive across navigations
CURLMOPT_MAXCONNECTS was never set, so libcurl used its default of 4x the
number of easy handles currently attached to the multi. Handles are added
and removed per transfer, so between page loads that default collapses to
roughly zero and every cached connection is evicted: revisiting a host
after browsing elsewhere re-paid connect + TLS every time.

Measured over 25 navigations across 5 sites (5 rounds, ReleaseFast), median
warm navigation drops from 0.73s to 0.42s on news.ycombinator.com and from
0.71s to 0.42s on github.com; total navigation time 11.1s -> 7.3s. Loading
one host repeatedly was already fast and is unchanged; the win is on
cross-site browsing, which is what agents and crawlers actually do.
2026-09-02 11:00:04 +08:00
Karl Seguin 2745d8510a Merge pull request #3388 from lightpanda-io/tests-no-watchdog
ci: disable watchdog during tests to reduce flake
2026-09-02 10:55:45 +08:00
Karl Seguin 2817922305 ci: disable watchdog during tests to reduce flake 2026-09-02 10:41:46 +08:00
Karl Seguin 8888be1229 indexeddb: support nextunique and prevunique cursor directions 2026-09-02 10:17:11 +08:00
Karl Seguin d7c70b0b60 Merge pull request #3386 from lightpanda-io/ci-orderfile-2
ci: regenerate orderifle
2026-09-02 10:00:29 +08:00
Karl Seguin fe3a491d22 ci: regenerate orderifle
remove auto orderfile generation, too much hassle.

Manually regenerate the two orderfiles. Change it to use newlines so the diff
is better.
2026-09-02 09:44:25 +08:00
Karl Seguin 2474a2c2bc Merge pull request #3368 from lightpanda-io/window-stop
webapi: window.stop()
2026-09-02 09:06:07 +08:00
Karl Seguin 9bbabfe92a Merge pull request #3364 from lightpanda-io/owner-consolidation
chore: Simplify making HTTP requests.
2026-09-02 09:03:00 +08:00
Karl Seguin 9df492b453 Merge pull request #3385 from lightpanda-io/ci-orderfile-2
strip spacing in captured output
2026-09-02 08:18:36 +08:00
Karl Seguin ed88bd763f complete merge 2026-09-02 08:17:15 +08:00
Karl Seguin 537ea4d890 strip spacing in captured output 2026-09-02 08:04:39 +08:00
Karl Seguin e25d2da0c0 chore: Simplify making HTTP requests.
If you look at https://github.com/lightpanda-io/browser/pull/3293, you'll see
a relatively contained change that has to touch over 20 files. The issue is that
every HttpClient.newRequest needs to provide a lot of data. But `newRequest`
takes a 2nd parameter: the HttpClient.Owner. If we make that Owner a little
smarter, we can start to remove some of the individual fields needed in
newRequest. For example, we can still allow a callsite to pass frame_id but,
by default, we can use the owner's frame_id (which is what we want in most
cases).
2026-09-02 07:55:53 +08:00
Karl Seguin eea50fe98a Merge pull request #3293 from lightpanda-io/nikneym/samesite-strict-bypass
compute site-for-cookies from ancestor chain of a `Frame`
2026-09-02 07:54:24 +08:00
Karl Seguin 5e42a253e1 Merge pull request #3372 from lightpanda-io/html-contenteditable-reflect
webapi: reflect HTMLElement.contentEditable
2026-09-02 07:29:40 +08:00
Karl Seguin 07198b71ff fix comments 2026-09-02 07:28:25 +08:00
Halil Durak add8a8c003 Cookie: getter/setter changes for site-for-cookies
Also updates matchCookies/onCookieChanged from the hard-coded "same-site + navigation" to areSameSite(exec.siteForCookies(), host) and is_navigation=false.
2026-09-02 07:19:39 +08:00
Halil Durak 9ddf6ad02a Worker: inherit site-for-cookies from the creating document 2026-09-02 07:19:39 +08:00
Halil Durak 87308b5e23 compute site-for-cookies from ancestor chain of a Frame
The site for cookies were computed from the immediate parent `Frame`, which would allow sending a cookie that's `SameSite=Strict` from 2 levels deep under. Directly from RFC6265bis, this PR essentially implements (except for step 4, we skip host-less ancestors):

   Given a Document (document), the following algorithm returns its
   "site for cookies":

   1.  Let top-document be the active document in document's navigable's
       top-level traversable.

   2.  Let top-origin be the origin of top-document's URI if top-
       document's sandboxed origin browsing context flag is set, and
       top-document's origin otherwise.

   3.  Let documents be a list consisting of the active documents of
       document's inclusive ancestor navigables.

   4.  For each item in documents:

       1.  Let origin be the origin of item's URI if item's sandboxed
           origin browsing context flag is set, and item's origin
           otherwise.

       2.  If origin is not same-site with top-origin, return an origin
           set to an opaque origin.

   5.  Return top-origin.
2026-09-02 07:19:35 +08:00
Karl Seguin eb9d12205e Merge pull request #3384 from lightpanda-io/ci-orderfile-2
CI permission tweak
2026-09-02 07:16:49 +08:00
Karl Seguin 3befda2acc Merge pull request #3360 from lightpanda-io/indexeddb-better-errors
indexeddb: Improve error handling
2026-09-02 07:13:24 +08:00
Karl Seguin 0b3e75ca08 Merge pull request #3361 from lightpanda-io/cdp-urlpattern
cdp: support request interception patterns
2026-09-02 07:13:02 +08:00
Karl Seguin b852b7b89a Merge pull request #3380 from lightpanda-io/RenderTree
chore: Extract common Markdown/Screenshot logic
2026-09-02 07:12:29 +08:00
Adrià Arrufat 38017c3d41 ci: release lightpanda-python on version tags
Add an update-python-package job that creates the matching release on
lightpanda-io/lightpanda-python (same distribution-app pattern as the
docker and homebrew jobs). That release triggers the wheels workflow
there, replacing its daily track-browser polling; its publish to PyPI
still waits for approval on that repo's pypi environment.

The distribution app needs contents write access to lightpanda-python.
2026-09-01 22:26:50 +02:00
Adrià Arrufat afc97d31f3 stability: disconnect runaway intersection observers
We report every attached element as fully visible, because without layout the
alternative (a faux position vs. the viewport) hides everything past the first
screenful. Infinite scroll turns that into a non-terminating loop: the page
observes a sentinel, we report it intersecting immediately, the callback loads
the next page and observes a fresh sentinel, and so on with nothing to stop it.

Seen on a live storefront: the collection page paginated itself to page 10 over
68 delivery sessions and ~94 requests, exhausted the timer table
(error.TooManyTimeout), and hung until the 30s watchdog killed the navigation.
It reproduced on roughly one run in seven.

Cap the delivery sessions per document and disconnect the frame's intersection
observers past it, mirroring the MutationObserver runaway guard. The limit is
32: measured over a static page, an ad-heavy news hub and a storefront, no
document needed more than 7 sessions, while a runaway climbs without bound.

This bounds the damage rather than fixing the visibility model. Reporting
intersections against a real viewport, and resetting the budget when the page
actually scrolls, are the follow-ups.
2026-09-01 16:26:54 +02:00
Karl Seguin 4df0b5e5a3 Merge pull request #3374 from lightpanda-io/webapi-small-accessors
webapi: Headers.getSetCookie, customElements.getName, Element.part
2026-09-01 19:11:58 +08:00
Karl Seguin 41ae39789b zig fmt 2026-09-01 18:52:58 +08:00
Karl Seguin 96de22fc0a webapi: enhance header to block forbidden response headers 2026-09-01 18:51:39 +08:00
Karl Seguin d663afc0ee Merge pull request #3379 from lightpanda-io/input-value-as
webapi: input valueAsNumber, valueAsDate, stepUp/stepDown, showPicker
2026-09-01 18:43:32 +08:00
Karl Seguin 83b3b21a34 CI permission tweak 2026-09-01 18:40:55 +08:00