Commit Graph

8347 Commits

Author SHA1 Message Date
Karl Seguin
acf7ea5115 cdp: support explicit browser context session_id
Goal is to better support attachToBrowserTarget by giving the browser context
its own session_id. Without this calls to attachToBrowserTarget clobber the
bc.session_id which is the page/target session_id.
2026-07-16 10:38:37 +08:00
Francis Bouvier
5c79bc4804 webapi: parser insertions into the document notify mutation observers
Fixes "parser insertion mutations" in WPT
/dom/nodes/MutationObserver-document.html (1/4 -> 2/4): a script running
during the initial document parse can observe the document with
{subtree, childList} and must receive records for the nodes the parser
inserts after it. We suppressed all notifications for main-document
parser insertions.

Parser insertions now notify per inserted node. Fragment parses
(innerHTML et al.) stay silent, since Node.setHTML queues a single
combined "replace all" record. There is no overhead in the common case:
notifyChildInserted is gated on any observer existing, which is never
true during a parse unless an earlier script registered one.

The remaining two subtests need synchronous execution of scripts
inserted by other scripts during parsing (record batching across the
two) and parser insertions into a parent removed mid-parse; both are
script-scheduling semantics, not observer plumbing.

Coverage: /dom/nodes/MutationObserver-document.html 1/4 -> 2/4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:29:14 +08:00
Francis Bouvier
c6b592e782 webapi: spec mutation records for fragment insertion and replaceChild
Fixes the remaining 8 failing subtests of WPT
/dom/nodes/MutationObserver-childList.html (30/38 -> 38/38, fully
green):

- Inserting a DocumentFragment emitted one record per child for both
  the removals from the fragment and the insertions into the parent.
  Per the DOM insert algorithm observers get exactly two records: one
  on the fragment with all removedNodes and one on the parent with all
  addedNodes. appendAllChildren/insertAllChildrenBefore now share
  moveAllChildren, which suppresses per-node notification and queues
  the two combined records (or none, in the silent mode replaceChild
  uses).
- replaceChild emitted separate insertion and removal records; the
  "replace" algorithm queues one combined record {addedNodes,
  removedNodes}. Removing the new child from its previous position
  still notifies separately (internal replacement), and replacing a
  node with itself reports a removal record followed by an addition
  record with no tree change, matching browsers.
- Range.insertNode into a Text container rebuilt the text as new
  before/after nodes (replaceChild + two insertBefores, three records).
  It now uses splitText and inserts before the second half: two
  records, and live ranges are updated by the split as a bonus
  (Range-mutations-replaceChild 37/60 -> 39/60).

The unit test mutation_observer/childlist.html asserted the old
two-record replaceChild; updated to the spec's single combined record.

Coverage:
- /dom/nodes/MutationObserver-childList.html 30/38 -> 38/38 (fully green)
- /dom/ranges/Range-mutations-replaceChild.html 37/60 -> 39/60

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:29:13 +08:00
Francis Bouvier
91a2bbcfe3 webapi: Attr.ownerDocument follows the owning element
Fixes the failing subtest of WPT /dom/nodes/Node-mutation-adoptNode.html
("Adopting an element ... owner docs of it's attributes"): after
adopting an element into another document, its attribute nodes'
ownerDocument must report the new document.

Attribute nodes are parent-less, so ownerDocument fell through to the
detached-node fallback (the per-frame owner map / main document), which
never changes on adoption. An attribute node with an owning element now
delegates to that element's ownerDocument; detached Attr nodes keep the
old resolution.

Coverage:
- /dom/nodes/Node-mutation-adoptNode.html 1/2 -> 2/2 (fully green)
- /dom/nodes/attributes-namednodemap-cross-document.window.html 0/2 -> 1/2

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:29:13 +08:00
Francis Bouvier
bf3c858b35 webapi: spec-ordered, complete pre-insert validity for node insertion
Fixes the remaining failures of WPT /dom/nodes/Node-insertBefore.html
(16/40), Node-replaceChild.html (15/29) and Node-appendChild.html
(9/11) - all three are now fully green.

The insertion validity checks were incomplete and ran in the wrong
order:

- The reference-child NotFoundError check ran before the parent-type
  and cycle checks, but per "ensure pre-insert validity" the
  HierarchyRequestError checks for the parent kind and for node being
  an inclusive ancestor of parent come first.
- Inserting a Document node was not rejected.
- DocumentFragment insertion into a document skipped validation
  entirely: fragments with multiple elements, with a Text child, or
  whose element joins an existing document element now throw.
