Commit Graph

8347 Commits

Author SHA1 Message Date
Francis Bouvier
cb23ad7cbb webapi: moveBefore fires connectedCallback without a disconnectedCallback
Fixes the failing subtest of WPT
/dom/nodes/moveBefore/custom-element-move-reactions.html (5/6 -> 6/6):
when a moved custom element defines no connectedMoveCallback, the
fallback is disconnectedCallback + connectedCallback - and either may
be undefined. The error from invoking the missing disconnectedCallback
returned early and skipped connectedCallback entirely.

Both fallback invocations now tolerate a missing method and let the
other one run.

Coverage: /dom/nodes/moveBefore/custom-element-move-reactions.html
5/6 -> 6/6 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:47:50 +08:00
Francis Bouvier
d8bc0bacf2 webapi: spec-conformant script insertion/execution steps
Fixes 10 failing files under /dom/nodes/insertion-removing-steps/
(5/20 -> 15/20 files passing), which pin down when dynamically inserted
scripts execute:

- Scripts (and iframes/links/styles) appended to a DETACHED parent no
  longer activate; the ready work is gated on the node being connected,
  and runs later if the subtree gets inserted into the document.
- Inserting a subtree runs the ready work for its descendant elements
  too, in tree order (nodeIsReadySubtree), after the node is in place.
- Multi-node insertions (fragments, and ParentNode.append/prepend with
  several arguments, which now convert to a fragment per spec) run all
  the ready work only after every node is inserted: an earlier script
  observes its later siblings connected, and can remove one to prevent
  it from running.
- Inserting into a not-yet-started connected script runs the script's
  children-changed steps (once, with the full content) before the
  inserted nodes' own ready work, per whatwg/html#10188: the outer
  script executes before an inner one.
- Mutation records queue before any script runs, so an inserted script
  observes its own insertion record via takeRecords. The script
  manager's pre-execution microtask drain now only happens at a real
  checkpoint (empty JS stack): a script executed synchronously from a
  running script must not flush the queue mid-task.

The remaining files in the directory need meta-referrer /
meta-default-style / media <source> insertion steps and style/iframe
load interleavings.

Coverage: /dom/nodes/insertion-removing-steps/: 15/20 files passing
(10 newly); /dom/nodes and /dom/events regressions clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:47:50 +08:00
Francis Bouvier
7f9a3071d1 webapi: cookie-averse documents; HTMLElement.accessKeyLabel
Fixes two WPT files:

- /html/dom/documents/resource-metadata-management/document-cookie.html
  (4/5 -> 5/5, fully green): a cookie-averse document (one without a
  browsing context, e.g. createHTMLDocument) must read document.cookie
  as the empty string and silently ignore writes. Both accessors now
  no-op for documents that aren't a frame's active document.
- /html/dom/access-key-label.html (1/2 -> 2/2, fully green):
  HTMLElement.accessKeyLabel was missing. It reports an Alt+ chord for
  a valid single-character accesskey, like Chromium, and the empty
  string otherwise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:47:49 +08:00
Francis Bouvier
7bbfc1a871 webapi: assigning outerText to a MathML element leaves the tree alone
Fixes the last failing subtest of WPT /html/dom/elements/
the-innertext-and-outertext-properties/outertext-setter.html
(42/43 -> 43/43): outerText is an HTMLElement property, so assigning it
on a MathML <math> element must not replace the element.

MathML elements currently share the HTML wrapper types (giving them a
plain Element wrapper is part of the pending element-interface work),
so they expose the outerText setter; it now stays inert for non-HTML
namespaces.

Coverage: outertext-setter.html 42/43 -> 43/43 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 07:47:49 +08:00
Karl Seguin
e23a5adacc Merge pull request #2960 from lightpanda-io/dom-pr-19-document-misc
webapi: foreign-element attribute matching, document.head/getElementsByName, lastModified
2026-07-17 07:47:12 +08:00
Pierre Tachoire
8d1a09317b reject focus() on non-rendered elements (display:none)
Per HTML spec, elements inside display:none containers are not
focusable. This prevented third-party popups from stealing focus
mid-typing, fixing kitandace.js integration flakiness.

fix kitandace integration test
2026-07-16 17:13:31 +02:00
Karl Seguin
ab357b354e Move localtime to datetime module
avoid uncessary attribute dump

