Commit Graph
7966 Commits
Author SHA1 Message Date
Francis BouvierandClaude Fable 5 ca92bb6a1b webapi: window-reflecting body/frameset event handlers
Fixes 24 failing tests in WPT /dom/events/Body-FrameSet-Event-
Handlers.html (24/48 -> 48/48).

Per the HTML spec, the onblur, onerror, onfocus, onload, onresize and
onscroll event handlers of body and frameset elements are aliases for
the Window's handlers (the "window-reflecting body element event
handler set"), and setting the corresponding content attribute on those
elements must activate the Window's handler too. Two groups of
failures:

- "Set ...": assigning a non-callable (e.g. "") to the IDL attribute
  threw "invalid argument" instead of storing null, because the setters
  took ?js.Function.Global. Per [LegacyTreatNonObjectAsNull], the
  Body/FrameSet setters now use Window.FunctionSetter (function or
  anything-else-means-null), like the existing Window handler setters.
- "Forward ... to Window": only body.onload was forwarded. Window now
  stores onblur/onfocus/onresize/onscroll handlers too (and exposes the
  matching accessors, which it lacked entirely), and Body/FrameSet
  implement getters/setters forwarding all six handlers to the Window
  of the current frame. Setting or removing one of the six content
  attributes (at parse time via Build.complete or at runtime via the
  attributeChange/attributeRemove build hooks) compiles the handler
  body and stores it on the Window, so window.onX === element.onX.

Coverage: /dom/events/Body-FrameSet-Event-Handlers.html 24/48 -> 48/48.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:00:58 +02:00
Francis BouvierandClaude Fable 5 f1498f2da9 webapi: throw TypeError for addEventListener({ signal: null })
Fixes 2 failing tests in WPT /dom/events/AddEventListenerOptions-
signal.any.html (and its .worker variant): "Passing null as the signal
should throw" (with a valid and with a null listener).

The AddEventListenerOptions dictionary declares `signal` as a
non-nullable AbortSignal, so per Web IDL an explicit null (or any
non-AbortSignal value) must be rejected with a TypeError during the
dictionary conversion — even when the listener argument is null. The
bridge collapsed both a missing member and an explicit null into a Zig
null, silently ignoring the signal.

EventTarget.addEventListener now receives the signal member as a raw
js.Value: an absent or undefined member means "no signal", anything
else is converted to an AbortSignal (throwing a TypeError when it
isn't one), and the conversion runs before the null-callback early
return to match the spec's argument-conversion ordering.

Coverage: /dom/events/AddEventListenerOptions-signal.any.html
9/11 -> 11/11, /dom/events/AddEventListenerOptions-signal.any.worker.html
9/11 -> 11/11.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:00:58 +02:00
Francis BouvierandKarl Seguin a00d06d2d8 webapi: finish the exec -> frame parameter conversion in enumerators
The applied review suggestions switched the getNames/getIndexes
enumerator callbacks of HTMLCollection, NamedNodeMap and DOMStringMap
from *const js.Execution to the injected *Frame, but only partially:
signatures and bodies disagreed and the build broke. Complete the
conversion — the bridge resolves *Frame from the context, so the manual
exec.js.global switch goes away, and the arenas/JS locals are reached
through the frame.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:59:20 +02:00
Francis BouvierandKarl Seguin 63ad289cb1 Update src/browser/webapi/element/DOMStringMap.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:50:59 +02:00
Francis BouvierandKarl Seguin 071eee15ea Update src/browser/webapi/element/Attribute.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:50:52 +02:00
Francis BouvierandKarl Seguin ad61533def Update src/browser/webapi/element/Attribute.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:50:45 +02:00
Francis BouvierandKarl Seguin 58b42499d9 Update src/browser/webapi/element/Attribute.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:50:39 +02:00
Francis BouvierandKarl Seguin 5072722222 Update src/browser/webapi/collections/HTMLCollection.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:50:33 +02:00
Francis BouvierandKarl Seguin fb06ea0f44 js: rename deleteIndex helpers to deleteOrDefineIndex
The Caller.deleteIndex and Caller.deleteNamedIndex helpers are invoked
by the bridge for definer interceptors too, since definers share the
deleter's (self, index/name) -> bool calling shape. Rename them (and
their private counterparts) to deleteOrDefineIndex and
deleteOrDefineNamedIndex so the name reflects both uses.

