Commit Graph
7758 Commits
Author SHA1 Message Date
Karl Seguin e330f5eab1 update v8 (2nd try) 2026-07-04 10:36:58 +08:00
Karl Seguin 590bd0b3be update v8 2026-07-04 10:29:12 +08:00
Karl Seguin 4bebb2207d idb: on abort, rollback created stores/indexes
Also, better query key validation
2026-07-03 19:09:57 +08:00
Karl Seguin c50c66a21f idb: add compound keys 2026-07-03 18:30:54 +08:00
Karl Seguin 7d79c73c39 idb: expose request.source, and improve cursor state check 2026-07-03 17:31:00 +08:00
Karl Seguin 55894afd65 idb: reject operations on deleted datastore 2026-07-03 12:47:41 +08:00
Karl Seguin 5993e9889a idb: abort transaction on callback exception 2026-07-03 09:22:53 +08:00
Karl Seguin c4ec6c4adb idb: handle reentrant upgradeneeded callbacks
Add handling for upgradeneeded callbacks enqueuing more work.
2026-07-03 07:10:03 +08:00
Karl Seguin e3b605c35d idb: improve indexeddb memory model
Rather than having everything tied to the page's memory (arena, factory), most
things are now tied to an IDBTransaction and its arena. The IDBTransaction is
reference counted and finalized with v8. The lifecycle is relatively complicated
compared to anything else we have, which is a concern, but using the page arena
seems like a dealbreaker to me.

Any "child" created for the transaction (e.g. IDBRequest) has its v8 acquireRef
and releaseRef forwarded to the Transaction. This should make v8's usage safe.
However, in addition to this, Zig itself must take a RC whenever a drain is
scheduled AND whenever the transaction is parked in the engine. And these
things, especially on cleanup, can be a little messy.

The _awful_ cursor allocations have been improved by a local re-used ArrayLists
for the key/primary key/value. So rather than accumulating _every_ allocation
we now only capture the peak.

One final memory-related area this commit addresses is the lifetime difference
between Transactions (Page) and Engine (Session). This is problematic because
the Engine can reference the Transaction. On js.Context deinit, the engine is
notified and all related Transactions are canceled/removed. This is...unusual
in our design. We have other similar cases that use a list on the frame/GWS to
track resources to cleanup. But, the Engine already _has_ to have this list so
rather than book-keeping in Engine AND Frame AND WGS, only Engine has a list at
the cost of js.Context having to notify it on teardown.
2026-07-03 07:10:03 +08:00
Karl Seguin 64d162af16 idb: allow re-entrant and interleaved transactions
Because of the single-connection nature of our Engine (in-memory SQLite), the
IDB Engine now keeps a list of "gated" transactions, processing one at time.
When one transaction completes, waiting transactions are signaled so that they
can processes their queue.

This also adds a double-queue so that any requests that comes in while
processing requests goes into a new queue and is only drained on the next tick.

Note: this introduces a UAF, where Engine lives on Session and referneces
IDBTransactions which are tied to the page. The next bit work on IDB is the
memory re-work, so I'm punting that there.
2026-07-03 07:10:03 +08:00
Karl Seguin ed18d6ede3 fix broken zig fmt 2026-07-03 07:10:02 +08:00
Karl Seguin b3aae60827 idb: handle requeue index order and abort in settle 2026-07-03 07:10:02 +08:00
Karl Seguin df32868bba idb: add IDBRecord 2026-07-03 07:10:02 +08:00
Karl Seguin 1b46d48f43 idb: rework transaction model
Previously, every operation would run synchronously, and resolve the value
on the next drain (schedule task run on the next tick).

With 1 connection per DB, this doesn't work with SQLite: you cannot have nested
transactions. Imagine:

add
  begin
    insert into
    JS callback (success)
      add
        begin <-- nested
          insert

This approach captures the requested operation and does all necessary validation
(since most validation errors are returned synchronously), but only executes
operations on drain. Hence, rather than IDBTransaction._requests being a
queue of result values to emit on drain, it is now a queue of operations to
execute and then emit.

This will also potentially make it easier to address serious memory
issues by better scoping the lifetime of things (particularly requests).
2026-07-03 07:10:02 +08:00
Karl Seguin 7101f47610 make indexedb in-memory only (for now) 2026-07-03 07:10:02 +08:00
Karl Seguin 3764224cb6 Add object store name support + DOMStringList 2026-07-03 07:10:02 +08:00
Karl Seguin b948fae38f add IDBIndex 2026-07-03 07:10:01 +08:00
Karl Seguin 239883ce84 webapi, idb: Add IDBCursor and IDBCursorWithValue 2026-07-03 07:10:01 +08:00
Karl Seguin 09e5e5df12 webapi, idb: add IDBKeyRange
Including support for IDBKeyRange based operations
2026-07-03 07:10:01 +08:00
Karl Seguin 90a5a5fc51 webapi: IndexedDB base
Initial WIP on IndexedDB WebAPI. Uses sqlite and a new `--indexdb_dir` config.
Defaults to :memory:.

The main things missing are the IDBCursor, IDBIndex and IDBKeyRange, along with
a bunch of smaller apis.

Also, every object is currently tied to the Page arena / factory. It's possible
that's how it will have to be, but, once more of the API lands, I will check
if we can scope these better.
2026-07-03 07:09:59 +08:00
Karl Seguin 6708d5b961 Merge pull request #2862 from lightpanda-io/global_enumerable
webapi: Make all interfaces non-enumerable
2026-07-03 07:06:54 +08:00
Pierre Tachoire ca0579413e Merge pull request #2837 from lightpanda-io/cache-simple-heuristic
Cache Simple Heuristic
2026-07-02 17:42:19 +02:00
Karl Seguin 53767d2dad Merge pull request #2852 from lightpanda-io/local_arena
mem: add local_arena
2026-07-02 22:46:11 +08:00
Karl Seguin 25d8b7d5c7 mem: add local_arena
The `call_arena` is currently our shortest-lived arena. Its promise is that it
will be valid for at least 1 v8 -> zig -> v8 function call, which makes it ideal
for getting values from v8 into zig, temporary work, and getting values from
zig to v8.