- The doctype rules were missing: a second doctype, a doctype inserted
  after an element, and an element inserted before a doctype all throw,
  with replaceChild's variants (the replaced child doesn't count).
- replaceChild threw HierarchyRequestError for a child with the wrong
  parent; the spec requires NotFoundError (unit test updated
  accordingly).
- insertBefore's reference-child argument is required-but-nullable per
  WebIDL: omitting it is now a TypeError (js.Nullable), while passing
  null still appends.

appendChild/insertBefore/replaceChild now share ensurePreInsertValidity
(insert and replace modes); replaceChildren keeps its replace-all
validation.

Coverage:
- /dom/nodes/Node-insertBefore.html 16/40 -> 40/40 (fully green)
- /dom/nodes/Node-replaceChild.html 15/29 -> 29/29 (fully green)
- /dom/nodes/Node-appendChild.html 9/11 -> 11/11 (fully green)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:29:13 +08:00
Karl Seguin
581eafcf7e Merge pull request #2954 from lightpanda-io/dom-pr-13-traversal
webapi: TreeWalker, NodeFilter and NodeIterator spec conformance
2026-07-16 10:28:26 +08:00
Karl Seguin
fe5012c482 fix potential UAF 2026-07-16 09:55:40 +08:00
Karl Seguin
268a487719 Merge pull request #2975 from lightpanda-io/eventsoruce-relative
Fix: Allow EventSource with relative URLs
2026-07-16 08:30:34 +08:00
Karl Seguin
3e51547350 Merge pull request #2968 from rohitsux/feat/cdp-get-partial-ax-tree
feat(cdp): implement Accessibility.getPartialAXTree
2026-07-16 08:16:58 +08:00
Karl Seguin
8ec64547e7 Fix: Allow EventSource with relative URLs 2026-07-16 08:06:43 +08:00
Karl Seguin
10c46aabc8 Merge pull request #2921 from lightpanda-io/request-finalizer
mem: Make Request, URLSearchParams finalized
2026-07-16 07:53:46 +08:00
Karl Seguin
aa8bcb67e1 log not implemented on fetchRelatives == true (default) 2026-07-16 07:53:01 +08:00
Francis Bouvier
20df3d5820 webapi: implement NodeIterator pre-removing steps
Fixes WPT /dom/traversal/NodeIterator-removal.html (0/23 -> 23/23) and
/dom/nodes/moveBefore/moveBefore-nodeiterator.html: removing a node
that is an inclusive ancestor of a live NodeIterator's reference node
must move the reference per the DOM pre-removing steps. We never
adjusted iterators, so the reference kept pointing into the detached
subtree.

The frame keeps an intrusive list of live NodeIterators (mirroring
_live_ranges; iterators are slab-allocated for the frame lifetime, so
they are never unlinked) and Frame.removeNode runs the steps while the
tree is still intact:

- removing the root or an ancestor of the root leaves the iterator
  untouched (matching browsers and the WPT model);
- with the pointer before the reference, the reference moves to the
  first node following the removed subtree, if any;
- otherwise (or when there is no such node) it moves to the node
  immediately preceding the removed node in tree order, clearing the
  before-pointer in the fallthrough case.

Coverage:
- /dom/traversal/NodeIterator-removal.html 0/23 -> 23/23 (fully green)
- /dom/nodes/moveBefore/moveBefore-nodeiterator.html 0/1 -> 1/1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:49:28 +08:00
Francis Bouvier
f64952bd35 webapi: NodeFilter follows WebIDL callback interface semantics
Fixes the 5 failing subtests of WPT
/dom/traversal/TreeWalker-acceptNode-filter.html (7/12 -> 12/12):

- A filter object was converted eagerly at createTreeWalker time, so an
  object without a callable acceptNode threw "invalid argument" at
  creation. Per WebIDL any object converts to the NodeFilter callback
  interface; the TypeError belongs at invocation time.
- The acceptNode member was cached at conversion. Per "call a user
  object's operation" it must be looked up with a fresh Get on every
  traversal, rethrowing errors from a throwing getter.
- The callback was invoked with the default this; the spec requires the
  filter object itself as the this value.

NodeFilter now stores the raw function or object (js.Object.Global) and
performs the per-invocation lookup, callability check and this-binding
in acceptNode.