Prefer childrenIterator
2026-07-16 18:54:59 +08:00
Francis Bouvier
6e4028a446 webapi: use the local arena for the lastModified string
Review pattern from #2943: prefer the local arena for scratch-only
allocations. The formatted date is converted to a JS string by the
bridge before the local arena resets, like other local_arena-backed
return values (e.g. getClientRects).

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-16 17:48:46 +08:00
Francis Bouvier
556ac4319c webapi: implement document.lastModified
Fixes WPT /html/dom/documents/resource-metadata-management/
document-lastModified.html (0/1 -> 1/1) and document-lastModified-01.html
(0/3 -> 3/3): document.lastModified was missing entirely.

The getter reports the navigation response's Last-Modified header
(already captured on the frame), parsed as an RFC 822 date and
formatted in the user's local time zone as "MM/DD/YYYY hh:mm:ss" via
libc localtime_r, matching what scripts derive from `new Date(...)`.
Documents without such a header - including script-created ones, which
don't inherit the frame's headers - report the current time, as the
spec requires.

Coverage: both files above fully green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:48:45 +08:00
Francis Bouvier
867fd9bc2e webapi: document.head matches by local name; getElementsByName is HTML-only
Fixes two WPT files (1 subtest each, both fully green):

- /html/dom/documents/dom-tree-accessors/document.head-02.html:
  document.head is the first html-namespace child of the document
  element whose LOCAL name is head. A createElementNS(HTML_NS,
  "blah:head") element is an HTMLUnknownElement in our type system, so
  the old is(Html.Head) walk skipped it; getHead now matches on
  namespace + local name (and returns *Element accordingly).
- .../document.getElementsByName/document.getElementsByName-namespace.html:
  getElementsByName must only consider HTML-namespace elements; the
  live collection's name filter now rejects foreign elements, matching
  the earlier fix for name-based access on HTMLCollections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:48:45 +08:00
Francis Bouvier
9ff221e2c0 selector: attribute names match case-sensitively on foreign elements
Fixes WPT /dom/nodes/querySelector-mixed-case.html: per the Selectors
spec, attribute names in selectors match ASCII case-insensitively
against HTML elements (in an HTML document) but case-sensitively
against foreign (SVG/MathML) elements. We lowercased the selector's
attribute name at parse time for everyone, so [viewBox] never matched
the case-preserved viewBox attribute on an SVG element.

The parsed attribute selector now also keeps the name as written;
matching picks the lowercased name for HTML elements (whose stored
attributes are normalized) and the original for foreign elements
(whose attributes are stored as written).

Coverage: /dom/nodes/querySelector-mixed-case.html 0/1 -> 1/1 (fully
green); Element-matches.html stays 669/669.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:48:45 +08:00
Karl Seguin
122b5bdf06 Merge pull request #2959 from lightpanda-io/dom-pr-18-html-misc
webapi: response content type, element scroll events, translate, SVGAElement.relList
2026-07-16 17:41:08 +08:00
Adrià Arrufat
d06731d8fb cli: page help output through $PAGER when stdout is a tty
Explicitly requested help (help, help <cmd>, --help) now goes to stdout
instead of stderr, and is piped through $PAGER (fallback: less -FIRX)
when stdout is an interactive terminal, so long help like 'help agent'
(~225 lines) no longer clobbers the screen. Piped or redirected output
stays plain, and error-path usage remains unpaged on stderr.
2026-07-16 11:27:32 +02:00
Karl Seguin
40c18f6142 Fix UAF on ScrollTask
Free ScrollTask on shutdown / when complete.

