Commit Graph

8321 Commits

Author SHA1 Message Date
Karl Seguin
eab33428fb zig fmt 2026-07-15 11:42:18 +08:00
Karl Seguin
09a4092634 use StaticStringMap for lookup 2026-07-15 11:32:39 +08:00
Francis Bouvier
46e9497d35 webapi: scroll-blocking listeners are passive by default on root targets
Fixes the 32 failing tests in WPT /dom/events/passive-by-default.html
(68/100 -> 100/100): per the DOM spec's "default passive value",
touchstart, touchmove, wheel and mousewheel listeners added to the
Window, the Document, or the html or body elements default to
{passive: true} (so their preventDefault is ignored), unless the
options explicitly say {passive: false}.

addEventListener's options dictionary now keeps `passive` optional so
an absent or undefined member can fall back to the default passive
value computed from the event type and target; an explicit boolean
keeps its meaning.

Coverage: /dom/events/passive-by-default.html 68/100 -> 100/100. No
regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:51:11 +08:00
Francis Bouvier
2c64c7fdbe webapi: real clicks on disabled form controls dispatch nothing
Fixes the "Real clicks on disabled elements must not dispatch events"
subtest of WPT /dom/events/Event-dispatch-on-disabled-elements.html
(verified in isolation): a testdriver click on a disabled
button/input/select/textarea must not fire any pointer or mouse events,
while a click on the re-enabled element must. WebDriver.click now
checks the disabled state like HTMLElement.click does.

The file's overall score is unchanged (4/9) because its four CSS
transition/animation subtests hang awaiting animation events (no CSS
animation engine in the renderless browser) and, being sequential
promise_tests, also mask this subtest's result in the full run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:51:11 +08:00
Francis Bouvier
6587778c12 webapi: spec-conformant click activation target selection
Fixes 3 failing tests in WPT /dom/events/Event-dispatch-click.html
(29/33 -> 32/33) and both tests in /dom/events/no-focus-events-at-
clicking-editable-content-in-link.html (1/2 -> 2/2).

- Only a MouseEvent click is an activation event.
- The activation target is the nearest inclusive ancestor with
  activation behavior (ancestors only for bubbling events), shared by
  ActivationState and handleClick via findClickActivationTarget.
- Clicks on editable content don't activate enclosing links; mousedown
  focuses the editing host (CDP and WebDriver paths).
- Interactive submission of a disconnected form does nothing; the
  programmatic form.submit() stays exempt per HTML 'cannot navigate'.

The remaining Event-dispatch-click.html failure needs javascript: URL
navigation.

Coverage: Event-dispatch-click.html 29/33 -> 32/33,
no-focus-events-at-clicking-editable-content-in-link.html 1/2 -> 2/2.
No regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:51:11 +08:00
Francis Bouvier
f72cdf33cf webapi: retarget relatedTarget and reset shadow-tree targets pre-dispatch
Fixes 5 failing tests across WPT /dom/events/relatedTarget.window.html
(0/6 -> 3/6) and /dom/events/shadow-relatedTarget.html (0/2 -> 2/2).
The remaining 3 relatedTarget.window.html subtests also fail in Chrome
stable (verified on wpt.fyi), so they are out of scope.

Per the DOM dispatch algorithm:

- The event's relatedTarget is retargeted against the dispatch target
  when dispatch starts (Event gains relatedTargetPtr() to reach the
  MouseEvent/FocusEvent storage generically), so listeners and
  post-dispatch reads observe e.g. the shadow host instead of a node
  inside a shadow tree.
- The "clear targets" decision (reset target and relatedTarget to null
  when they would expose nodes whose root is a shadow root) is now
  computed when the event path is built — on the pre-dispatch tree —
  instead of after dispatch, so listeners moving nodes out of the
  shadow tree during dispatch can't defeat it, and it now also applies
  to relatedTarget, not just target.

Coverage: /dom/events/relatedTarget.window.html 0/6 -> 3/6 (Chrome
parity), /dom/events/shadow-relatedTarget.html 0/2 -> 2/2. No
regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:51:10 +08:00
Karl Seguin
64b4276cfa Merge pull request #2947 from lightpanda-io/dom-pr-06-window-event-createevent
webapi: window.event rules, Window/Document handlers, createEvent, timeStamp
2026-07-15 10:49:57 +08:00
Karl Seguin
54ae8800b2 Use Window frame to set click listener, not invocation frame
Same with document