Coverage: /dom/traversal/TreeWalker-acceptNode-filter.html 7/12 ->
12/12 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:49:27 +08:00
Francis Bouvier
7581db26d4 webapi: TreeWalker methods throw when called from their own filter
Fixes the last failing subtest of WPT /dom/traversal/TreeWalker.html
("Recursive filters need to throw"): per the DOM traversal "filter"
algorithm, a NodeFilter that re-enters the walker (calling parentNode()
etc. from inside the callback) must get an InvalidStateError.

DOMTreeWalker tracks an active flag around the filter invocation and
throws InvalidStateError when a traversal starts while it is set.

Coverage: /dom/traversal/TreeWalker.html 760/761 -> 761/761 (fully
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:49:27 +08:00
Francis Bouvier
ee97f3b888 webapi: TreeWalker sibling traversal follows the spec algorithm
Fixes WPT /dom/traversal/TreeWalker-previousSiblingLastChildSkip.html
and 59 subtests of TreeWalker.html (701/761 -> 760/761):
TreeWalker.previousSibling()/nextSibling() only scanned the current
node's direct siblings, but the spec's "traverse siblings" algorithm
also:

- descends into a skipped (FILTER_SKIP) sibling's children - only a
  rejected sibling excludes its whole subtree - so from B2 with B1
  skipped, previousSibling() must return B1's last child;
- climbs to the parent when the siblings are exhausted and continues
  from the parent's siblings, stopping at the root or at an accepted
  parent.

Both directions now share the spec's traverseSiblings implementation.

Coverage:
- /dom/traversal/TreeWalker-previousSiblingLastChildSkip.html 0/1 -> 1/1
  (fully green)
- /dom/traversal/TreeWalker.html 701/761 -> 760/761

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 07:49:27 +08:00
Karl Seguin
df94585d0a Merge pull request #2953 from lightpanda-io/dom-pr-12-text-and-tokens
webapi: class-token whitespace, table row collections, wholeText, UTF-16 offsets
2026-07-16 07:46:32 +08:00
Karl Seguin
9e52b779a0 Merge pull request #2971 from lightpanda-io/cdp-expose-lightpanda-version
cdp: expose lightpanda version
2026-07-16 07:28:47 +08:00
Karl Seguin
978636f7be Extract common logic
Add infallible utf16OffsetToUtf8 variant (utf16OffsetToUtf8Floor)
2026-07-16 07:25:34 +08:00
Karl Seguin
84c72eae7f Merge pull request #2973 from lightpanda-io/htmlcollection-rc-size
crash: increase HTMLCollection's RC from u8 to u32
2026-07-15 23:07:23 +08:00
Karl Seguin
d2cb58a7d1 crash: increase HTMLCollection's RC from u8 to u32 2026-07-15 22:42:14 +08:00
Pierre Tachoire
8222e624dc cdp: emit non-UTF-8 header values as Latin-1 strings, not byte arrays
std.json serializes []u8 that isn't valid UTF-8 as a JSON array of
numbers, so headers like "expires: mié, 15 jul 2026 ..." (Latin-1 0xE9)
appeared as byte arrays in Network.responseReceived. Stream such values
through the JSON writer with Latin-1 -> UTF-8 transcoding, matching
Chrome's behavior for DevTools.
2026-07-15 15:58:48 +02:00
Pierre Tachoire
447a340366 expose LP.version including Lightpanda's version 2026-07-15 15:38:13 +02:00
Pierre Tachoire
fb6e0b751b expose Lightpanda-Version into /json/version endpoint 2026-07-15 15:37:45 +02:00
Karl Seguin
4365e01530 table deleteRow without full collection + add tests 2026-07-15 20:56:28 +08:00
Muki Kiboigo
9b6e203df8 add dispose event to NavigationHistoryEntry 2026-07-15 05:54:28 -07:00
Rohit
d75ef37d42 feat(cdp): implement Accessibility.getPartialAXTree
The Accessibility domain handled getFullAXTree and queryAXTree but not
getPartialAXTree, so a client asking for the accessibility subtree rooted at
a specific node got UnknownMethod. Tools like Playwright/Puppeteer and
axe-core use getPartialAXTree for scoped a11y snapshots.

Add it by mirroring the existing methods: resolve the node via dom.getNode
(like queryAXTree) and emit its unfiltered AX subtree via bc.axnodeWriter
(like getFullAXTree). fetchRelatives is accepted for protocol compatibility;
subtree emission matches queryAXTree's scope. Adds tests for the missing-id
and unknown-id error paths.
2026-07-15 16:36:20 +05:30
Adrià Arrufat
98134feb7a prompt_assist: use explicit PathMatchIterator.init 2026-07-15 12:34:35 +02:00
Adrià Arrufat
1d8a0b9fc4 agent: trim narrative comments from the terminal split 2026-07-15 12:20:56 +02:00
Karl Seguin
9d138c1780 Merge pull request #2952 from lightpanda-io/dom-pr-11-mutation-records
webapi: insert-adjacent semantics; combined mutation records
2026-07-15 18:18:37 +08:00
Adrià Arrufat
f462223277 agent: tighten the seams the terminal split left behind
- attachCompleter now takes the CompletionSource: Agent no longer
  reaches through terminal.assist to configure what it just attached,
  and attach/configure can't be sequenced wrong.
- history_paths flows through one channel: attach() loads the initial
  history from the same State field modeCallback swaps from, instead of
  setupRepl receiving a second copy that had to agree.
- all_names + closestCommand move to SlashCommand.zig, the command
  registry - prompt_assist consumes it like everyone else and
  Terminal's closestCommand re-export disappears.
- picker treats a tty that refuses raw mode as NotInteractive and
  degrades to the line prompt instead of leaking termios errnos to
  callers that can only blanket-catch.
- kitty keyboard flags become named ansi.zig constants shared by
  Terminal.readLine and picker's RawTerminal.
- skipWhitespace was std.mem.indexOfNonePos; single-use style consts
  inline into the style table; drop the dead CompletionSource default
  and doc-comment splice in Spinner.
2026-07-15 12:16:53 +02:00
Adrià Arrufat
6845c3a542 agent: use decl-literal init syntax in picker and prompt_assist 2026-07-15 12:05:34 +02:00
Adrià Arrufat
b4c971799c agent: share one filesystem scan between path completion and ghost hint
addPathCompletions and ghostPathFirstMatch were ~20-line twins (split
dir/base, openDir, iterate, prefix-match, '/' suffix for directories).
A fix landing in one copy — hidden-file filtering, the shared
symlink-to-directory suffix gap — would make Tab completion and the
ghost hint disagree. Both now consume one PathMatchIterator.
2026-07-15 11:56:27 +02:00
Adrià Arrufat
2d43d5103e agent: derive prompt style registration and kind mapping from one table
ic_style_def registration lived in setup while the Kind->style switch
lived 600 lines away in IcSink: adding a js_highlight.Kind
compile-errored the sink but silently skipped registration, rendering
the new kind unstyled with no test able to catch it (isocline is
live-only). One table now drives both; an unmapped or doubly-mapped
kind is a compile error.
2026-07-15 11:53:56 +02:00
Francis Bouvier
ecf7dd2137 webapi: use the local arena for Table.collectRows
Review pattern from #2943: prefer the local arena for scratch-only
allocations. The collected row list is only read before deleteRow's
removeChild call - the one point that can re-enter JS - so it does not
need to survive a nested callback.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-15 17:53:10 +08:00
Francis Bouvier
20299e4b5c webapi: node length and Range text offsets are UTF-16 code units
Fixes the remaining failures of WPT /dom/ranges/Range-selectNode.html
(288/292) and improves several other /dom/ranges files: for
CharacterData, the DOM "node length" is the data length in UTF-16 code
units, but Node.getLength returned the UTF-8 byte length, so
selectNodeContents() on text containing non-ASCII set endOffset to the
byte count (25 instead of 17 in the test's fixture).

Node.getLength now defers to CData.getLength (already UTF-16). Since
Range/Selection offsets are therefore UTF-16 units, the Range routines
that slice the UTF-8 data by offset (insertNode's text split,
deleteContents, cloneContents, toString) convert them with
utf16OffsetToUtf8 (clamped to the end) instead of using them as byte
indices, which would cut surrogate pairs and multi-byte sequences
apart.

Coverage:
- /dom/ranges/Range-selectNode.html 288/292 -> 292/292 (fully green)
- /dom/ranges/Range-deleteContents.html 95/125 -> 105/125
- /dom/ranges/Range-extractContents.html 128/187 -> 141/187
- /dom/ranges/Range-cloneContents.html 168/187 -> 171/187

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:53:10 +08:00
Francis Bouvier
f197fd6388 webapi: Text.wholeText spans contiguous Text siblings
Fixes WPT /dom/nodes/Text-wholeText.html: wholeText must return the
concatenated data of the contiguous exclusive Text nodes (adjacent Text
siblings on both sides of the node) in tree order; we returned only the
node's own data.

The old own-data behavior is what every internal caller (semantic tree,
markdown dump, AX names, textarea default value, textContent) actually
wants, since they iterate the text nodes themselves; those now use the
new Text.ownData helper, and getWholeText implements the spec walk
(single-node case stays allocation-free).

Coverage: /dom/nodes/Text-wholeText.html 0/1 -> 1/1 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:53:10 +08:00
Francis Bouvier
8b1baec769 webapi: class tokens split on all ASCII whitespace; table row collections
Fixes 7 failing WPT /dom/nodes/getElementsByClassName-*.htm files:

- Class attribute matching (getElementsByClassName and .class
  selectors) treated only the space character as a token separator, so
  class="a\nb" or class="a\tb" never matched "a". Per the spec the
  separators are ASCII whitespace (tab, LF, FF, CR, space);
  classAttributeContains now accepts all of them at token boundaries
  (deliberately not std.ascii.isWhitespace, which would also accept
  vertical tab). Fixes getElementsByClassName-02/04/15/22/25.

- getElementsByClassName-20 exercised table.tBodies[0].rows[0].cells[0]
  and -21 table.deleteRow(1); the table interfaces were bare stubs.
  Added HTMLTableElement.tBodies (child tbody collection),
  HTMLTableSectionElement.rows (child tr collection),
  HTMLTableRowElement.cells (child td/th collection, a new `cells`
  NodeLive mode since child_tag filters a single tag), and
  HTMLTableElement.deleteRow with the spec's row ordering (thead rows,
  then table/tbody rows in tree order, then tfoot rows), -1 meaning the
  last row, and IndexSizeError for out-of-range indices.

Coverage: getElementsByClassName-02/04/15/20/21/22/25.htm each
0/1 -> 1/1 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:53:10 +08:00
Karl Seguin
7945df66ba more local_arena, less pub 2026-07-15 17:52:24 +08:00
Adrià Arrufat
673b16528f agent: extract prompt assistance into prompt_assist.zig
Completion, ghost hints, and prompt highlighting (~640 lines) were
cohabiting with Terminal's readline lifecycle and output printing. They
now live in prompt_assist.zig, which owns the isocline callbacks, the
ps-* style palette, and the slash-command name table. The C callbacks
read a small prompt_assist.State (js_mode, completion_source,
history_paths) embedded in Terminal, replacing the three loose fields.
Pure code motion otherwise; the valueAt/renderSchemaHint tests move
with their code and stay in the suite via Terminal's test hook.
2026-07-15 11:50:44 +02:00
Pierre Tachoire
0562e774f0 Merge pull request #2966 from lightpanda-io/blob-lifetime
uaf: Dupe blobs before using them, or else risk revokeObjectURL
2026-07-15 11:49:43 +02:00
Adrià Arrufat
f48498073d agent: extract the choice picker into picker.zig
The numbered/interactive picker (ChoiceState, RawTerminal, render loop)
was ~215 self-contained lines inside Terminal.zig. It runs before - or
without - the isocline REPL (provider selection during setup), so
settings.zig no longer imports the isocline-configured Terminal at all.
Pure code motion; the ChoiceState tests move with it and stay in the
suite via Agent.zig's test hook.
2026-07-15 11:40:55 +02:00
Adrià Arrufat
772a3fc7db agent: move terminal width query into its only user, Spinner
Terminal.columns() had exactly one caller (Spinner's line renderer) and
Terminal itself never used it. Moving it breaks the Spinner<->Terminal
import cycle and stops Spinner type-checking against the isocline
@cImport for one ioctl.
2026-07-15 11:36:31 +02:00
Karl Seguin
fbe229c4bd uaf: Dupe blobs before using them, or else risk revokeObjectURL
When using a blob that might live through a JS call, dupe it so that any
subsequent revokeObjectURL doesn't invalidate the memory.
2026-07-15 17:24:35 +08:00
Adrià Arrufat
44193a6fca Merge pull request #2932 from lightpanda-io/agent-repl-markdown-output
agent: render markdown in REPL output
2026-07-15 11:18:52 +02:00
Adrià Arrufat
4307509094 agent: improve REPL syntax highlighting and terminal colors
- Add highlighting for JS functions, methods, types, and template
  interpolations.
- Introduce teal color and adjust cyan to bright cyan.
- Import `ansi.zig` directly instead of through `Terminal`.
- Add PandaScript semantics note to the agent's system prompt.
- Refactor markdown table rendering and streaming.
2026-07-15 11:02:46 +02:00
Francis Bouvier
35cf234f21 webapi: innerHTML/outerHTML queue one combined mutation record
Fixes WPT /dom/nodes/MutationObserver-inner-outer.html (0/3 -> 3/3):
setting innerHTML must queue a single "replace all" mutation record
(all removed children + all parsed children), and setting outerHTML a
single record replacing the element with the parsed nodes. We emitted
one record per removed child plus one per inserted child.

- Node.setHTML suppresses per-node records (removals via the new
  notify_observers opt, insertions by making fragment parsing never
  notify) and queues the combined record itself. Parsing still targets
  the element directly so html5ever keeps the right fragment context
  (e.g. raw-text content of <script>).
- Fragment parsing no longer notifies observers per inserted child:
  every other fragment-parse target is a detached DocumentFragment
  where notification is a no-op.
- Element.setOuterHTML moves the parsed nodes in with notification
  suppressed and queues one record with removedNodes=[the element],
  addedNodes=parsed children and the element's siblings.
- The unit test mutation_observer/childlist.html expected the old
  6-record innerHTML behavior; updated to the spec's single record
  (matches Chrome).

Coverage: /dom/nodes/MutationObserver-inner-outer.html 0/3 -> 3/3
(fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:54:01 +08:00
Francis Bouvier
0ee54e3343 webapi: replace-all queues one combined mutation record
Fixes WPT /dom/nodes/MutationObserver-textContent.html (1/4) and the
remaining failures of ParentNode-replaceChildren.html (25/29): the DOM
"replace all" algorithm (Element.textContent setter,
ParentNode.replaceChildren) must queue a single tree mutation record
with all removedNodes and addedNodes (and null previous/next sibling).
We emitted one record per removed child plus one per added child, so
observers saw 2+ records where the spec requires exactly one.

Frame.removeNode/appendNode gain a notify_observers opt (default true);
Node.replaceChildren suppresses the per-node records and queues the
combined record itself. Removing an added child from its previous
parent still notifies that parent's observers separately, as the spec
requires.

Coverage:
- /dom/nodes/MutationObserver-textContent.html 1/4 -> 4/4 (fully green)
- /dom/nodes/ParentNode-replaceChildren.html 25/29 -> 29/29 (fully green)
- /dom/nodes/MutationObserver-childList.html 29/38 -> 30/38

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:54:01 +08:00
Francis Bouvier
575ca0000c webapi: invoke MutationObserver callbacks with the observer as this
Fixes WPT /dom/nodes/MutationObserver-callback-arguments.html: per the
DOM spec ("invoke callback with a list of MutationRecord objects and mo,
and mo as callback this value"), the mutation callback's this value must
be the MutationObserver itself. We invoked it with the default receiver
(undefined -> globalThis), so `this === mo` failed.

deliverRecords now uses tryCallWithThis with the observer as receiver.

Coverage: /dom/nodes/MutationObserver-callback-arguments.html 0/1 -> 1/1
(fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:54:00 +08:00
Francis Bouvier
a603721f5f webapi: insert-adjacent spec semantics; document pre-insert validity
Fixes all failing subtests of WPT /dom/nodes/Element-insertAdjacentElement.html
(1/6), Element-insertAdjacentText.html (5/6) and insert-adjacent.html (11/14):

- insertAdjacentElement/Text wrongly reused insertAdjacentHTML's rule of
  throwing NoModificationAllowedError for a null or document parent. Per
  DOM's insert-adjacent algorithm they instead return null (no-op) for a
  null parent and otherwise rely on pre-insert validity, so inserting
  before/after the document element reports HierarchyRequestError.
  findAdjacentNodes takes the variant; insertAdjacentHTML keeps its
  HTML-spec behavior.
- insertAdjacentElement now returns the inserted element (was void, so
  scripts using the return value got undefined).
- The pre-insert validity checks did not implement the document-parent
  rules: a document can't contain Text children and has at most one
  element child. validateDocumentInsertion enforces both in
  appendChild/insertBefore/replaceChild; replaceChild excludes the child
  being replaced from the single-element rule, and replaceChildren
  (which replaces every child) is intentionally unaffected.

Coverage:
- /dom/nodes/Element-insertAdjacentElement.html 1/6 -> 6/6 (fully green)
- /dom/nodes/Element-insertAdjacentText.html 5/6 -> 6/6 (fully green)
- /dom/nodes/insert-adjacent.html 11/14 -> 14/14 (fully green)
- /dom/nodes/Node-insertBefore.html 13/40 -> 16/40
- /dom/nodes/Node-replaceChild.html 14/29 -> 15/29

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:54:00 +08:00