Skip content_type logic when type is known to be html.
2026-07-16 17:23:33 +08:00
Adrià Arrufat
f97b224662 Merge remote-tracking branch 'origin/main' into pr-2719-update 2026-07-16 10:10:21 +02:00
Karl Seguin
dcd8a6ed07 fix: Prevent class selector from doing substring match
Previously, "aa" would have matched "baaa". Now the matching is strict equality
on tokenized words.
2026-07-16 16:07:20 +08:00
Karl Seguin
1a0767d934 Merge pull request #2979 from lightpanda-io/cleanup-cells-match
minor: cleanup cells matching logic.
2026-07-16 16:02:45 +08:00
Adrià Arrufat
7c7df999bf Merge pull request #2967 from lightpanda-io/agent-terminal-refactor
agent: split Terminal.zig into picker and prompt_assist
2026-07-16 10:02:08 +02:00
Karl Seguin
39dda124ae minor: cleanup cells matching logic. 2026-07-16 15:45:02 +08:00
Francis Bouvier
3fd9149917 webapi: move SVGAElement.getRelList out of the JsApi bridge block
Review pattern: the JsApi struct is for JS/v8 bridging only; the relList
getter is plain delegation with no v8 adaptation, so it moves to the
type and the bridge accessor references it.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-16 15:35:58 +08:00
Francis Bouvier
1a51a2ec1d webapi: element scroll state lives in the element's own frame
Review pattern from #2944: an injected `frame` parameter is the frame of
the calling realm, not the frame owning the element. The element scroll
positions map, the scheduled scroll/scrollend events and the
scrolling-element (root) comparison all used the caller's frame, so a
same-origin script scrolling an element inside an iframe stored the
position in the wrong frame's map (the iframe's own reads returned 0),
fired the events through the wrong frame, and compared against the wrong
document's root.

All scroll accessors (scrollTop/scrollLeft getters and setters, scrollTo,
scrollBy) now resolve Element.ownerFrame first; scheduleScrollEvents and
the ScrollEventTask receive that owner frame.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-16 15:35:58 +08:00
Francis Bouvier
4bd52eb32c webapi: SVGAElement exposes relList
Fixes the last failing subtest of WPT
/dom/lists/DOMTokenList-coverage-for-attributes.html (174/175 ->
175/175): an <a> element in the SVG namespace must expose relList as a
DOMTokenList (while SVG <area>/<link> have none).

Main now provides the full SVGAElement type (SVGGraphicsElement
hierarchy, href as SVGAnimatedString); this commit only adds the
relList accessor, reflecting the rel attribute through the shared
Element.getRelList.

