All checks were successful
ci / fast (linux) (push) Successful in 7m9s
Create comprehensive implementation-ready story files for the remaining Epic 3 (JavaScript Engine Maturity) stories and update sprint status from backlog to ready-for-dev: - 3.5: Built-in Completeness (Array/String/Object) - 3.6: Built-in Completeness (Date/RegExp/Map/Set) - 3.7: WeakRef, FinalizationRegistry & Strict Mode Edge Cases - 3.8: DOM Bindings via web_api - 3.9: Event Dispatch Completeness - 3.10: Web API Exposure (fetch, Math, setInterval, rAF) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
363 lines
23 KiB
Markdown
363 lines
23 KiB
Markdown
# Story 3.8: DOM Bindings via web_api
|
|
|
|
Status: ready-for-dev
|
|
|
|
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
|
|
|
## Story
|
|
|
|
As a web developer using JavaScript,
|
|
I want full DOM manipulation capabilities from JavaScript,
|
|
So that dynamic web pages can create, modify, and style elements at runtime.
|
|
|
|
## Acceptance Criteria
|
|
|
|
1. **Attribute access:** `element.getAttribute(name)` returns attribute value or null. `element.setAttribute(name, value)` sets attribute and triggers dependent behavior (e.g., `class` attribute updates styling). `element.removeAttribute(name)` removes attribute. `element.hasAttribute(name)` returns boolean. Per DOM §7.3.
|
|
|
|
2. **Element identity properties:** `element.id` (get/set) reflects the `id` attribute. `element.className` (get/set) reflects the `class` attribute. `element.tagName` returns uppercase tag name for HTML elements. Per DOM §5.4/§7.3.
|
|
|
|
3. **classList DOMTokenList:** `element.classList.add(...classes)`, `.remove(...classes)`, `.toggle(class, force?)`, `.contains(class)`, `.replace(old, new)`, `.length`, `.item(index)`, `[index]` access. Modifying classList updates the `class` attribute and triggers style recomputation. Per DOM §7.1.
|
|
|
|
4. **Inline style access:** `element.style.propertyName` (get/set) for camelCase CSS properties (e.g., `element.style.backgroundColor = "red"`). `element.style.cssText` (get/set) for full inline style string. `element.style.getPropertyValue(name)` and `element.style.setProperty(name, value)` for kebab-case access. Setting style triggers re-style/re-layout on next render. Per CSSOM §6.7.
|
|
|
|
5. **Tree navigation properties:** `node.parentNode`, `node.parentElement`, `node.childNodes` (live NodeList), `node.children` (live HTMLCollection of elements only), `node.firstChild`, `node.lastChild`, `node.nextSibling`, `node.previousSibling`, `node.firstElementChild`, `node.lastElementChild`, `node.nextElementSibling`, `node.previousElementSibling`. All return HostObject or null. Per DOM §4.4.
|
|
|
|
6. **Additional element properties:** `element.outerHTML` (getter), `element.cloneNode(deep)`, `element.contains(other)`, `element.matches(selector)`, `element.closest(selector)`, `node.nodeType`, `node.nodeName`, `node.ownerDocument`, `element.childElementCount`. Per DOM §4.4/§4.5.
|
|
|
|
7. **Integration tests verify each binding**, DOM checklist is updated, and `just ci` passes.
|
|
|
|
## What NOT to Implement
|
|
|
|
- **No `element.dataset`** -- `data-*` attribute reflection deferred. `getAttribute("data-foo")` works as workaround.
|
|
- **No `NamedNodeMap`** -- `element.attributes` collection deferred. Individual `getAttribute`/`setAttribute` sufficient.
|
|
- **No computed style** -- `window.getComputedStyle(element)` deferred to Story 3.10 (Web API Exposure).
|
|
- **No `element.scrollTop`/`scrollLeft`/`scrollWidth`/`scrollHeight`** -- scroll metrics deferred to Epic 5.
|
|
- **No `element.offsetTop`/`offsetLeft`/`offsetWidth`/`offsetHeight`/`getBoundingClientRect()`** -- layout metrics deferred to Story 3.10.
|
|
- **No `element.focus()`/`element.blur()`** -- focus management deferred to Epic 4.
|
|
- **No `MutationObserver`** -- DOM observation API deferred.
|
|
- **No `element.animate()`** -- Web Animations API out of scope.
|
|
|
|
## Files to Modify
|
|
|
|
| File | Change |
|
|
|------|--------|
|
|
| `crates/web_api/src/dom_host/host_environment.rs` | Add attribute access (getAttribute/setAttribute/removeAttribute/hasAttribute), tree navigation properties (parentNode, childNodes, nextSibling, etc.), element identity (id, className, tagName), classList dispatch, style dispatch, cloneNode, contains, matches, closest, nodeType, nodeName, outerHTML. |
|
|
| `crates/web_api/src/dom_host/mod.rs` | Add classList helper struct/methods. Add CSSStyleDeclaration helper for element.style bridge. |
|
|
| `crates/web_api/src/dom_host/classlist.rs` | **New file** -- DOMTokenList implementation for classList. |
|
|
| `crates/web_api/src/dom_host/style_bridge.rs` | **New file** -- CSSStyleDeclaration bridge: camelCase-to-kebab conversion, inline style parsing/serialization, property get/set. |
|
|
| `crates/dom/src/document.rs` | Expose any missing DOM operations needed (most already exist). Add `clone_node(id, deep)` if not present. Add `contains(ancestor_id, descendant_id)`. |
|
|
| `crates/web_api/src/dom_host/live_collection.rs` | Add `NodeList` support (childNodes returns live NodeList of all node types, not just elements). |
|
|
| `crates/web_api/src/dom_host/tests/dom_tests.rs` | Add tests for all new bindings. |
|
|
| `tests/js_dom_tests.rs` | Add integration tests for attribute access, classList, style, navigation, cloneNode, matches, closest. |
|
|
| `docs/HTML5_Implementation_Checklist.md` | Check off DOMTokenList, attribute access, navigation properties. |
|
|
|
|
## Tasks / Subtasks
|
|
|
|
- [ ] Task 1: Attribute access APIs (AC: #1)
|
|
- [ ] 1.1 Add `getAttribute`, `setAttribute`, `removeAttribute`, `hasAttribute` to `call_method()` in `host_environment.rs`:
|
|
- `getAttribute(name)` → delegate to `document.get_attribute(node_id, &name)`, return String or Null
|
|
- `setAttribute(name, value)` → delegate to `document.set_attribute(node_id, &name, &value)`, return Undefined
|
|
- `removeAttribute(name)` → delegate to `document.remove_attribute(node_id, &name)` (add to dom crate if missing), return Undefined
|
|
- `hasAttribute(name)` → check if attribute exists, return Boolean
|
|
- All methods: validate that host object is Element type, TypeError otherwise
|
|
- [ ] 1.2 Add `remove_attribute(node_id, name)` to `crates/dom/src/document.rs` if not already present
|
|
- [ ] 1.3 Add unit tests and integration tests:
|
|
- `element.setAttribute("data-x", "hello"); element.getAttribute("data-x") === "hello"`
|
|
- `element.removeAttribute("class"); element.hasAttribute("class") === false`
|
|
- Setting `class` attribute manually should work (classList tests in Task 3)
|
|
|
|
- [ ] Task 2: Element identity and navigation properties (AC: #2, #5)
|
|
- [ ] 2.1 Add element identity properties to `get_property()` in `host_environment.rs`:
|
|
- `"id"` → `document.get_attribute(node_id, "id")` or empty string
|
|
- `"className"` → `document.get_attribute(node_id, "class")` or empty string
|
|
- `"tagName"` → `document.tag_name(node_id).to_uppercase()` (HTML elements return uppercase per spec)
|
|
- `"nodeName"` → same as tagName for elements; `"#text"` for text nodes, `"#document"` for document, `"#comment"` for comments, `"#document-fragment"` for fragments
|
|
- `"nodeType"` → `1` (Element), `3` (Text), `8` (Comment), `9` (Document), `11` (DocumentFragment)
|
|
- `"ownerDocument"` → return Document HostObject (DOCUMENT_HOST_ID)
|
|
- `"childElementCount"` → count of element children
|
|
- [ ] 2.2 Add `id` and `className` setters to `set_property()`:
|
|
- `"id"` → `document.set_attribute(node_id, "id", &value)`
|
|
- `"className"` → `document.set_attribute(node_id, "class", &value)`
|
|
- [ ] 2.3 Add tree navigation properties to `get_property()`:
|
|
- `"parentNode"` → `document.parent(node_id)` → HostObject or Null
|
|
- `"parentElement"` → `document.parent_element(node_id)` → HostObject or Null (only if parent is element)
|
|
- `"firstChild"` → `document.first_child(node_id)` → HostObject or Null
|
|
- `"lastChild"` → `document.last_child(node_id)` → HostObject or Null (add to dom if missing)
|
|
- `"nextSibling"` → `document.next_sibling(node_id)` → HostObject or Null
|
|
- `"previousSibling"` → `document.previous_sibling(node_id)` → HostObject or Null (add to dom if missing)
|
|
- `"firstElementChild"` → first child that is an element → HostObject or Null
|
|
- `"lastElementChild"` → last child that is an element → HostObject or Null
|
|
- `"nextElementSibling"` → next sibling that is an element → HostObject or Null
|
|
- `"previousElementSibling"` → previous sibling that is an element → HostObject or Null
|
|
- [ ] 2.4 Add `childNodes` property (live NodeList):
|
|
- Return a HostObject representing a live NodeList (includes ALL child nodes: elements, text, comments)
|
|
- Reuse `LiveCollection` pattern from `live_collection.rs` but for all node types
|
|
- Support `length`, `[index]`, `item(index)` access
|
|
- [ ] 2.5 Add `children` property (live HTMLCollection):
|
|
- Return a HostObject representing a live HTMLCollection (elements only)
|
|
- Reuse existing `LiveCollection` infrastructure
|
|
- [ ] 2.6 Add missing DOM helpers to `crates/dom/src/document.rs`:
|
|
- `last_child(id)` if not present
|
|
- `previous_sibling(id)` if not present
|
|
- Helper iterators for element-only navigation
|
|
- [ ] 2.7 Add tests for all navigation properties and identity getters
|
|
|
|
- [ ] Task 3: classList DOMTokenList (AC: #3)
|
|
- [ ] 3.1 Create `crates/web_api/src/dom_host/classlist.rs`:
|
|
- `ClassList` is NOT a standalone object -- it's a live view of the element's `class` attribute
|
|
- When JS accesses `element.classList`, return a HostObject with type `"DOMTokenList"` and a combined ID encoding both the element NodeId and a marker
|
|
- ID encoding: `CLASSLIST_ID_BASE + node_id` (reserve ID range like LiveCollection)
|
|
- [ ] 3.2 Implement DOMTokenList methods via `call_method()` dispatch on `"DOMTokenList"` type:
|
|
- `.add(...classes)` → parse class attribute, add new classes, set attribute back
|
|
- `.remove(...classes)` → parse, remove, set back
|
|
- `.toggle(class, force?)` → toggle or force add/remove, return boolean
|
|
- `.contains(class)` → check if class exists in attribute
|
|
- `.replace(oldClass, newClass)` → replace if present, return boolean
|
|
- `.item(index)` → return class at index or null
|
|
- `.length` (via get_property) → number of classes
|
|
- Index access (`0`, `1`, etc.) → same as item()
|
|
- [ ] 3.3 Ensure class attribute changes trigger style recomputation:
|
|
- `document.set_attribute()` for `class` should be sufficient if the rendering pipeline re-matches selectors
|
|
- **Verify**: After `classList.add("highlight")`, next render cycle applies `.highlight` styles
|
|
- [ ] 3.4 Add `"classList"` to `get_property()` for Element type → return DOMTokenList HostObject
|
|
- [ ] 3.5 Add tests:
|
|
- `el.classList.add("a", "b"); el.className === "a b"`
|
|
- `el.classList.remove("a"); el.className === "b"`
|
|
- `el.classList.toggle("c") === true; el.classList.contains("c") === true`
|
|
- `el.classList.toggle("c") === false; el.classList.contains("c") === false`
|
|
- `el.classList.replace("b", "d")`
|
|
- `el.classList.length === 1`
|
|
|
|
- [ ] Task 4: Inline style access (AC: #4)
|
|
- [ ] 4.1 Create `crates/web_api/src/dom_host/style_bridge.rs`:
|
|
- `CSSStyleDeclaration` bridge between JS `element.style.backgroundColor` and DOM inline `style` attribute
|
|
- camelCase to kebab-case conversion: `backgroundColor` → `background-color`, `marginTop` → `margin-top`
|
|
- kebab-case to camelCase for reading
|
|
- Handle CSS vendor prefixes: `webkitTransform` → `-webkit-transform` (low priority)
|
|
- [ ] 4.2 Return CSSStyleDeclaration HostObject from `get_property("style")`:
|
|
- Use ID encoding: `STYLE_ID_BASE + node_id`
|
|
- Type name: `"CSSStyleDeclaration"`
|
|
- [ ] 4.3 Implement style property get/set via `get_property()`/`set_property()` on `"CSSStyleDeclaration"` type:
|
|
- **Get**: Parse element's `style` attribute, find matching property, return value string or empty string
|
|
- **Set**: Parse existing style attribute, update/add property, serialize back to attribute
|
|
- **`cssText`** (get): Return full `style` attribute value
|
|
- **`cssText`** (set): Replace entire `style` attribute
|
|
- [ ] 4.4 Implement `getPropertyValue(name)` and `setProperty(name, value)` via `call_method()`:
|
|
- Accept kebab-case property names (`"background-color"`)
|
|
- `removeProperty(name)` removes the property and returns old value
|
|
- [ ] 4.5 Inline style parsing/serialization:
|
|
- Parse: `"color: red; font-size: 16px"` → `Vec<(String, String)>`
|
|
- Serialize: `Vec<(String, String)>` → `"color: red; font-size: 16px"`
|
|
- **Reuse CSS parser?** The `css` crate can parse property values. For inline styles, a simple split on `;` and `:` may suffice.
|
|
- [ ] 4.6 Ensure style changes trigger re-render:
|
|
- Setting `style` attribute via `document.set_attribute()` should invalidate style computation
|
|
- [ ] 4.7 Add tests:
|
|
- `el.style.color = "red"; el.getAttribute("style")` includes `color: red`
|
|
- `el.style.backgroundColor = "blue"` → `el.getAttribute("style")` includes `background-color: blue`
|
|
- `el.style.cssText = "margin: 10px"` replaces all inline styles
|
|
- `el.style.getPropertyValue("color") === "red"`
|
|
- `el.style.removeProperty("color")` removes it
|
|
|
|
- [ ] Task 5: Additional element methods (AC: #6)
|
|
- [ ] 5.1 Implement `element.cloneNode(deep)` in `call_method()`:
|
|
- Shallow clone (`deep=false`): clone element and attributes, no children
|
|
- Deep clone (`deep=true`): recursively clone element, attributes, and all descendants
|
|
- Add `clone_node(node_id, deep)` to `crates/dom/src/document.rs`
|
|
- Return new HostObject for cloned node
|
|
- [ ] 5.2 Implement `element.contains(other)` in `call_method()`:
|
|
- Walk ancestors of `other` checking if any match `element`
|
|
- Return boolean. `element.contains(element)` returns true (contains itself).
|
|
- Add `contains(ancestor_id, descendant_id)` to dom crate if missing
|
|
- [ ] 5.3 Implement `element.matches(selector)` in `call_method()`:
|
|
- Parse selector string using `selectors` crate
|
|
- Check if element matches the selector
|
|
- Return boolean
|
|
- Reuse existing selector matching from `document.query_selector()` path
|
|
- [ ] 5.4 Implement `element.closest(selector)` in `call_method()`:
|
|
- Walk ancestors (including self) checking `matches(selector)` on each
|
|
- Return first matching ancestor as HostObject, or Null
|
|
- [ ] 5.5 Implement `element.outerHTML` getter in `get_property()`:
|
|
- Serialize element AND its content to HTML string
|
|
- Add `outer_html(node_id)` to dom crate (wraps inner_html with the element's own tag)
|
|
- [ ] 5.6 Add tests for cloneNode, contains, matches, closest, outerHTML
|
|
|
|
- [ ] Task 6: Testing and validation (AC: #7)
|
|
- [ ] 6.1 Add integration tests in `tests/js_dom_tests.rs`:
|
|
- Attribute access: get/set/remove/has
|
|
- Identity properties: id, className, tagName
|
|
- classList: add, remove, toggle, contains, replace
|
|
- Style access: camelCase property set/get, cssText, getPropertyValue/setProperty
|
|
- Navigation: parentNode, childNodes, firstChild, lastChild, nextSibling, previousSibling
|
|
- Element navigation: firstElementChild, nextElementSibling, children
|
|
- Methods: cloneNode (shallow and deep), contains, matches, closest
|
|
- nodeType, nodeName for different node types
|
|
- [ ] 6.2 Run all existing test suites to verify no regressions:
|
|
- `cargo test -p web_api`
|
|
- `cargo test -p dom`
|
|
- `cargo test -p rust_browser --test js_dom_tests`
|
|
- `cargo test -p rust_browser --test js_tests`
|
|
- `cargo test -p rust_browser --test js_events`
|
|
- `cargo test -p rust_browser --test goldens`
|
|
- [ ] 6.3 Update `docs/HTML5_Implementation_Checklist.md`:
|
|
- Check off DOMTokenList (classList)
|
|
- Check off attribute access APIs
|
|
- Check off navigation properties
|
|
- Check off reflecting IDL attributes (id, className)
|
|
- [ ] 6.4 Run `just ci` -- full validation pass
|
|
|
|
## Dev Notes
|
|
|
|
### Key Architecture Decisions
|
|
|
|
**All bindings go through HostEnvironment trait.** JS code accesses DOM via `JsValue::HostObject { id, type_name }`. The DomHost implementation of `get_property()`, `set_property()`, and `call_method()` dispatches based on `type_name` ("Element", "Document", "DOMTokenList", "CSSStyleDeclaration", "HTMLCollection", "NodeList"). No direct DOM access from JS engine.
|
|
|
|
**classList and style return sub-HostObjects.** `element.classList` returns `HostObject { id: CLASSLIST_ID_BASE + node_id, type_name: "DOMTokenList" }`. `element.style` returns `HostObject { id: STYLE_ID_BASE + node_id, type_name: "CSSStyleDeclaration" }`. These are live views -- they read/write the underlying DOM attribute each time.
|
|
|
|
**ID space allocation pattern** (existing):
|
|
```
|
|
u64::MAX — DOCUMENT_HOST_ID
|
|
u64::MAX-2 — WINDOW_HOST_ID
|
|
u64::MAX-1000 ↓ — EVENT_HOST_ID_BASE
|
|
u64::MAX-100_000 ↓ — PROMISE_ID_START
|
|
u64::MAX-200_000 ↓ — COLLECTION_ID_BASE
|
|
0 ↑ — DOM NodeIds (elements, text nodes)
|
|
```
|
|
New ranges needed:
|
|
```
|
|
u64::MAX-300_000 ↓ — CLASSLIST_ID_BASE (classList objects)
|
|
u64::MAX-400_000 ↓ — STYLE_ID_BASE (style objects)
|
|
u64::MAX-500_000 ↓ — NODELIST_ID_BASE (childNodes NodeLists)
|
|
```
|
|
|
|
**Inline style parsing is simple string manipulation.** Don't use the full CSS parser for `element.style` access. Split on `;`, then `:` for each property. Serialize back with proper spacing. The full CSS parser is overkill for inline style get/set.
|
|
|
|
### Implementation Patterns from Existing Code
|
|
|
|
**Property getter dispatch** (in `host_environment.rs:get_property()`):
|
|
```rust
|
|
match obj_type {
|
|
"Element" | "DocumentFragment" => match property {
|
|
"textContent" => { /* ... */ }
|
|
"innerHTML" => { /* ... */ }
|
|
// Add: "id", "className", "tagName", "parentNode", "childNodes", etc.
|
|
},
|
|
"Document" => match property {
|
|
"readyState" => { /* ... */ }
|
|
// ...
|
|
},
|
|
// Add: "DOMTokenList", "CSSStyleDeclaration", "NodeList"
|
|
}
|
|
```
|
|
|
|
**Method call dispatch** (in `host_environment.rs:call_method()`):
|
|
```rust
|
|
match obj_type {
|
|
"Element" | "DocumentFragment" => match method {
|
|
"appendChild" => { /* ... */ }
|
|
// Add: "getAttribute", "setAttribute", "cloneNode", "matches", "closest"
|
|
},
|
|
}
|
|
```
|
|
|
|
**HostObject creation**:
|
|
```rust
|
|
Ok(JsValue::HostObject {
|
|
id: node_id.index() as u64,
|
|
type_name: "Element".to_string(),
|
|
})
|
|
```
|
|
|
|
### Critical Implementation Details
|
|
|
|
**`element.id` setter must update the document's ID index.** When `element.id = "newId"`, the DOM's `get_element_by_id()` lookup must reflect the change. Verify that `document.set_attribute(node_id, "id", value)` updates the internal index.
|
|
|
|
**`classList` mutations must update the `class` attribute.** After `classList.add("foo")`, `getAttribute("class")` must include `"foo"`. Implement classList by reading the current `class` attribute value, tokenizing on whitespace, mutating the token list, and writing back.
|
|
|
|
**camelCase-to-kebab conversion for style properties:**
|
|
```rust
|
|
fn camel_to_kebab(name: &str) -> String {
|
|
// "backgroundColor" → "background-color"
|
|
// "marginTop" → "margin-top"
|
|
// "cssFloat" → "float" (special case)
|
|
// "WebkitTransform" → "-webkit-transform" (if starts with uppercase)
|
|
}
|
|
```
|
|
|
|
**`childNodes` returns ALL children (text, comment, element).** This is different from `children` which returns only elements. The existing `LiveCollection` infrastructure may need adaptation since it currently only handles elements.
|
|
|
|
**`cloneNode(deep)` must NOT copy event listeners.** Per spec, cloned nodes don't carry over `addEventListener` registrations.
|
|
|
|
**`matches(selector)` reuse.** The `selectors` crate already has a `matches()` function used by `querySelector`. Reuse the same selector compilation and matching logic.
|
|
|
|
### Dependency on Other Stories
|
|
|
|
**No hard dependencies.** This story builds on existing web_api infrastructure. The property descriptor system (Story 3.5) is not required -- inline style and attribute access use the HostEnvironment bridge, not JS property descriptors.
|
|
|
|
**Story 3.9 (Event Dispatch Completeness)** builds on this story's DOM navigation (e.g., building ancestor chains for capture/bubble phases). Tree navigation properties from Task 2 will be used by event dispatch.
|
|
|
|
### Previous Story Patterns
|
|
|
|
From Story 3.4 (ES modules) and 3.7 (strict mode):
|
|
- HostEnvironment methods already have extensive match arms -- adding more follows the established pattern
|
|
- All new host object types need ID range reservation to avoid collisions
|
|
- Integration tests in `tests/js_dom_tests.rs` follow existing patterns
|
|
- Run `just ci` after each task
|
|
|
|
### Risk Assessment
|
|
|
|
**MEDIUM: host_environment.rs file size.** This file is already 1100+ lines. Adding attribute access, navigation, classList, and style dispatch will make it larger. Consider extracting DOMTokenList and CSSStyleDeclaration dispatch into separate files imported by host_environment.
|
|
|
|
**MEDIUM: Inline style parsing correctness.** CSS property values can contain colons and semicolons (e.g., `background: url("data:image/png;base64,...")` ). Simple string splitting may break on edge cases. Use a more robust parser that respects quotes and parentheses.
|
|
|
|
**LOW: Tree navigation properties.** Most DOM navigation methods already exist in the `dom` crate. Just need to wire them through HostEnvironment.
|
|
|
|
**LOW: Attribute access.** Direct delegation to existing `dom` crate methods.
|
|
|
|
### Phased Implementation Strategy
|
|
|
|
**Phase A -- Attributes + Identity (Tasks 1-2):** Foundation. Wire existing DOM methods through HostEnvironment. Quick wins.
|
|
|
|
**Phase B -- classList (Task 3):** New sub-HostObject type with DOMTokenList protocol. Moderate complexity.
|
|
|
|
**Phase C -- Style Bridge (Task 4):** New sub-HostObject type with camelCase conversion. Moderate complexity.
|
|
|
|
**Phase D -- Additional Methods (Task 5):** cloneNode, contains, matches, closest. Each is standalone.
|
|
|
|
**Phase E -- Testing + Validation (Task 6):** After all bindings implemented.
|
|
|
|
### Project Structure Notes
|
|
|
|
- All DOM binding changes in `crates/web_api/src/dom_host/` (Layer 1) -- no layer violations
|
|
- DOM operations in `crates/dom/src/` (Layer 1) -- may need minor additions
|
|
- New files: `classlist.rs`, `style_bridge.rs` in `crates/web_api/src/dom_host/`
|
|
- `web_api` depends on `dom`, `css`, `selectors` (all Layer 1, horizontal deps OK)
|
|
- No `unsafe` code needed
|
|
- No new external dependencies
|
|
|
|
### References
|
|
|
|
- [DOM Living Standard §4.4 -- Interface Node](https://dom.spec.whatwg.org/#interface-node) -- parentNode, childNodes, firstChild, etc.
|
|
- [DOM Living Standard §4.5 -- Interface Element](https://dom.spec.whatwg.org/#interface-element) -- getAttribute, setAttribute, matches, closest
|
|
- [DOM Living Standard §7.1 -- DOMTokenList](https://dom.spec.whatwg.org/#interface-domtokenlist) -- classList interface
|
|
- [DOM Living Standard §7.3 -- NamedNodeMap](https://dom.spec.whatwg.org/#interface-namednodemap) -- Attribute access
|
|
- [CSSOM §6.7 -- CSSStyleDeclaration](https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface) -- Inline style access
|
|
- [Source: crates/web_api/src/dom_host/host_environment.rs] -- Existing HostEnvironment impl (1100+ lines)
|
|
- [Source: crates/web_api/src/dom_host/mod.rs] -- DomHost struct
|
|
- [Source: crates/web_api/src/dom_host/live_collection.rs] -- LiveCollection pattern for HTMLCollection
|
|
- [Source: crates/dom/src/document.rs] -- DOM operations (get_attribute, set_attribute, parent, children, etc.)
|
|
- [Source: crates/web_api/src/lib.rs] -- WebApiFacade with ID space allocation constants
|
|
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.8] -- Story requirements
|
|
- [Source: _bmad-output/planning-artifacts/architecture.md#Web API Crate Scaling] -- Module-per-domain organization
|
|
|
|
## Dev Agent Record
|
|
|
|
### Agent Model Used
|
|
|
|
{{agent_model_name_version}}
|
|
|
|
### Debug Log References
|
|
|
|
### Completion Notes List
|
|
|
|
### File List
|