WPT conformance: dom/ranges, FileAPI, detached documents (#236)
* dom: complete detached documents; add ProgressEvent and parseHTMLUnsafe createHTMLDocument returned a document whose body was null: parsing the skeleton string as innerHTML of <html> collapsed through the fragment parser and kept only <title>. Build head>title and body explicitly instead. The DOMParser doc node also gains appendChild/removeChild/insertBefore and a doctype getter, and createDocument carries its doctype argument. This lets dom/common.js setupRangeTests() finish (it builds fixtures with new Document(), createHTMLDocument(), createDocument()) instead of aborting at harness setup, so the dom/ranges files run. Range-collapse 186/186, Range-attributes 1/1, Range-cloneRange 55/62, StaticRange-constructor 14/17. The large O(ranges x points) matrix files (Range-set, comparePoint, compareBoundaryPoints) still time out: the boundary-point comparison walks the tree through per-step DOM ops, which is too slow for those matrices. Speeding that up (a Rust node-position op) is a separate change. Also add globalThis.ProgressEvent (Event subclass with lengthComputable/ loaded/total) and the static Document.parseHTMLUnsafe(html). * perf(dom): compute Range tree-order in Rust ops The Range boundary-point algorithms compared nodes by walking ancestor chains and sibling lists from JS, one DOM op per step. The dom/ranges matrices (testRanges x testPoints, thousands of comparisons) made that time out. Add node_index, compare_order, and node_root ops that do the walk in Rust over the DOM tree (slab lookups, no per-step FFI), and use them in _rngNodeIndex, _rngOrder, and _rngRoot. Range-comparePoint 3534/5580, Range-isPointInRange 5197/5733, Range-compareBoundaryPoints 6441/9313, all completing in under 10s where they previously did not finish. Not on any engine hot path (querySelector, mutation, navigation, fetch do not use these). * fileapi: real byte-backed Blob/File, FileReader state machine, Request.blob Blob was a one-line stub that string-joined its parts (wrong size for binary or multibyte input) and exposed only text(). Replace it with a Uint8Array-backed Blob: normalizes string/ArrayBuffer/typed-array/Blob parts, correct size, slice(), text()/arrayBuffer()/bytes(), type normalization (lowercased, cleared when non-ASCII), the endings option (native CRLF normalization), and Symbol.toStringTag. File adds name/lastModified, the 2-argument requirement, and its own toStringTag. FileReader becomes a real state machine: EMPTY/LOADING/DONE, throws InvalidStateError when a read starts while already LOADING, and fires loadstart/progress/load/loadend (and abort) to both on* handlers and addEventListener. readAsText/DataURL/ArrayBuffer/BinaryString produce correct results. Request gains blob(). FileReader-multiple-reads 6/6, File-constructor-endings 10/11, Blob-slice 141/150.
This commit is contained in:
1 parent
387c74f26a
commit
9c3dd6b21a
2 files changed
+228
-30
No files matched your search
@@ -337,10 +337,84 @@ fn op_dom(state: &OpState, #[string] cmd: String, #[string] arg1: String, #[stri
|
||||
let other = arg2.parse::<u32>().unwrap_or(0);
|
||||
dom.descendants(NodeId::new(nid)).contains(&NodeId::new(other)).to_string()
|
||||
}
|
||||
// Index of a node among its parent's children. Walks prev siblings in
|
||||
// Rust, avoiding the per-step JS->op round trips a Range comparison
|
||||
// would otherwise make.
|
||||
"node_index" => {
|
||||
let nid = arg1.parse::<u32>().unwrap_or(0);
|
||||
node_child_index(dom, NodeId::new(nid)).to_string()
|
||||
}
|
||||
// Document (preorder) tree order of two nodes: -1 if a precedes b, 1 if
|
||||
// a follows b, 0 if equal. Used by the Range boundary-point algorithms.
|
||||
"compare_order" => {
|
||||
let a = NodeId::new(arg1.parse::<u32>().unwrap_or(0));
|
||||
let b = NodeId::new(arg2.parse::<u32>().unwrap_or(0));
|
||||
compare_node_order(dom, a, b).to_string()
|
||||
}
|
||||
// Root (topmost ancestor) of a node, in one op rather than an O(depth)
|
||||
// walk of parentNode ops from JS.
|
||||
"node_root" => {
|
||||
let mut cur = NodeId::new(arg1.parse::<u32>().unwrap_or(0));
|
||||
while let Some(p) = dom.get_node(cur).and_then(|x| x.parent) {
|
||||
cur = p;
|
||||
}
|
||||
cur.index().to_string()
|
||||
}
|
||||
_ => "null".into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Index of `n` among its parent's children (0-based).
|
||||
fn node_child_index(dom: &DomTree, n: NodeId) -> usize {
|
||||
let mut i = 0usize;
|
||||
let mut cur = dom.get_node(n).and_then(|x| x.prev_sibling);
|
||||
while let Some(p) = cur {
|
||||
i += 1;
|
||||
cur = dom.get_node(p).and_then(|x| x.prev_sibling);
|
||||
}
|
||||
i
|
||||
}
|
||||
|
||||
/// Ancestor chain of `n` from the root down to `n` (root first).
|
||||
fn node_ancestors_root_first(dom: &DomTree, n: NodeId) -> Vec<NodeId> {
|
||||
let mut v = vec![n];
|
||||
let mut cur = n;
|
||||
while let Some(p) = dom.get_node(cur).and_then(|x| x.parent) {
|
||||
v.push(p);
|
||||
cur = p;
|
||||
}
|
||||
v.reverse();
|
||||
v
|
||||
}
|
||||
|
||||
/// Preorder (document) order comparison of two nodes: -1 before, 1 after, 0 same.
|
||||
fn compare_node_order(dom: &DomTree, a: NodeId, b: NodeId) -> i32 {
|
||||
if a == b {
|
||||
return 0;
|
||||
}
|
||||
let aa = node_ancestors_root_first(dom, a);
|
||||
let bb = node_ancestors_root_first(dom, b);
|
||||
// Different roots: order is undefined per spec; keep it stable by node id.
|
||||
if aa[0] != bb[0] {
|
||||
return if a.index() < b.index() { -1 } else { 1 };
|
||||
}
|
||||
let mut i = 0usize;
|
||||
while i < aa.len() && i < bb.len() && aa[i] == bb[i] {
|
||||
i += 1;
|
||||
}
|
||||
if i >= aa.len() {
|
||||
return -1; // a is an ancestor of b -> a precedes
|
||||
}
|
||||
if i >= bb.len() {
|
||||
return 1; // b is an ancestor of a -> a follows
|
||||
}
|
||||
if node_child_index(dom, aa[i]) < node_child_index(dom, bb[i]) {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_console_msg(state: &OpState, #[string] level: &str, #[string] msg: &str) {
|
||||
let _ = state;
|
||||
|
||||
Reference in new issue
Block a user