Use existing `rootIsShadowRoot` helper in EventManager
2026-07-15 10:24:22 +08:00
Francis Bouvier
40fa69ecf6 webapi: Event.timeStamp relative to the performance time origin
Fixes all 4 failing tests in WPT
/dom/events/Event-timestamp-high-resolution.html: event.timeStamp
returned a raw monotonic-clock value (milliseconds since boot) instead
of a DOMHighResTimeStamp sharing performance.now()'s time origin.

- Events now record their creation time with the same (already
  coarsened) clock Performance uses, and the timeStamp getter reports
  it in milliseconds relative to the time origin.
- Per spec the origin is the *event's relevant global*'s: the JS Event
  constructor captures the creating realm's origin so cross-realm reads
  (Event-timestamp-cross-realm-getter.html) stay correct; events
  without a captured origin fall back to the accessing realm's, which
  is the creating realm in all other paths.

Coverage: /dom/events/Event-timestamp-high-resolution.html 0/4 -> 4/4,
Event-timestamp-cross-realm-getter.html stays 1/1. No regressions
across /dom/events; Event-dispatch-single-activation-behavior.html now
runs (0/0 crash -> 3/132).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:39:16 +08:00
Francis Bouvier
ad9e1d8dc6 webapi: uninitialized-event dispatch throws; more createEvent aliases
Fixes the 20 failing tests in WPT /dom/events/EventTarget-
dispatchEvent.html (5/25 -> 25/25), all of the form "If the event's
initialized flag is not set, an InvalidStateError must be thrown".

Per the DOM spec, an event created via document.createEvent has its
initialized flag unset until one of the legacy init*Event methods runs,
and dispatchEvent must throw an InvalidStateError for it. Lightpanda
dispatched such events happily. Event now carries an _initialized flag:
true by default (constructor-created and internal events), cleared by
document.createEvent, and set by initEvent, initCustomEvent,
initUIEvent, initMouseEvent, initKeyboardEvent, initCompositionEvent
and initTextEvent. EventTarget.dispatchEvent rejects uninitialized
events.

document.createEvent also gains the spec-required aliases it was
missing: DragEvent, HashChangeEvent and SVGEvents, plus
BeforeUnloadEvent, DeviceMotionEvent, DeviceOrientationEvent and
StorageEvent mapped to a plain Event (logged as not_implemented) until
those interfaces exist.

Coverage: /dom/events/EventTarget-dispatchEvent.html 5/25 -> 25/25. No
regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:39:16 +08:00
Francis Bouvier
a0418aed5c webapi: support onclick property handler on Window and Document
Fixes the failing test in WPT /dom/events/handler-count.html?window and
?document (1/2 -> 2/2 each): setting `window.onclick` or
`document.onclick` did nothing, because neither interface exposed the
accessor — the assignment created an inert expando and the handler
never fired for clicks bubbling up from the page.

Both now store the handler in the frame's attribute-listener map (the
same mechanism as element and ShadowRoot property handlers), which the
dispatch propagation path already consults for any event target.
Non-callable values clear the handler per [LegacyTreatNonObjectAsNull].

Coverage: /dom/events/handler-count.html?window 1/2 -> 2/2,
/dom/events/handler-count.html?document 1/2 -> 2/2. No regressions
across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:39:16 +08:00
Francis Bouvier
86f9059f3f webapi: window.event shadow-tree rule; property handlers on direct dispatch
Fixes the 3 failing tests in WPT /dom/events/event-global.html
(5/8 -> 8/8):

- "window.event is undefined if the target is in a shadow tree" (and
  the window.onerror variant): per the DOM invoke algorithm, the
  window's current event is only set while invoking listeners whose
  invocation target is not in a shadow tree. window.event was set once
  for the whole dispatch; it is now set per invocation target (in
  dispatchPhase and around inline handler calls), left undefined for
  targets whose root is a shadow root.