(As originally written, this commit also created the SVGAElement type
and its factory/tag wiring; the rebase keeps main's implementation.)

Coverage: /dom/lists/DOMTokenList-coverage-for-attributes.html 174/175
-> 175/175 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:35:58 +08:00
Francis Bouvier
d5c57479a8 webapi: implement the translate IDL attribute
Fixes 7 failing files under /html/dom/elements/global-attributes/
(the-translate-attribute-007 through -012 and
translate-enumerated-ascii-case-insensitive, 1 subtest each): the
HTMLElement.translate property was missing entirely.

The getter computes the element's translation mode per the HTML spec:
translate="yes" or "" enables it, "no" disables it (both ASCII
case-insensitively), anything else inherits from the closest ancestor
with a valid value, defaulting to enabled. The setter reflects back as
"yes"/"no".

Coverage: the 7 files above each 0/1 -> 1/1 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:35:58 +08:00
Francis Bouvier
5f8e910312 webapi: element scrolls fire scroll and scrollend events
Fixes 16 failing files under /dom/events/scrolling/: all 8 variants of
scrollend-event-fired-for-programmatic-scroll.html and all 8 variants
of scrollend-event-fired-for-scroll-attr-change.html.

Window scrolls already dispatched throttled scroll + scrollend events,
but Element.scrollTo/scrollBy and the scrollTop/scrollLeft setters only
updated the stored position. They now schedule the same
scroll-then-scrollend pair (10ms/20ms, throttled through a per-element
state on the scroll position entry) when the position actually changes:

- events fired at the element don't bubble, per the UI Events
  cancelability rules the tests assert ("Event targeting element does
  not bubble");
- scrolling the document's scrolling element dispatches at the
  document instead, where the events do bubble.

The remaining files in the directory need real layout
(scrollIntoView offsets, scroll snapping) or gesture scrolling.

Coverage: /dom/events/scrolling/: 20 files passing (16 newly), no
regressions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:35:58 +08:00
Francis Bouvier
bf56adeb16 webapi: documents report the navigation response's content type
Fixes 9 failing files under /dom/nodes/Document-contentType/contentType/
(bmp, css, gif, jpg, mimeheader_01, png, txt, xml, and keeps the rest
green): a document created from a non-HTML response (the synthesized
image/text/raw documents) reported the text/html default from
document.contentType instead of the response's MIME type.

The frame records the response's MIME essence (type/subtype, lowercased,
parameters stripped) when the first chunk arrives, and applies it to
the new document's _content_type override once the navigation commits.
text/html responses keep the default.

The remaining file in the directory (contenttype_javascripturi) needs
iframes to navigate to javascript: URLs and document their string
result, which is a different mechanism.

Coverage: /dom/nodes/Document-contentType/: 14/15 files passing (9
newly passing, 1 subtest each).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:35:57 +08:00
Karl Seguin
f40fd3a0bc Merge pull request #2958 from lightpanda-io/dom-pr-17-testdriver-actions
webapi: wheel/touch actions, javascript: links, GamepadEvent, actionSequence timing
2026-07-16 15:34:58 +08:00
Karl Seguin
0fcda2635f Merge pull request #2978 from lightpanda-io/cdp-going-away-on-terminate-pending
cdp: send a going-away close frame (1001) on pending terminate
2026-07-16 15:23:49 +08:00
Karl Seguin
d925b92cd7 Merge pull request #2976 from lightpanda-io/browser_context_session_id
cdp: support explicit browser context session_id
2026-07-16 15:21:16 +08:00
Karl Seguin
888d6fd52f free WebDriver actionSequence resolver 2026-07-16 15:17:17 +08:00
Adrià Arrufat
8c75769e52 agent: render picker frames in one write
Each redraw issued one unbuffered stderr write per row plus cursor
moves, which can tear visually mid-frame. Build the frame in a stack
buffer and emit it with a single locked write, like Spinner's
renderLocked.
2026-07-16 09:15:59 +02:00
Pierre Tachoire
fc45c0980f cdp: send a going-away close frame (1001) on pending terminate
Previously tick() tore the connection down without a WebSocket close
frame, so clients saw an abnormal closure (1006). Safe to send from
tick(): the worker thread is the sole writer of the socket.
2026-07-16 09:09:49 +02:00
Karl Seguin
9ff4fc2705 Merge pull request #2957 from lightpanda-io/dom-pr-16-range-clone-extract
webapi: Range cloneContents/extractContents; replaceChild live-range ordering
2026-07-16 13:51:14 +08:00
Karl Seguin
914bf027be Merge pull request #2941 from lightpanda-io/websocket-delivery
websocket: Process WebSocket message through delivery query
2026-07-16 13:17:18 +08:00
Karl Seguin
7f01c2943d webapi: Add SharedWorker
Shared on the Session (by url+name), but owned by a Page. When the created page
goes, so does the SharedWorker.
2026-07-16 13:13:09 +08:00
Francis Bouvier
2d52bd4f8e webapi: resolve the element's frame in hasNonPassiveListener
Review pattern from #2944: an injected `frame` parameter is the frame of
the calling realm, not the frame owning the element. The wheel/touch
cancelability check consulted the caller's event manager and window; for
an element living in another frame (e.g. inside an iframe) its listeners
are registered in its own frame's event manager and its propagation path
ends at that frame's window. Resolve the frame through Element.ownerFrame
like the other cross-realm-safe paths.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
9bd5aa3885 webapi: replaceChild removes the old child before inserting the new one
Fixes the 21 failing subtests of WPT
/dom/ranges/Range-mutations-replaceChild.html (39/60 -> 60/60): live
range boundary points anchored on the parent were off by one because we
inserted the new child before removing the old one. Per the DOM
"replace" algorithm, the reference points are captured first (child's
next sibling, or node's own next sibling when they're adjacent), the
old child is removed, and only then is the new node inserted before the
reference - each step running the live-range update rules in that
order.

Self-replacement (replaceChild(c, c)) now also performs the actual
remove-and-reinsert instead of only queueing records, so ranges
anchored inside the node move to its old position, as the remove step
requires.

Node-replaceChild.html and MutationObserver-childList.html stay fully
green.

Coverage: /dom/ranges/Range-mutations-replaceChild.html 39/60 -> 60/60
(fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
65e4b3c612 webapi: WebDriver.actionSequence resolves when the actions have run
Fixes WPT /dom/events/focus-event-document-move.html: testdriver's
Actions().send() promise resolved immediately (the vendor glue returned
Promise.resolve()) while the action sequence runs on the next scheduler
tick, so tests asserting right after `await ...send()` observed the
pre-action state.

actionSequence now returns a promise that a persisted resolver settles
once the input sources have been performed; the testdriver vendor glue
returns it. The persisted resolver handle is page-managed (persist
tracks it on the context), so the task does not reset it itself - doing
so double-freed the v8 global at page teardown.

Coverage: /dom/events/focus-event-document-move.html 0/1 -> 1/1 (fully
green); /dom/events regression clean (201 files).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
18df15959b webapi: add the GamepadEvent interface
Fixes WPT /dom/events/Event-timestamp-high-resolution.https.html
("window[eventType] is not a constructor"): the test constructs a
GamepadEvent and checks its timeStamp against performance.now().

GamepadEvent is a plain Event subclass with a gamepad member that is
always null - there are no gamepads in a headless browser - following
the DeviceMotionEvent pattern.

Coverage: /dom/events/Event-timestamp-high-resolution.https.html
0/1 -> 1/1 (fully green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
1187621f1b webapi: clicking a javascript: link runs the script
Fixes the last failing subtest of WPT /dom/events/Event-dispatch-click.html
("pick the first with activation behavior <a href>", 32/33 -> 33/33):
following a link whose href is a javascript: URL must evaluate the URL
body as script in the link's frame. We explicitly ignored such hrefs,
so the test's completion callback never ran.

Anchor activation now schedules the script on the frame's JS scheduler
(navigation is a queued task) and compiles/runs it in the frame's
context, ignoring the completion value (a string result would replace
the document, which nothing relies on here). The URL body should be
percent-decoded per spec; markup hrefs rarely are, so that's left as a
TODO.

Coverage: /dom/events/Event-dispatch-click.html 32/33 -> 33/33 (fully
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
8ff4711c21 webapi: wheel/touch testdriver actions; passive-listener cancelability
Fixes all 29 failing files under /dom/events/non-cancelable-when-passive/
(13/42 -> 42/42 files passing):

- WebDriver wheel actions only worked with an element origin;
  testdriver's Actions().scroll() uses the viewport origin, so nothing
  was dispatched. Viewport origins now resolve their target through the
  faux layout (a new vertical-axis-only elementFromPoint variant, since
  the faux layout gives elements no useful horizontal extent), falling
  back to the document element. The faux dimensions also learn
  vh/vw units (resolved against the page viewport), which the tests'
  200vh scroll containers rely on.
- Wheel dispatch also fires the legacy mousewheel event, like Blink.
- Touch pointer sources (Actions().addPointer("touch")) now dispatch
  touchstart/touchmove/touchend (with pointer events, without mouse
  events) instead of being treated as a mouse.
- Per the cancelability rules for scroll-blocking events, wheel,
  mousewheel and touch events are dispatched cancelable only when some
  listener on the propagation path (target chain plus window) is
  non-passive: the UA knows preventDefault can't be called otherwise.

Coverage: /dom/events/non-cancelable-when-passive/: all 42 files pass
(29 newly passing, 1 subtest each); no regressions across /dom/events/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:51 +08:00
Francis Bouvier
7e97e77bab webapi: spec algorithms for Range.cloneContents and extractContents
Fixes all remaining failures of WPT /dom/ranges/Range-cloneContents.html
(171/187 -> 187/187) and Range-extractContents.html (153/187 ->
187/187):

- cloneContents only handled same-container and sibling-boundary
  ranges. It now implements the DOM "clone the contents" algorithm on
  explicit boundary points: partially contained CharacterData
  boundaries are cloned with the substring, partially contained
  elements are cloned shallow with the subrange recursed into the
  clone, contained nodes are cloned deep, and a contained doctype
  throws HierarchyRequestError.
- extractContents was cloneContents + deleteContents, which recreated
  the extracted nodes ("you created or detached nodes when you weren't
  supposed to"). It now implements the "extract" algorithm: contained
  nodes MOVE into the fragment preserving identity; only the boundary
  CharacterData substrings and the partially contained element shells
  are cloned, with the originals truncated via replaceData. Children
  are classified before anything moves, since moving a contained child
  shifts the indices the boundary comparisons rely on. The range then
  collapses to the same new node/new offset rule as deleteContents.

Coverage:
- /dom/ranges/Range-cloneContents.html 171/187 -> 187/187 (fully green)
- /dom/ranges/Range-extractContents.html 153/187 -> 187/187 (fully green)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:06:50 +08:00
Karl Seguin
1a3d7594a3 Merge pull request #2956 from lightpanda-io/dom-pr-15-range-basics
webapi: quirks-mode class matching; Range createRange/collapse/deleteContents
2026-07-16 13:06:13 +08:00
Karl Seguin
41823f06c4 limit range collection to container range 2026-07-16 12:41:20 +08:00
Karl Seguin
1c24379c9f Merge pull request #2955 from lightpanda-io/dom-pr-14-insertion-validity
webapi: pre-insert validity, Attr adoption, fragment/replaceChild mutation records
2026-07-16 11:36:11 +08:00
Francis Bouvier
60abfbc4f9 webapi: move getCompatMode out of the JsApi bridge block
Review pattern: the JsApi struct is for JS/v8 bridging only; getters
with actual logic belong on the type. getCompatMode moves next to
isQuirksMode and the bridge accessor just references it.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-16 11:19:10 +08:00
Francis Bouvier
5bc2d8ede2 webapi: implement the spec algorithm for Range.deleteContents
Fixes the 20 failing subtests of WPT /dom/ranges/Range-deleteContents.html
(105/125 -> 125/125) and 12 of Range-extractContents.html (which
delegates its deletion half): deleteContents only handled same-container
ranges and the "boundary containers are siblings" case, and always
collapsed to the start point.

It now follows the DOM algorithm:

- same-CharacterData ranges replace the data in place (replaceData, so
  live ranges update);
- the top-most contained nodes (contained per boundary-point comparison,
  with no contained parent) are collected before any mutation and
  removed wherever they live in the tree, not just among siblings;
- partially contained CharacterData boundaries are truncated with
  replaceData;
- the range collapses to the spec's "new node/new offset": the start
  point when the start node is an inclusive ancestor of the end node,
  otherwise just after the highest partially-contained ancestor of the
  start node.

Coverage:
- /dom/ranges/Range-deleteContents.html 105/125 -> 125/125 (fully green)
- /dom/ranges/Range-extractContents.html 141/187 -> 153/187

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:19:10 +08:00
Francis Bouvier
70ed3170a7 webapi: Range.collapse() defaults to collapsing to the end
Fixes the 44 failing subtests of WPT /dom/ranges/Range-collapse.html
(142/186 -> 186/186): per the DOM IDL, collapse(optional boolean
toStart = false) collapses the range to its END point when the argument
is omitted; we defaulted to the start.

Internal callers (deleteContents) already pass an explicit true and are
unaffected.

Coverage: /dom/ranges/Range-collapse.html 142/186 -> 186/186 (fully
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:19:10 +08:00
Francis Bouvier
aa12ed5e99 webapi: createRange() boundary points start in the document it's called on
Fixes the 22 failing subtests of WPT /dom/ranges/Range-cloneRange.html
(40/62 -> 62/62): per the DOM spec, document.createRange() returns a
range whose start and end are (document, 0) - the document the method
was called on. The range factory hardcoded the frame's main document,
so foreignDoc.createRange() produced a range rooted in the main
document.

Factory.abstractRangeIn takes the initial container; createRange passes
its own document, and Range.init keeps the main-document default for
internal callers.

Coverage: /dom/ranges/Range-cloneRange.html 40/62 -> 62/62 (fully
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:19:10 +08:00
Francis Bouvier
c03a8a3376 webapi: quirks-mode documents match class names case-insensitively
Fixes WPT /dom/nodes/getElementsByClassName-14.htm: in quirks mode
getElementsByClassName must match class names ASCII
case-insensitively (class="a A" matches "a" twice), while Unicode case
stays significant.

The document had no notion of quirks mode at all (compatMode was
hardcoded to "CSS1Compat"). Document.isQuirksMode approximates the
HTML parser's mode: an HTML document without a doctype child is in
quirks mode (legacy doctypes that also trigger quirks are not
detected). compatMode now reports BackCompat accordingly, and
getElementsByClassName bakes the mode into its live-collection filter
(ClassNameFilter), with classAttributeContainsCase doing the
ASCII-case-insensitive token scan.

Coverage: /dom/nodes/getElementsByClassName-14.htm 1/2 -> 2/2 (fully
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:19:10 +08:00
Karl Seguin
c0ac3afecd webapi: spec-exact replaceChild records: fragment removal record, sibling capture
- replaceChild with a DocumentFragment still queues the fragment's own
  removal record (the spec's insert algorithm queues it regardless of
  suppressObservers); only the parent-side addition record is suppressed
  in favor of the combined record.
- The combined record's previousSibling/nextSibling are captured before
  new_child is removed from its old position, per the replace algorithm's
  step order, so replacing a child with its immediate previous sibling
  reports that sibling.
- The fragment-into-document validity check treats CDATASection as a
  Text node, matching the direct-node check.

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