No behavior change.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:48:21 +02:00
Francis BouvierandKarl Seguin 13b253f1f8 Update src/browser/webapi/element/DOMStringMap.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:38:37 +02:00
Francis BouvierandKarl Seguin fba0390b92 Update src/browser/webapi/element/DOMStringMap.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:36:13 +02:00
Francis BouvierandKarl Seguin 640776c9ee Update src/browser/webapi/element/DOMStringMap.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:36:06 +02:00
Francis BouvierandKarl Seguin 5f553144fb Update src/browser/webapi/element/Attribute.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:35:58 +02:00
Francis BouvierandKarl Seguin 2bf5c4abb3 Update src/browser/webapi/collections/HTMLCollection.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 14:34:51 +02:00
Francis BouvierandClaude Fable 5 15a8a81e64 js: support definer/descriptor interceptors; fix HTMLCollection expandos
Fixes the remaining WPT /dom/collections failures around setting,
defining and describing HTMLCollection properties:

- HTMLCollection-own-props.html: assigning to a supported named property
  or to any array index must fail (TypeError in strict mode), while
  expandos for unsupported names must keep working.
- HTMLCollection-supported-property-indices.html: indexed property
  descriptors must be {enumerable: true, configurable: true, writable:
  false}, Object.defineProperty on any array index must throw, and
  deleting a supported index must fail.
- HTMLCollection-supported-property-names.html: named property
  descriptors must be {enumerable: false, configurable: true, writable:
  false} ([LegacyUnenumerableNamedProperties]) and defineProperty on a
  supported name must throw.

Bridge changes, mirroring the existing interceptor callbacks:

- bridge.Indexed gains a definer callback; bridge.NamedIndexed gains
  definer and descriptor callbacks, exposed through new indexedFull and
  namedIndexedFull builders so existing indexed/indexedReadWrite/
  namedIndexed call sites are untouched. Definer and descriptor
  handlers reuse the deleter (name -> bool) and getter (name -> value)
  calling shapes. The stale void-returning definer/descriptor typedefs
  in the v8 binding are bridged with @ptrCast; v8 actually reads the
  returned u32 as v8::Intercepted.
- Caller's query helpers (shared queryReturn) now accept, besides the
  existing bool shape, query callbacks returning the
  v8.PropertyAttribute bits directly (e.g. ReadOnly), with
  error.NotHandled falling through to the ordinary lookup.

HTMLCollection registers:

- an indexed setter and definer that always fail (no indexed setter in
  Web IDL), and an indexed query reporting ReadOnly for supported
  indices;
- a named definer that fails for supported names, and a named
  *descriptor* (not query) reporting {writable: false, enumerable:
  false, configurable: true}. Using the descriptor callback instead of
  query is deliberate: the ordinary [[Set]] path does not consult it, so
  assigning through a derived object (HTMLCollection-as-prototype.html)
  still creates an expando on the receiver, as the spec's
  ignore-named-props rule requires, while direct assignments fail via
  the definer.

Coverage: /dom/collections is now fully green (53/53):
- HTMLCollection-own-props.html 4/8 -> 8/8
- HTMLCollection-supported-property-indices.html 4/7 -> 7/7
- HTMLCollection-supported-property-names.html 5/6 -> 6/6
- HTMLCollection-as-prototype.html stays 2/2

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:34:13 +02:00
Francis BouvierandClaude Fable 5 809b36d96a webapi: enumerate collection supported properties for ownPropertyNames
Fixes the Object.getOwnPropertyNames related failures in WPT
/dom/collections:

- HTMLCollection-live-mutations.window.html: "ownPropertyNames" case
- HTMLCollection-supported-property-names.html: 3 getOwnPropertyNames cases
- namednodemap-supported-property-names.html: all 3 cases
- domstringmap-supported-property-names.html: all 5 cases

Object.getOwnPropertyNames returned no interceptor-backed properties
because these collections registered no enumerator callbacks.

Using the bridge's existing indexed and named enumerator support:

- HTMLCollection registers an indexed enumerator (the supported
  indices) and a named enumerator implementing the spec's supported
  property names: for each element in tree order, its non-empty id and,
  for HTML elements, its non-empty name attribute, without duplicates.