- "window.event is set to the current event, which is the event passed
  to dispatch (2)": script-dispatched events on non-node targets only
  ran addEventListener listeners. Property event handlers now fire too:
  XMLHttpRequestEventTarget resolves its on* fields by event type, and
  the Window's handler fields are consulted both when an event
  propagates to the window (getInlineHandler) and when one is
  dispatched directly on it.

Coverage: /dom/events/event-global.html 5/8 -> 8/8. No regressions
across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:39:15 +08:00
Karl Seguin
6df58c2318 Merge pull request #2946 from lightpanda-io/dom-pr-05-event-interfaces
webapi: event interface conformance (isTrusted, constructor defaults, handleEvent)
2026-07-15 09:38:40 +08:00
Karl Seguin
d42e8b4d81 Improve unforgeable detection
Collect types with unforgeable upfront, so that detection in the child is only
O(N) over the very small set of types with an unforgeable property.

Remove duplicate comments.
2026-07-15 09:14:29 +08:00
Francis Bouvier
169d3b8314 webapi: report TypeError for non-callable handleEvent listeners
Fixes the 2 remaining failures in WPT
/dom/events/EventListener-handleEvent.html (4/6 -> 6/6): "throws if
`handleEvent` is falsy/truthy and not callable".

Per the Web IDL callback interface invocation steps, when an object
event listener's handleEvent member is not callable, invoking the
listener reports a TypeError to the global (window.onerror / an "error"
event). Lightpanda silently skipped such listeners.

Listener.run's object branch now performs the Get(handleEvent)
explicitly: a throwing getter is reported like a throwing listener, a
non-callable value reports a TypeError, and only a callable one is
invoked (with exceptions reported as before).

Coverage: /dom/events/EventListener-handleEvent.html 4/6 -> 6/6. No
regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:39:51 +08:00
Francis Bouvier
5ebc61907e webapi: accept arbitrary button/location values; legacy keyboard init members
Fixes the 3 remaining failures in WPT
/dom/events/Event-subclasses-constructors.html (46/49 -> 49/49): the
"argument with non-default values" cases for MouseEvent, WheelEvent and
KeyboardEvent.

- MouseEvent.button is a WebIDL short: any value (e.g. 40) is allowed.
  It was stored as the MouseButton enum and construction threw a
  TypeError for values outside the named buttons. The field is now a
  raw i16 (the enum remains for the named constants); same fix in
  WheelEvent, DragEvent and PointerEvent, and in initMouseEvent.
- KeyboardEvent.location is an unsigned long with the same problem
  (Location enum); now stored as a raw u32.
- KeyboardEvent supports the legacy KeyboardEventInit members charCode
  and keyCode: synthetic events report the initialized values (still 0
  by default), while trusted events keep deriving them from the key.
  UIEvent.which now returns keyCode for keyboard events, and clamps
  negative mouse buttons instead of overflowing.

Coverage: /dom/events/Event-subclasses-constructors.html 46/49 ->
49/49. No regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:39:51 +08:00
Francis Bouvier
b309640ab0 webapi: fix UIEvent.view default, initCustomEvent guard, KeyboardEvent flags
Fixes 4 failing tests in WPT /dom/events/Event-init-while-dispatching.html
(1/5 -> 5/5), and as a side effect most of
Event-subclasses-constructors.html (16/49 -> 46/49). Three distinct
bugs, all about synthetic (constructor-created) events not following
the spec's dictionary defaults:

- UIEvent.view defaulted to the window: the constructor stored
  `opts.view orelse frame.window` and the getter fell back to the
  window too, so `new UIEvent("x").view` was never null. Per
  UIEventInit, view defaults to null. The window fallback is kept for
  *trusted* (browser-generated) UI events only, which do target the
  window, so internal dispatch call sites stay unchanged.
- initCustomEvent was missing the "short-circuit while dispatching"
  guard that initEvent and the other legacy initializers have.
- KeyboardEvent's constructor unconditionally forced bubbles,
  cancelable and composed to true (correct only for trusted key events
  per UI Events); synthetic KeyboardEvents now follow the EventInit
  defaults.

The unit tests asserting the old view-defaults-to-window behavior are
updated to expect null.

