Files
rust_browser/docs/DOM_Implementation_Checklist.md
Zachary D. Rowitsch 626a1c517c Implement document lifecycle events with code review fixes (§8.4, §7.1)
Add DOMContentLoaded, load, and readystatechange events with correct
readyState transitions (loading→interactive→complete). Includes Window
as a first-class event target, body onload spec quirk, and idempotency
guards to prevent double-firing. Code review hardened the API surface
by enforcing forward-only state transitions, eliminating a redundant
wrapper function, and requesting a redraw after load handler DOM mutations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 00:38:46 -04:00

383 lines
18 KiB
Markdown

# DOM Implementation Checklist
Checked items mean the feature is implemented end-to-end: internal Rust API exists, JS binding is exposed (where applicable), and behavior is correct. Partial support is left unchecked and called out inline.
## Phase 0: Architecture & Infrastructure
- [x] Arena-based DOM tree using `NodeId` indices (no lifetimes in cross-crate APIs)
- [x] Document dirty flag for mutation tracking
- [ ] DOM conformance test harness (WPT DOM suite integration)
- [ ] IDL attribute reflection framework (auto-generate JS getters/setters from IDL)
## Phase 1: Node Types
- [x] `Document` node
- [x] `Element` node
- [x] `Text` node (CharacterData)
- [x] `Comment` node (CharacterData)
- [ ] `DocumentFragment`
- Current state: not implemented; needed for `template` contents, `createDocumentFragment()`, and Range operations.
- [ ] `DocumentType` (doctype node)
- [ ] `ProcessingInstruction`
- [ ] `CDATASection` (XML only; low priority)
- [ ] `Attr` as a node type (attributes are stored as key-value pairs on `ElementData`, not as separate nodes)
## Phase 2: Node Interface
### 2.1 Properties
- [x] `nodeType` (internal — used by the engine)
- Current state: Rust-level only; not exposed to JS as a numeric constant.
- [ ] `nodeName` / `nodeValue`
- [ ] `ownerDocument`
- [ ] `parentNode` / `parentElement`
- Current state: Rust-level `parent()` / `parentElement()` exist; not JS-exposed.
- [x] `childNodes` (internal iteration via `children()`)
- Current state: Rust-level only; not returned as a live `NodeList`.
- [ ] `firstChild` / `lastChild`
- Current state: `first_child()` exists in Rust; `lastChild` is missing. Neither JS-exposed.
- [ ] `nextSibling` / `previousSibling`
- Current state: `next_sibling()` exists in Rust; `previousSibling` is missing. Neither JS-exposed.
- [ ] `textContent` (full Node-level semantics)
- Current state: getter/setter works on Elements via JS; full Node-level semantics (null for Document, concatenation for DocumentFragment) not complete.
- [ ] `isConnected`
- [ ] `baseURI`
### 2.2 Node Constants
- [ ] `Node.ELEMENT_NODE` (1), `Node.TEXT_NODE` (3), `Node.COMMENT_NODE` (8), etc.
### 2.3 Mutation Methods
- [x] `appendChild(child)` (Rust + JS)
- [x] `removeChild(child)` (Rust + JS)
- [ ] `insertBefore(newNode, referenceNode)`
- [ ] `replaceChild(newChild, oldChild)`
- [x] `set_text_content()` (replaces all children with text; Rust-level, JS setter on Element)
### 2.4 Query & Comparison Methods
- [ ] `cloneNode(deep)`
- [ ] `contains(node)`
- [ ] `hasChildNodes()`
- [ ] `compareDocumentPosition(other)`
- [ ] `isEqualNode(other)` / `isSameNode(other)`
- [ ] `normalize()` (merge adjacent Text nodes)
- [ ] `lookupPrefix()` / `lookupNamespaceURI()` / `isDefaultNamespace()` (XML namespace; low priority)
## Phase 3: Document Interface
### 3.1 Node Creation
- [x] `document.createElement(tagName)`
- [x] `document.createTextNode(data)` (Rust-level `create_text()`)
- Current state: Rust-level only; not JS-exposed.
- [x] `document.createComment(data)` (Rust-level `create_comment()`)
- Current state: Rust-level only; not JS-exposed.
- [ ] `document.createDocumentFragment()`
- [ ] `document.createEvent(type)` (legacy but widely used)
- [ ] `document.createTreeWalker(root, whatToShow, filter)`
- [ ] `document.createNodeIterator(root, whatToShow, filter)`
- [ ] `document.createRange()`
- [ ] `document.importNode(node, deep)` / `document.adoptNode(node)`
### 3.2 Query Methods
- [x] `document.getElementById(id)` (Rust + JS)
- [ ] `document.getElementsByTagName(tagName)` (JS binding)
- Current state: Rust-level implementation exists; not exposed to JS.
- [ ] `document.getElementsByClassName(className)` (JS binding)
- Current state: Rust-level implementation exists; not exposed to JS.
- [ ] `document.getElementsByName(name)`
- [ ] `document.querySelector(selectors)`
- [ ] `document.querySelectorAll(selectors)`
- Current state: selector matching engine exists in the `selectors` crate; not wired to JS query APIs.
### 3.3 Document Properties
- [ ] `document.documentElement` (root `<html>` element)
- [ ] `document.head` / `document.body`
- [ ] `document.title` (get/set)
- [ ] `document.URL` / `document.documentURI`
- [ ] `document.domain` (deprecated but commonly accessed)
- [ ] `document.referrer`
- [ ] `document.cookie` (get/set)
- [x] `document.readyState`
- [ ] `document.characterSet` / `document.charset`
- [ ] `document.contentType`
- [ ] `document.compatMode` (quirks vs standards)
- [ ] `document.doctype`
- [ ] `document.activeElement`
- [ ] `document.visibilityState` / `document.hidden`
### 3.4 Document Lifecycle Events
- [x] `DOMContentLoaded` event
- [x] `load` event
- [x] `readystatechange` event
- [ ] `visibilitychange` event
## Phase 4: Element Interface
### 4.1 Core Properties
- [x] `element.id` (get; JS-exposed)
- [x] `element.tagName` (JS-exposed)
- [x] `element.className` (JS-exposed)
- [ ] `element.classList` (returns `DOMTokenList`)
- [ ] `element.slot`
- [ ] `element.localName` / `element.namespaceURI` / `element.prefix`
### 4.2 Attribute Methods
- [x] `element.getAttribute(name)` (internal storage; JS-exposed as property access)
- Current state: attributes stored as Vec of name-value pairs; JS reads attributes via property access, not a dedicated `getAttribute()` call.
- [x] `element.setAttribute(name, value)` (Rust-level)
- Current state: Rust-level only; not exposed as a JS method.
- [ ] `element.getAttribute(name)` (JS method)
- [ ] `element.setAttribute(name, value)` (JS method)
- [ ] `element.removeAttribute(name)`
- [ ] `element.hasAttribute(name)`
- [ ] `element.toggleAttribute(name, force)`
- [ ] `element.getAttributeNames()`
- [ ] `element.attributes` (returns `NamedNodeMap`)
- [ ] `element.dataset` (data-* attributes as DOMStringMap)
### 4.3 Content Properties
- [x] `element.textContent` (getter/setter; JS-exposed)
- [x] `element.innerHTML` (getter/setter; JS-exposed)
- Current state: wired to fragment parse/serialize; not fully spec-compliant context-sensitive parsing.
- [ ] `element.outerHTML` (getter/setter)
- [ ] `element.innerText` (getter/setter; layout-aware)
- [ ] `element.insertAdjacentHTML(position, text)`
- [ ] `element.insertAdjacentElement(position, element)`
- [ ] `element.insertAdjacentText(position, text)`
### 4.4 Traversal Properties
- [ ] `element.children` (returns live `HTMLCollection` of child elements)
- [ ] `element.childElementCount`
- [ ] `element.firstElementChild` / `element.lastElementChild`
- [ ] `element.nextElementSibling` / `element.previousElementSibling`
- Current state: Rust-level `element_siblings()` exists for CSS selector matching; not JS-exposed.
- [ ] `element.closest(selectors)`
- [ ] `element.matches(selectors)`
- Current state: selector matching logic exists internally; not JS-exposed.
### 4.5 Query Methods (on Element)
- [ ] `element.querySelector(selectors)`
- [ ] `element.querySelectorAll(selectors)`
- [ ] `element.getElementsByTagName(tagName)`
- [ ] `element.getElementsByClassName(className)`
### 4.6 Geometry & Scroll
- [ ] `element.getBoundingClientRect()``DOMRect`
- [ ] `element.getClientRects()``DOMRectList`
- [ ] `element.clientWidth` / `element.clientHeight`
- [ ] `element.clientTop` / `element.clientLeft`
- [ ] `element.scrollWidth` / `element.scrollHeight`
- [ ] `element.scrollTop` / `element.scrollLeft` (get/set)
- [ ] `element.scrollIntoView()`
- [ ] `element.offsetWidth` / `element.offsetHeight`
- [ ] `element.offsetTop` / `element.offsetLeft`
- [ ] `element.offsetParent`
### 4.7 Focus & Interaction
- [ ] `element.focus()` / `element.blur()`
- [ ] `element.click()`
- [ ] `element.tabIndex`
## Phase 5: DOM Collections
- [ ] `NodeList` (live and static variants)
- Live: returned by `childNodes`, `getElementsByTagName`, `getElementsByClassName`
- Static: returned by `querySelectorAll`
- Required methods: `item(index)`, `length`, `forEach()`, `entries()`, `keys()`, `values()`
- [ ] `HTMLCollection` (live, returned by `children`, `getElementsByTagName` on Element)
- Required methods: `item(index)`, `namedItem(name)`, `length`
- [ ] `DOMTokenList` (for `classList`, `relList`, `sandbox`, etc.)
- Required methods: `add()`, `remove()`, `toggle()`, `contains()`, `replace()`, `item()`, `length`, `value`, `forEach()`
- [ ] `NamedNodeMap` (for `element.attributes`)
- Required methods: `getNamedItem()`, `setNamedItem()`, `removeNamedItem()`, `item()`, `length`
## Phase 6: Events
### 6.1 EventTarget Interface
- [x] `addEventListener(type, listener, options/useCapture)`
- [x] `removeEventListener(type, listener)`
- [ ] `dispatchEvent(event)` (programmatic dispatch)
### 6.2 Event Interface
- [x] `event.type`
- [x] `event.target` / `event.currentTarget`
- [x] `event.bubbles` / `event.cancelable`
- [x] `event.defaultPrevented`
- [x] `event.eventPhase`
- [x] `event.preventDefault()`
- [x] `event.stopPropagation()`
- [ ] `event.stopImmediatePropagation()`
- [ ] `event.composed` / `event.composedPath()`
- [ ] `event.isTrusted`
- [ ] `event.timeStamp`
### 6.3 Event Dispatch Algorithm
- [x] Target phase
- [x] Bubble phase
- Current state: implemented for click events.
- [ ] Capture phase
- [ ] Correct event path construction (including shadow DOM composed path)
- [ ] Passive event listeners (`{ passive: true }`)
- [ ] `once` option (`{ once: true }`)
- [ ] Event listener `signal` option (AbortSignal integration)
### 6.4 Event Constructors
- [ ] `new Event(type, options)`
- [ ] `new CustomEvent(type, options)` with `detail` property
- [ ] `new MouseEvent(type, options)`
- [ ] `new KeyboardEvent(type, options)`
- [ ] `new FocusEvent(type, options)`
- [ ] `new InputEvent(type, options)`
- [ ] `new WheelEvent(type, options)`
- [ ] `new PointerEvent(type, options)`
### 6.5 Event Types
- [x] `click`
- [ ] Mouse: `mousedown`, `mouseup`, `mousemove`, `mouseenter`, `mouseleave`, `mouseover`, `mouseout`, `dblclick`, `contextmenu`
- [ ] Keyboard: `keydown`, `keyup`, `keypress` (deprecated)
- [ ] Focus: `focus`, `blur`, `focusin`, `focusout`
- [ ] Input: `input`, `change`, `beforeinput`
- [ ] Form: `submit`, `reset`, `invalid`
- [ ] Drag: `dragstart`, `drag`, `dragend`, `dragenter`, `dragleave`, `dragover`, `drop`
- [ ] Touch: `touchstart`, `touchmove`, `touchend`, `touchcancel`
- [ ] Pointer: `pointerdown`, `pointerup`, `pointermove`, `pointerenter`, `pointerleave`, `pointerover`, `pointerout`, `pointercancel`, `gotpointercapture`, `lostpointercapture`
- [ ] Scroll: `scroll`, `scrollend`
- [ ] Resize: `resize`
- [ ] Clipboard: `copy`, `cut`, `paste`
- [ ] Animation: `animationstart`, `animationend`, `animationiteration`, `transitionend`
## Phase 7: DOM Traversal
- [ ] `TreeWalker` interface
- `createTreeWalker(root, whatToShow, filter)`
- Methods: `parentNode()`, `firstChild()`, `lastChild()`, `previousSibling()`, `nextSibling()`, `previousNode()`, `nextNode()`
- `whatToShow` bitmask filtering
- [ ] `NodeIterator` interface
- `createNodeIterator(root, whatToShow, filter)`
- Methods: `nextNode()`, `previousNode()`, `detach()`
- [x] Internal tree iteration (Rust-level `TreeIterator` for pre-order traversal)
- Current state: used by the engine for style/layout; not JS-exposed.
## Phase 8: Ranges
- [ ] `Range` interface
- Construction: `document.createRange()`, `new Range()`
- Boundary: `setStart()`, `setEnd()`, `setStartBefore()`, `setStartAfter()`, `setEndBefore()`, `setEndAfter()`
- Properties: `startContainer`, `startOffset`, `endContainer`, `endOffset`, `collapsed`, `commonAncestorContainer`
- Comparison: `compareBoundaryPoints()`, `comparePoint()`, `isPointInRange()`, `intersectsNode()`
- Content: `cloneContents()`, `extractContents()`, `deleteContents()`, `insertNode()`, `surroundContents()`
- Utility: `cloneRange()`, `detach()`, `toString()`
- `createContextualFragment(html)`
- [ ] `StaticRange` interface
- [ ] `AbstractRange` base interface
## Phase 9: Selection API
- [ ] `window.getSelection()``Selection`
- [ ] `Selection` interface
- Properties: `anchorNode`, `anchorOffset`, `focusNode`, `focusOffset`, `isCollapsed`, `rangeCount`, `type`
- Methods: `getRangeAt()`, `addRange()`, `removeRange()`, `removeAllRanges()`, `collapse()`, `collapseToStart()`, `collapseToEnd()`, `extend()`, `selectAllChildren()`, `deleteFromDocument()`, `containsNode()`, `toString()`
- [ ] Input selection: `selectionStart`, `selectionEnd`, `selectionDirection` on input/textarea
- [ ] `select` event
## Phase 10: MutationObserver
- [ ] `new MutationObserver(callback)`
- [ ] `.observe(target, options)` — options: `childList`, `attributes`, `characterData`, `subtree`, `attributeFilter`, `attributeOldValue`, `characterDataOldValue`
- [ ] `.disconnect()`
- [ ] `.takeRecords()`
- [ ] `MutationRecord` interface
- Properties: `type`, `target`, `addedNodes`, `removedNodes`, `previousSibling`, `nextSibling`, `attributeName`, `attributeNamespace`, `oldValue`
## Phase 11: CSSOM (CSS Object Model)
### 11.1 Inline Style Access
- [ ] `element.style``CSSStyleDeclaration`
- Property access: `element.style.color`, `element.style.fontSize`, etc.
- Methods: `getPropertyValue()`, `setProperty()`, `removeProperty()`, `getPropertyPriority()`
- `element.style.cssText` (get/set)
- `element.style.length` / `element.style.item(index)`
### 11.2 Computed Style
- [ ] `window.getComputedStyle(element, pseudoElt)``CSSStyleDeclaration`
- Current state: computed styles exist in the `style` crate; not exposed to JS.
### 11.3 Stylesheet Access
- [ ] `document.styleSheets``StyleSheetList`
- [ ] `CSSStyleSheet` interface
- `.cssRules` / `.insertRule()` / `.deleteRule()`
- [ ] `CSSRule` / `CSSStyleRule` / `CSSMediaRule` / `CSSImportRule` interfaces
## Phase 12: DOM Parsing & Serialization
- [ ] `DOMParser`
- `new DOMParser()`
- `.parseFromString(str, type)` — types: `text/html`, `text/xml`, `application/xml`, `application/xhtml+xml`, `image/svg+xml`
- [ ] `XMLSerializer`
- `new XMLSerializer()`
- `.serializeToString(node)`
- [x] `innerHTML` setter (uses HTML parser for fragment parsing)
- Current state: basic fragment parse/serialize; not fully spec-compliant.
- [x] `innerHTML` getter (serializes child nodes to HTML string)
## Phase 13: HTML-Specific Element Interfaces
### 13.1 HTMLElement Base
- [ ] `HTMLElement` interface (extends `Element`)
- Properties: `title`, `lang`, `dir`, `hidden`, `draggable`, `contentEditable`, `isContentEditable`, `spellcheck`, `tabIndex`
- Style: `.style` property → `CSSStyleDeclaration`
- Data: `.dataset``DOMStringMap`
- Interaction: `.click()`, `.focus()`, `.blur()`
- Offset: `.offsetTop`, `.offsetLeft`, `.offsetWidth`, `.offsetHeight`, `.offsetParent`
- Scroll: `.scrollTop`, `.scrollLeft`, `.scrollWidth`, `.scrollHeight`
### 13.2 Specific Element Interfaces
- [ ] `HTMLAnchorElement``href`, `target`, `rel`, `download`, `origin`, `protocol`, `hostname`, `pathname`, etc.
- [ ] `HTMLImageElement``src`, `alt`, `width`, `height`, `naturalWidth`, `naturalHeight`, `complete`, `currentSrc`, `decode()`
- [ ] `HTMLInputElement``value`, `type`, `name`, `checked`, `disabled`, `readOnly`, `placeholder`, `required`, `validity`, `checkValidity()`, `setCustomValidity()`
- [ ] `HTMLSelectElement``value`, `selectedIndex`, `options`, `selectedOptions`, `multiple`, `add()`, `remove()`
- [ ] `HTMLTextAreaElement``value`, `rows`, `cols`, `selectionStart`, `selectionEnd`, `select()`
- [ ] `HTMLFormElement``elements`, `action`, `method`, `submit()`, `reset()`, `checkValidity()`
- [ ] `HTMLButtonElement``type`, `value`, `disabled`, `form`
- [ ] `HTMLCanvasElement``getContext()`, `width`, `height`, `toDataURL()`, `toBlob()`
- [ ] `HTMLMediaElement``src`, `play()`, `pause()`, `currentTime`, `duration`, `paused`, `volume`, `muted`
- [ ] `HTMLVideoElement``videoWidth`, `videoHeight`, `poster`
- [ ] `HTMLTableElement``rows`, `tBodies`, `tHead`, `tFoot`, `caption`, `insertRow()`, `deleteRow()`, `createTHead()`, `createTBody()`, `createTFoot()`, `createCaption()`
- [ ] `HTMLScriptElement``src`, `type`, `async`, `defer`, `text`
- [ ] `HTMLStyleElement``sheet`
- [ ] `HTMLLinkElement``href`, `rel`, `type`, `sheet`
- [ ] `HTMLTemplateElement``content` (DocumentFragment)
## Phase 14: AbortController / AbortSignal
- [ ] `new AbortController()`
- [ ] `AbortController.prototype.signal``AbortSignal`
- [ ] `AbortController.prototype.abort(reason)`
- [ ] `AbortSignal.prototype.aborted`
- [ ] `AbortSignal.prototype.reason`
- [ ] `AbortSignal.prototype.onabort` / `addEventListener('abort', ...)`
- [ ] `AbortSignal.abort(reason)` (static)
- [ ] `AbortSignal.timeout(ms)` (static)
- [ ] Integration with `fetch()`, event listeners, and other abortable APIs
## Phase 15: Intersection & Resize Observers
- [ ] `IntersectionObserver`
- `new IntersectionObserver(callback, options)`
- `.observe(target)`, `.unobserve(target)`, `.disconnect()`, `.takeRecords()`
- `IntersectionObserverEntry``target`, `isIntersecting`, `intersectionRatio`, `intersectionRect`, `boundingClientRect`, `rootBounds`, `time`
- [ ] `ResizeObserver`
- `new ResizeObserver(callback)`
- `.observe(target, options)`, `.unobserve(target)`, `.disconnect()`
- `ResizeObserverEntry``target`, `contentRect`, `borderBoxSize`, `contentBoxSize`
## Phase 16: Conformance Exit Criteria
- [ ] All Node types per DOM Living Standard implemented
- [ ] DOM mutation methods complete and correct (`appendChild`, `insertBefore`, `replaceChild`, `removeChild`, `cloneNode`)
- [ ] Query APIs wired to JS (`querySelector`, `querySelectorAll`, `getElementById`, `getElementsByTagName`, `getElementsByClassName`)
- [ ] DOM collections implemented (`NodeList`, `HTMLCollection`, `DOMTokenList`)
- [ ] Event dispatch algorithm spec-compliant (capture → target → bubble)
- [ ] CSSOM basics exposed to JS (`element.style`, `getComputedStyle`)
- [ ] Pass WPT DOM test suite at target threshold
- [ ] No crashers: malformed DOM operations must not crash the engine
- [ ] Publish DOM conformance report with pass rates and remaining gaps