VBut `call_arena`'s lifetime is actually much longer. It's only reset when the
call_depth reaches 0. And the reason for that are Zig functions that invoke
v8 callbacks. If you just do it when a function ends, then if you allocate in
ZigA and then call CallbackA which calls ZigB, then when ZigB ends, your ZigA
allocation is cleared. This is something we could solve by reaching into
Zig's ArenaAllocator to capture a position to rollback from.

So, `call_arena` can end up living relatively long and accumulating quite a bit
of memory. But most calls _don't_ invoke callbacks. Hence, `local_arena` IS
reset at the end of every function.

Using `local_arena` _is_ dangerous, mostly for the case where some of our APIs
receive a *Frame (or *Execution) and thus might use a `local_arena` because they
know they aren't invoking a JS callback. BUT, those APIs might not know that
they're also invoked by some other Zig code that _could_ be invoking JS.

Dangerous? Sure. But, github has code that looks like:

```js
function onDelegatedClick(e) {
  for (const el of document.querySelectorAll('[data-action]')) {
    if (el.matches('.menu > .item:not(.disabled) a[href]')) {
      handle(el);
    }
    el.closest('.panel');
  }
}
```

Anything allocated in the `call_arena` will only be freed when the caller of
`onDelegatedClick` ends. The `querySelectorAll` returns hundreds of elements
and thus builds hundreds of parsed CSS and other scrap (x2 for `matches` and
closest`). `call_arena` peaks at 15MB. With the local_arena? 1MB. If 10x more
elements were returned, the peak would be 10x higher. With local_arena, it
stays 1MB.
2026-07-02 21:06:51 +08:00
Karl Seguin 3fbe460b04 Merge pull request #2861 from lightpanda-io/MessageEvent_getSource
crash, worker: Fix crash when MessageEvent.source is called from worker
2026-07-02 21:04:45 +08:00
Karl Seguin 0db0b89def Merge pull request #2858 from lightpanda-io/structuredClone
webapi: structuredClone for host (aka Zig) objects
2026-07-02 21:00:57 +08:00
Karl Seguin 69547db8ba webapi: Make all interfaces non-enumerable
When I added this, I was going through the list in /dom/interface-objects.html
thinking that was exhaustive. But no, no interfaces should be enumerable and
various other WPT tests (usually the idlharness ones) assert that for their
respective types.

Make it _always_ non enumerable means we no longer need Meta.enumerable to
be declared true/false (it's always false).
2026-07-02 20:54:33 +08:00
Karl Seguin 8f8df00b0f crash, worker: Fix crash when MessageEvent.source is called from worker
The source for a worker is always null. The getter cannot receive a *Frame since
it can be called from a Worker's context.
2026-07-02 19:28:09 +08:00
Pierre Tachoire 5fcfb4e344 Merge pull request #2859 from lightpanda-io/crypto-random-leak
use getrandom syscall for std.crypto.random
2026-07-02 12:03:30 +02:00
Pierre Tachoire 37a7f034c8 use writerStreaming in log 2026-07-02 11:23:45 +02:00
Pierre Tachoire e56a4f6259 use getrandom syscall for std.crypto.random
std.crypto.random's default backend mmaps a thread-local 528-byte state
page on first use and never unmaps it — there is no thread-exit hook.
With one detached thread per CDP connection (Server.handleConnection),
that leaks one resident page per connection (uuidv4 in
Page.getOrCreateOrigin touches it), ~4KB/conn of unbounded RSS growth.
Route every std.crypto.random call to the getrandom syscall instead.
.crypto_always_getrandom = true,
2026-07-02 11:19:53 +02:00
Halil Durak 60aacfa2b0 Merge pull request #2851 from lightpanda-io/nikneym/network-x509-store
networking: prefer `X509_STORE` instead of `ca_blob`
2026-07-02 09:10:15 +03:00
Halil Durak c86dad5430 libcrypto: remove unused utilities 2026-07-02 08:46:22 +03:00
Halil Durak 5e567adf1b ci: run serve command with TLS host verification disabled 2026-07-02 08:46:21 +03:00
Halil Durak fd0bff3af2 http: remove dead code 2026-07-02 08:46:21 +03:00
Halil Durak 820f5684cb networking: replace ca_blob with X509_STORE
Idea here is to skip re-parsing that happen for each connection; we already use BoringSSL, so we can take more advantage of it by directly mutating cert store of `SSL_CTX`.
2026-07-02 08:46:21 +03:00
Halil Durak 00123d5102 libcurl: more bindings
* Make curl_easy_setopt aware of SSL_CTX_FUNCTION and SSL_CTX_DATA,
* Add CURLE_* errors.
2026-07-02 08:46:21 +03:00
Halil Durak 6e75a0caa9 libcrypto: bind couple X509, X509_STORE and SSL_CTX helpers 2026-07-02 08:46:20 +03:00
Karl Seguin a750a6942d webapi: structuredClone for host (aka Zig) objects
Adds the infrastructure for [de]serializing Zig objects via structuredClone.
Adds support to Blob, File, FileList and ImageData. These are the easiest to
implement. Blob is used extensively by WPT IndexedDB tests, but this PR can be
merged prior to IndexedDB landing.
2026-07-02 12:43:56 +08:00
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