Coverage: /dom/events/Event-init-while-dispatching.html 1/5 -> 5/5,
/dom/events/Event-subclasses-constructors.html 16/49 -> 46/49. No
regressions across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:39:51 +08:00
Francis Bouvier
0b2cfc35f4 js: support [LegacyUnforgeable] accessors; make Event.isTrusted one
Fixes the failing test in WPT /dom/events/Event-isTrusted.any.html (and
its .worker variant): Object.getOwnPropertyDescriptor(event,
"isTrusted") returned undefined because the accessor lived on
Event.prototype. Per Web IDL, isTrusted is [LegacyUnforgeable]: an own,
non-configurable accessor property of every Event instance, with the
same getter function shared between instances.

- bridge.accessor gains an `unforgeable` option. Such accessors are
  installed on the class's v8 instance template instead of the
  prototype template, so every instance gets an own accessor backed by
  one getter function per context.
- Since v8's Inherit only chains prototype templates, attachClass now
  walks the ancestor chain (via the _proto fields) and re-installs
  ancestors' unforgeable accessors on the subclass's instance template,
  so MouseEvent instances & co. get their own isTrusted as well. The
  accessor installation code is extracted into attachAccessorProperty
  for reuse.
- Event.isTrusted is declared unforgeable and non-deletable.

Coverage: /dom/events/Event-isTrusted.any.html 0/1 -> 1/1,
/dom/events/Event-isTrusted.any.worker.html 0/1 -> 1/1. No regressions
across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:39:50 +08:00
Karl Seguin
2623be4493 Merge pull request #2945 from lightpanda-io/dom-pr-04-dispatch-and-onerror
webapi: dispatch passes, trusted clicks, listener exceptions to onerror
2026-07-15 08:39:04 +08:00
Karl Seguin
b392cbd95a use childIterator to iterate child nodes 2026-07-15 08:04:05 +08:00
Francis Bouvier
f1bdf9baa6 webapi: report listener exceptions to window.onerror
Fixes both failing tests in WPT /dom/events/Event-dispatch-throwing.html
("Throwing in event listener with a single/multiple listeners"): an
exception thrown by an event listener must be reported to the global —
firing window.onerror and an "error" event — while the dispatch
continues with the remaining listeners. Lightpanda only logged the
exception internally, so window.onerror never fired.

- js.Function gains callWithThisRethrow, built on the existing
  callRethrow machinery (_tryCallWithThis with .rethrow = true): a
  thrown JS exception is surfaced as error.TryCatchRethrow past the
  internal TryCatch so an enclosing TryCatch of the caller can observe
  the exception value. js.TryCatch gains exceptionValue() returning
  the raw caught value.
- Listener.run wraps function and handleEvent callbacks in a TryCatch
  and routes the caught exception value to Window.reportError (the
  existing "report the exception" implementation, which invokes
  onerror with the 5-argument form and dispatches the error event).
  This merges with the pre-existing _listeners_did_throw tracking on
  Event (used by IndexedDB): the reporting paths also set the flag.
  Worker globals are left as-is for now.
- Window.reportError now implements the spec's "in error reporting
  mode" guard: an exception thrown while reporting one (e.g. by a
  throwing "error" listener) is not reported again, which would recurse
  without bound.

Coverage: /dom/events/Event-dispatch-throwing.html 0/2 -> 2/2. No
regressions across /dom/events (verified against a baseline build).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 07:59:29 +08:00
Francis Bouvier
cb70a911c7 webapi: add WebDriver.click for trusted testdriver clicks
Fixes 1 failing test in WPT /dom/events/Event-dispatch-redispatch.html:
"Redispatching mouseup event whose default action dispatches a click
event".

The WPT testdriver-vendor shim implemented test_driver.click() with
HTMLElement.click(), which dispatches a single untrusted click event
and no press/release, so the test saw no trusted mouseup at all and an
untrusted click. window.webdriver (the -Dwpt_extensions WebAPI) now
exposes click(element): a full trusted primary-button sequence
(pointerdown, mousedown, pointerup, mouseup, click), dispatched
synchronously so the events are observable when the testdriver promise
resolves. The shim routes test_driver.click through it, like
action_sequence already routes through webdriver.actionSequence.

