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>
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>
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.
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.
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
```
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.
Track active connections and shut down sockets during deinit to unblock
pending reads. Drain the job queue properly before stopping the worker.
Also fix `--http-port` references to `--port` in docs and errors.
Event dispatch using a try/catch (tryCallWithThis) so that the EventManager can
decide how to handle the error. Previously, using the `callWithThis` would
result in always logging the error. Now, the EventManager can skip logging
JsExceptions.
error.TryCatchRethrow is used as a control flow (sorry). It signals that an
error has already been throw in v8 and that the method should exit (ultimately,
it ends up being the return value passed to our v8 bridge, which knows to ignore
it). We shouldn't log this as a WARN, it's completely normal that it happens AND
it really contains no meaningful information as-is.
Add an explicit 'default' ResponseType, which mostly behaves like `text`, but
can be used do discriminate between an explicit text and the default value.
Also, on JSON parse error, return null, not an exception.
We had a basic DORect implementation (which probably covered more cases). This
commit extends the implementation to be more complete:
- Add DOMRectReadOnly (which DOMRect inherits from)
- Add StructuredClone support to DOMRect and DOMRectReadOnly
- Make DOMRect and DOMRectReadOnly usable from Worker
DOMRect can be on the hot path with IntersectionObserver, so we maintain a
value-based data object (DOMRect.Data) for internal use (things returned to js
always have to be heap allocated (and always were, we're just more explicit
about this part now)).
Pervious, extension was limited to HtmlElement. CustomElements can now extend
any html element interface.
The implementation isn't great. This is a special case, and rather than coming
up with some abstract way to define this in bridge, we make the Snapshot
aware of this specific case directly.
`stream_active` relied on every runTools exit path balancing pause with
`endStreamedText`. If one is ever missed, a stale-true flag skips the
spinner pause on the next turn, interleaving frames with streamed text.
Reset it alongside `streamed_text` so the invariant is local.