- NamedNodeMap registers indexed and named enumerators (attribute
  qualified names in order, via List.getNames).
- DOMStringMap registers a named enumerator (camel-cased data-*
  attribute names). The previously unused kebabToCamel helper is fixed
  to follow the spec conversion: only a '-' followed by an ASCII
  lowercase letter is folded to an uppercase letter, a bare "data-"
  attribute maps to the empty name, and a trailing '-' is preserved.

(As originally written, this commit also added named enumerator
interceptor support to the js bridge; main gained equivalent support
independently, so the rebase keeps main's bridge API.)

Coverage:
- /dom/collections/HTMLCollection-live-mutations.window.html 4/5 -> 5/5
- /dom/collections/HTMLCollection-supported-property-names.html 2/6 -> 5/6
- /dom/collections/namednodemap-supported-property-names.html 0/3 -> 3/3
- /dom/collections/domstringmap-supported-property-names.html 0/5 -> 5/5

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:19:19 +02:00
Francis BouvierandClaude Fable 5 01a16fe66e webapi: supported HTMLCollection properties can't be deleted
Fixes 2 failing tests in WPT /dom/collections/HTMLCollection-delete.html
("Strict id" and "Strict name"): deleting a supported indexed or named
property of an HTMLCollection must fail (and throw a TypeError in strict
mode), since those properties are backed by the live collection.

HTMLCollection registered no deleter interceptors, so `delete c[0]` and
`delete c.name` fell through to the ordinary [[Delete]] which succeeded
on the (non-own) property.

HTMLCollection now registers an indexed and a named deleter (via the
bridge's indexedReadWrite/namedIndexed deleter callbacks) that return
false for supported properties - which v8 turns into a TypeError in
strict mode - and error.NotHandled for unsupported ones, leaving those
to the ordinary path.

(As originally written, this commit also added indexed setter/deleter
interceptor support to the js bridge; main gained equivalent support
independently, so the rebase keeps main's bridge API.)

Coverage: /dom/collections/HTMLCollection-delete.html 2/4 -> 4/4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:09:42 +02:00
Francis BouvierandClaude Fable 5 cc81c5993b webapi: don't fire AbortSignal.timeout() after frame detach
Fixes the failing test in WPT /dom/abort/abort-signal-timeout.html:
"Signal returned by AbortSignal.timeout() is not aborted after frame
detach".

The scheduler task registered by AbortSignal.timeout() kept running
after the iframe that created the signal was removed from the document,
so the signal aborted and the abort event fired. Per the DOM spec, the
abort is queued as a global task on the signal's relevant global, and
such tasks must not run once the global's document stops being fully
active.

The timeout callback now walks the signal's frame and its ancestors and
skips the abort when a hosting iframe element is no longer connected.

Coverage: /dom/abort/abort-signal-timeout.html 0/1 -> 1/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:53:15 +02:00
Francis BouvierandClaude Fable 5 1d263aabbd webapi: keep explicit null as AbortSignal abort reason
Fixes 1 failing test in WPT /dom/abort/event.any.html (and its .worker
variant): "AbortController abort(null) should set signal.reason".

abort() reason parameters were typed ?js.Value.Global, and the JS->Zig
conversion collapses an explicit JS null into a missing argument, so
controller.abort(null) fell back to the default "AbortError"
DOMException. Per the DOM spec, the abort reason defaults to a new
AbortError only when the reason is not given (undefined); an explicit
null must be stored as-is.

The parameters are now ?js.Value, which preserves the missing vs
explicit-null distinction, and the new AbortSignal.reasonFromJs helper
only falls back to the default for a missing or undefined reason.

Coverage: /dom/abort/event.any.worker.html 15/16 -> 16/16,
/dom/abort/event.any.html 15/16 -> 16/16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:47:42 +02:00
Francis BouvierandClaude Fable 5 7d23b62573 webapi: fix AbortSignal dependent-signal abort semantics
Fixes 3 failing tests in WPT /dom/abort/abort-signal-any.any.html (and
its .worker variant):

- "Dependent signals are aborted correctly for reentrant aborts": when a
  source signal aborted a second source from within an abort listener,
  the shared dependent signal dispatched its abort event twice. Per the
  DOM spec, only dependent signals that are not already aborted are
  appended to the list of signals to dispatch. Dependend.markAborted now
  reports whether the dependent was newly marked, and abort() only
  dispatches for those.

- "Dependent signals should use the same DOMException instance" (already
  aborted source / source aborted later): Reason.dom held a DOMException
  by value, so every .reason access wrapped a fresh JS object and the
  identity check source.reason === dependent.reason failed. Reason.dom
  is now a *DOMException allocated once at abort time; the JS bridge
  identity-maps the pointer so the source and all dependents expose the
  very same DOMException instance.

Coverage: /dom/abort/abort-signal-any.any.worker.html 11/14 -> 14/14,
/dom/abort/abort-signal-any.any.html 11/14 -> 14/14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:42:51 +02:00
Karl Seguin cfe771e776 Merge pull request #2938 from lightpanda-io/fix-debug
ooops: remove stray debug print
2026-07-14 15:39:36 +08:00
Karl Seguin 15744bca37 ooops: remove stray debug print 2026-07-14 15:15:10 +08:00
Karl Seguin 93e86cbe6a Merge pull request #2936 from lightpanda-io/silence-expected-test-errors
test: Filter out expected error messages from tests
2026-07-14 15:10:31 +08:00
Karl Seguin 3b266359d2 Merge pull request #2937 from lightpanda-io/support-large-content-length
http: u32 -> usize for [very] large content length
2026-07-14 15:10:21 +08:00
Karl Seguin d8b2551baf Merge pull request #2911 from lightpanda-io/eventsource
webapi: Add EventSource
2026-07-14 14:45:45 +08:00
Karl Seguin 0f589567f9 update for new v8 global management 2026-07-14 14:21:44 +08:00
Karl Seguin 8ad4bd9015 webapi: Add EventSource
The EventSource API only requires a simple line-based parser. It's pretty
straightforward. It's RC'd by v8 and itself, but also by scheduled tasks (e.g.
to reconnect).

The bigger change is to the HttpClient to support streaming requests. This
changes to things:
1 - A transfer doesn't only deliver() once
2 - A transfer can be in the multi while delivering

Neither of these are surprising, but they both add complexity.
2026-07-14 14:19:27 +08:00
Karl Seguin cebd8f15f6 Merge pull request #2904 from lightpanda-io/metrics
ops: Add prometheus metrics
2026-07-14 14:17:13 +08:00
Karl Seguin a0549b072d http: u32 -> usize for [very] large content length
There's 1 WPT test that returns a 8GB value content-length (testing failures)
and the u32 limit fails to parse it and makes it as though no content-length
header exists.
2026-07-14 14:09:01 +08:00
Karl Seguin 46e90a984f zig fmt 2026-07-14 11:55:59 +08:00
Karl Seguin 10e91d0503 test: Filter out expected error messages from tests
Please enter the commit message for your changes. Lines starting
2026-07-14 11:50:22 +08:00
Karl Seguin 1ac4435660 extend test buffer size 2026-07-14 11:45:17 +08:00
Karl Seguin f0784de015 add http metrics 2026-07-14 11:32:52 +08:00
Karl Seguin 4d81e48e44 ops: Add prometheus metrics
Adds a /metrics endpoint in serve mode. Can be disabled via --disable-metrics.

In fetch, dumps the metrics if --metrics is specified (defaults to false).

None of the metrics being collected are on a hot path, so they're just always
collected using atomic operations (i.e. no `if (!enabled) return;`). Because
the operations are cheap and infrequent enough not to matter.

The one place I want to add more metrics is in HttpClient (bytes, status,
counts, ...) but after the pending HttpClient-related PR is merged.

./lightpanda fetch --log-level fatal --metrics "https://lightpanda.io/"

```
build_info{version="1.0.0-dev.7837+70493ce35"} 1
cdp_connections_total 0
cdp_connection_limit_total 0
cdp_active_connections 0
cdp_commands_total 0
cdp_unknown_commands_total 0
js_heap_limits_total 0
script_errors_total 0
arena_hit_total{size="tiny"} 908
arena_hit_total{size="small"} 35
arena_hit_total{size="medium"} 0
arena_hit_total{size="large"} 27
arena_miss_total{size="tiny"} 193
arena_miss_total{size="small"} 31
arena_miss_total{size="medium"} 5
arena_miss_total{size="large"} 23
navigate_total{type="page"} 1
navigate_total{type="iframe"} 0
navigate_total{type="popup"} 0
js_heap_size_bytes_bucket{le="4194304"} 0
js_heap_size_bytes_bucket{le="8388608"} 0
js_heap_size_bytes_bucket{le="16777216"} 1
js_heap_size_bytes_bucket{le="33554432"} 1
js_heap_size_bytes_bucket{le="67108864"} 1
js_heap_size_bytes_bucket{le="134217728"} 1
js_heap_size_bytes_bucket{le="268435456"} 1
js_heap_size_bytes_bucket{le="536870912"} 1
js_heap_size_bytes_bucket{le="+Inf"} 1
js_heap_size_bytes_sum 11223040
js_heap_size_bytes_count 1
```
2026-07-14 09:17:14 +08:00
Karl Seguin 56c5701b7b Merge pull request #2925 from lightpanda-io/default-max-response-size
http: Change default max http size to 1GB (from unlimited)
2026-07-14 09:08:50 +08:00
Karl Seguin 3615c16589 Merge pull request #2934 from lightpanda-io/docs-fix-readme-typo
docs: fix typo in installation command for nightly build
2026-07-14 07:59:35 +08:00
Karl Seguin bd6706b2b4 Merge pull request #2923 from lightpanda-io/domrect
webapi: Improve DOMRect
2026-07-14 07:59:06 +08:00
Karl Seguin 8ccb9b2948 Merge pull request #2922 from lightpanda-io/custom-element-extend-html-interfaces
webapi: Allow CustomElements to extend any HTMLElement interfaces.
2026-07-14 07:58:45 +08:00
Karl Seguin c5f26cd7a0 Merge pull request #2924 from lightpanda-io/xhr-tweaks
webapi, minor: XHR Response-Type and invalid JSON parsing tweaks
2026-07-14 07:58:18 +08:00
Karl Seguin 2d325ce8b3 Merge pull request #2926 from lightpanda-io/set-outerHTML-document-protection
minor, webapi: Prevent outerHTML of document's element's from being w…
2026-07-14 07:57:37 +08:00
Karl Seguin 70278c0091 'GB' -> 'GiB' 2026-07-14 07:57:11 +08:00
Adrià Arrufat fb36c20eb5 docs: fix typo in installation command for nightly build 2026-07-13 23:05:46 +02:00
Adrià Arrufat 830d90adc8 Merge pull request #2915 from lightpanda-io/agent-streaming
feat(agent): add assistant text streaming
2026-07-13 19:19:58 +02:00
Karl Seguin 1ae105f08f Merge pull request #2933 from lightpanda-io/fix-custom-element-recursion
fix: Custom-element constructor parser endless recursion
2026-07-13 23:40:36 +08:00
Karl Seguin d4da7e1352 Merge pull request #2931 from lightpanda-io/ResizeObserver-Observer-Default
webapi: ResizeObserver.observer box option default
2026-07-13 23:28:18 +08:00
Karl Seguin ca1e80a397 fix: Custom-element constructor parser endless recursion
In a custom element, when this.innerHTML = '....' is called, we need to be
careful to prevent endless recursion. The html5ever callback used to determine
the context element should not invoke the custom-element constructor, else we'll
enter an endless loop.

This also fixes an ungating problem added with the new HttpClient when a
waitForImport can block forever.

Both issues were see on a WooCommerce site - though the HttpClient is only
due to an earlier HttpClient refactor.
2026-07-13 23:23:00 +08:00
Adrià Arrufat f06681587d build: update zenai dependency 2026-07-13 14:47:09 +02:00
Adrià Arrufat 8c575e2dc8 Merge branch 'main' into agent-streaming 2026-07-13 14:38:38 +02:00
Karl Seguin 9f47b28666 webapi: ResizeObserver.observer box option default
This should default to "content-box". Not that the value is important, but
without the default, we fail if an empty object is passed.
2026-07-13 20:30:43 +08:00
Karl Seguin 43b55b8013 http: Change default max http size to 1GB (from unlimited) 2026-07-13 18:55:05 +08:00