Note this test (like everything testdriver-based) needs the browser
built with -Dwpt_extensions=true, otherwise window.webdriver is
undefined.

Coverage: /dom/events/Event-dispatch-redispatch.html 3/4 -> 4/4 (with a
wpt_extensions build).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 07:59:29 +08:00
Francis Bouvier
b59400c836 webapi: invoke event target separately for capture and bubble passes
Fixes the failing test in WPT /dom/events/Event-dispatch-handlers-
changed.html ("Dispatch additional events inside an event listener").

At the target, listeners were run in a single pass mixing capture and
bubble listeners, with one snapshot of the listener list. Per the DOM
dispatch algorithm, the target participates in both the capturing and
the bubbling iterations of the event path, and each invoke clones the
listener list separately: a bubble listener added by one of the
target's capture listeners must run during the bubbling pass. The
single pass used one snapshot, so such a listener never ran.

The at-target step now dispatches twice — capture listeners first, then
non-capture listeners — re-fetching the listener list (and taking a new
sentinel snapshot) for the second pass, and honoring stopPropagation /
stopImmediatePropagation between the two.

Coverage: /dom/events/Event-dispatch-handlers-changed.html 0/1 -> 1/1,
/dom/events/EventTarget-dispatchEvent.html 4/25 -> 5/25. No regressions
across /dom/events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 07:59:28 +08:00
Francis Bouvier
27ba864697 webapi: implement cloneNode for Document nodes
Fixes 1 failing test each in WPT /dom/events/Event-dispatch-bubbles-
false.html and Event-dispatch-bubbles-true.html ("In
window.document.cloneNode(true)"): document.cloneNode() threw
NotSupportedError because the .document case of Node.cloneNode was
unimplemented.

Per the DOM spec cloning steps for Document, a new document of the same
type (HTML vs XML) is created with the same URL, and a deep clone
copies the document's children (doctype and document element subtree)
into it, reusing the existing per-child cloning path.

Coverage: /dom/events/Event-dispatch-bubbles-false.html 4/5 -> 5/5,
/dom/events/Event-dispatch-bubbles-true.html 4/5 -> 5/5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 07:59:28 +08:00
Karl Seguin
48750df46a Merge pull request #2944 from lightpanda-io/dom-pr-03-handler-plumbing
webapi: listener option validation, window-reflecting handlers, exception propagation
2026-07-15 07:08:39 +08:00
Karl Seguin
a60eaa724b Merge pull request #2930 from lightpanda-io/popup-origin
webapi: Improve popup window
2026-07-15 07:02:44 +08:00
Karl Seguin
2e9aebc6b1 Merge pull request #2935 from lightpanda-io/robots-skip-non-200-body
robots, mem: don't buffer body on a non-200 robots response
2026-07-15 07:02:28 +08:00
Adrià Arrufat
53e1dbe8e8 agent: clean up terminal and markdown rendering
- Move ANSI escape definitions to `md_term.zig`.
- Fix table cell width calculation using a scratch buffer.
- Ensure `Stream.close` fully resets markdown stream state.
- Extract helper functions for styled output and fence checks.
2026-07-14 23:13:52 +02:00
Adrià Arrufat
d0ce9a02a8 terminal: stream and render markdown incrementally
Introduces an incremental markdown renderer (`md_term.Stream`) to
format streamed assistant responses in real time. Buffers lines,
maintains code fence state across chunks, and temporarily withholds
tables to render them with proper column alignment once complete.
2026-07-14 22:51:43 +02:00
Adrià Arrufat
aacddfdbea Merge branch 'main' into agent-repl-markdown-output 2026-07-14 22:32:26 +02:00
Adrià Arrufat
74536507dd md_term: add markdown table rendering 2026-07-14 22:32:07 +02:00
Adrià Arrufat
d52e56ece8 md_term: support arbitrary nested inline styles
Introduces a stack-threaded `Style` chain to track and re-apply
parent styles upon nested resets. This fixes styling loss in
deeply nested markdown spans.
2026-07-14 22:06:25 +02:00
Adrià Arrufat
97e25b5abc Merge pull request #2929 from lightpanda-io/pandascript-skill
script: generate the PandaScript skill from the tool schemas
2026-07-14 21:53:25 +02:00
Adrià Arrufat
0618515132 terminal: fix nested markdown styles and dim color
- Use a 256-ramp gray for `ansi.dim` to improve readability.
- Track and re-apply active styles in nested markdown spans.
- Collapse extra spaces after list markers.
2026-07-14 21:51:09 +02:00
Rohit
2f4b7ec9ce feat(cdp): emit Page.navigatedWithinDocument for same-document navigations
CDP clients (Playwright/Puppeteer) never saw same-document navigations —
history.pushState / replaceState changed frame.url but fired no CDP event,
so frame.url() went stale and waitForNavigation missed SPA route changes.

Add a frame_navigated_within_document notification, dispatched from
History.pushState/replaceState, and emit Page.navigatedWithinDocument from
the Page domain with frameId, url, and navigationType. Unlike frameNavigated,
this deliberately does NOT send Runtime.executionContextsCleared or
DOM.documentUpdated — the document and its execution context are unchanged,
and clearing them would invalidate the client's live context ids and break
frame.evaluate(). Adds a cdp.frame test.

Scoped to the History-API path; fragment and Navigation-API paths are left
for follow-ups.
2026-07-15 00:40:32 +05:30
Francis Bouvier
0c0e3626f3 webapi: take *Frame in HTMLCollection.getIndexes
Same review pattern as the getNames enumerators: the bridge can inject
*Frame directly, so the manual switch on exec.js.global is unnecessary.
getIndexes was the one enumerator the earlier suggestions missed.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 15:32:43 +02:00
Karl Seguin
9965e59672 Merge pull request #2943 from lightpanda-io/dom-pr-02-htmlcollection-properties
webapi: HTMLCollection & named-collection property semantics
2026-07-14 21:20:24 +08:00
Francis Bouvier
dba25389c6 webapi: window-reflecting handlers target the body's own window
Review follow-up on #2944: the injected `frame` parameter of a bridged
function is the frame of the executing JS context - the caller's realm -
not the frame owning the element. The window-reflecting body/frameset
event handlers (onblur, onerror, onfocus, onload, onresize, onscroll)
used it directly, so a same-origin script reaching into another frame,
e.g. the parent doing

    iframe.contentDocument.body.onblur = cb;

stored the handler on the parent's window instead of the iframe's. Per
the HTML spec these handlers alias the Window of the element's node
document, whichever realm performs the access.

All Body/FrameSet getters and setters now resolve that Window through
the existing Node.ownerFrame (element -> ownerDocument -> frame, falling
back to the caller's frame for detached/frameless documents), via a
shared reflectedWindow helper. The Build hooks (parse-complete,
attributeChange, attributeRemove) do the same, and additionally pass the
owner frame to setWindowReflectingHandlerFromAttribute so attribute
handler bodies are compiled in the element's document's realm.

Unit test added: tests/frames/window_reflecting_handlers.html covers the
IDL property path and the content-attribute path (parse-time and
runtime) across realms, asserting the handler lands on the iframe's
window while the parent's window stays untouched. It fails without this
fix.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 15:19:39 +02:00
Francis Bouvier
d2cc336c74 Update src/browser/webapi/Window.zig
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-14 15:00:58 +02:00
Francis Bouvier
0872b86383 js: propagate pending JS exceptions from argument conversion
Fixes 1 failing test in WPT /dom/events/Event-constructors.any.html
(and its .worker variant): "Event constructors 2", which does
`new Event({ toString: function() { throw test_error; } })` and expects
the exact test_error object to propagate.

When a JS value's conversion runs user code that throws (here, ToString
invoking a throwing toString method), the conversion fails with
error.JsException while the user's exception is pending in the isolate.
The argument-conversion catch folded that into error.InvalidArgument,
and handleError then threw a brand new "TypeError: invalid argument"
over the pending exception, so the script observed the wrong error.

error.JsException is now passed through the argument-conversion catch,
and handleError leaves the isolate untouched for it (like the existing
TryCatchRethrow case), letting the original exception surface.

Coverage: /dom/events/Event-constructors.any.html 13/14 -> 14/14,
/dom/events/Event-constructors.any.worker.html 13/14 -> 14/14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:00:58 +02:00
Francis Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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 Bouvier
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