diff --git a/build.log b/build.log new file mode 100644 index 0000000..f7a5ee5 --- /dev/null +++ b/build.log @@ -0,0 +1,283 @@ + Compiling proc-macro2 v1.0.106 + Compiling quote v1.0.45 + Compiling unicode-ident v1.0.24 + Compiling libc v0.2.186 + Compiling cfg-if v1.0.4 + Compiling parking_lot_core v0.9.12 + Compiling serde_core v1.0.228 + Compiling serde v1.0.228 + Compiling smallvec v1.15.2 + Compiling icu_properties_data v2.2.0 + Compiling icu_normalizer_data v2.2.0 + Compiling stable_deref_trait v1.2.1 + Compiling pin-project-lite v0.2.17 + Compiling version_check v0.9.5 + Compiling scopeguard v1.2.0 + Compiling itoa v1.0.18 + Compiling memchr v2.8.2 + Compiling typenum v1.20.1 + Compiling lock_api v0.4.14 + Compiling bytes v1.11.1 + Compiling crc32fast v1.5.0 + Compiling zmij v1.0.21 + Compiling generic-array v0.14.7 + Compiling rand_core v0.6.4 + Compiling thiserror v2.0.18 + Compiling rand v0.8.6 + Compiling serde_json v1.0.150 + Compiling subtle v2.6.1 + Compiling futures-core v0.3.32 + Compiling syn v2.0.117 + Compiling glob v0.3.3 + Compiling litemap v0.8.2 + Compiling writeable v0.6.3 + Compiling utf8_iter v1.0.4 + Compiling prettyplease v0.2.37 + Compiling clang-sys v1.8.1 + Compiling siphasher v1.0.3 + Compiling getrandom v0.2.17 + Compiling errno v0.3.14 + Compiling mio v1.2.1 + Compiling socket2 v0.6.4 + Compiling signal-hook-registry v1.4.8 + Compiling autocfg v1.5.1 + Compiling parking_lot v0.12.5 + Compiling futures-sink v0.3.32 + Compiling phf_shared v0.11.3 + Compiling crypto-common v0.1.7 + Compiling rustix v0.38.44 + Compiling regex-syntax v0.8.11 + Compiling percent-encoding v2.3.2 + Compiling num-traits v0.2.19 + Compiling either v1.16.0 + Compiling bitflags v2.13.0 + Compiling minimal-lexical v0.2.1 + Compiling phf_generator v0.11.3 + Compiling futures-channel v0.3.32 + Compiling nom v7.1.3 + Compiling libloading v0.8.9 + Compiling linux-raw-sys v0.4.15 + Compiling bindgen v0.71.1 + Compiling rustc-hash v2.1.2 + Compiling itertools v0.13.0 + Compiling log v0.4.32 + Compiling futures-io v0.3.32 + Compiling shlex v1.3.0 + Compiling adler2 v2.0.1 + Compiling home v0.5.12 + Compiling once_cell v1.21.4 + Compiling hashbrown v0.17.1 + Compiling regex-automata v0.4.14 + Compiling equivalent v1.0.2 + Compiling radium v0.7.0 + Compiling slab v0.4.12 + Compiling futures-task v0.3.32 + Compiling indexmap v2.14.0 + Compiling cexpr v0.6.0 + Compiling miniz_oxide v0.8.9 + Compiling gzip-header v1.1.0 + Compiling form_urlencoded v1.2.2 + Compiling fslock v0.2.1 + Compiling find-msvc-tools v0.1.9 + Compiling shlex v2.0.1 + Compiling cc v1.2.64 + Compiling which v6.0.3 + Compiling http v1.4.2 + Compiling paste v1.0.15 + Compiling synstructure v0.13.2 + Compiling regex v1.12.4 + Compiling heck v0.5.0 + Compiling httparse v1.10.1 + Compiling ring v0.17.14 + Compiling tracing-core v0.1.36 + Compiling phf_codegen v0.11.3 + Compiling block-buffer v0.10.4 + Compiling alloc-no-stdlib v2.0.4 + Compiling zerofrom-derive v0.1.7 + Compiling yoke-derive v0.8.2 + Compiling zerovec-derive v0.11.3 + Compiling displaydoc v0.2.6 + Compiling serde_derive v1.0.228 + Compiling tokio-macros v2.7.0 + Compiling futures-macro v0.3.32 + Compiling thiserror-impl v2.0.18 + Compiling deno_error_macro v0.6.1 + Compiling sys_traits_macros v0.1.0 + Compiling zerofrom v0.1.8 + Compiling yoke v0.8.3 + Compiling tokio v1.52.3 + Compiling zerotrie v0.2.4 + Compiling zerovec v0.11.6 + Compiling futures-util v0.3.32 + Compiling tinystr v0.8.3 + Compiling potential_utf v0.1.5 + Compiling icu_locale_core v2.2.0 + Compiling icu_collections v2.2.0 + Compiling cpufeatures v0.2.17 + Compiling zeroize v1.9.0 + Compiling anyhow v1.0.102 + Compiling icu_provider v2.2.0 + Compiling getrandom v0.4.2 + Compiling icu_properties v2.2.0 + Compiling icu_normalizer v2.2.0 + Compiling idna_adapter v1.2.2 + Compiling idna v1.1.0 + Compiling rustls-pki-types v1.14.1 + Compiling url v2.5.8 + Compiling v8 v137.3.0 + Compiling alloc-stdlib v0.2.2 + Compiling tracing-attributes v0.1.31 + Compiling digest v0.10.7 + Compiling http-body v1.0.1 + Compiling new_debug_unreachable v1.0.6 + Compiling simd-adler32 v0.3.9 + Compiling untrusted v0.9.0 + Compiling proc-macro-rules-macros v0.4.0 + Compiling tracing v0.1.44 + Compiling deno_error v0.6.1 + Compiling brotli-decompressor v5.0.1 + Compiling phf_macros v0.11.3 + Compiling strum_macros v0.27.2 + Compiling capacity_builder_macros v0.3.0 + Compiling pin-project-internal v1.1.13 + Compiling block-padding v0.3.3 + Compiling tap v1.0.1 + Compiling rustls v0.23.40 + Compiling tower-service v0.3.3 + Compiling try-lock v0.2.5 + Compiling utf-8 v0.7.6 + Compiling wyz v0.5.1 + Compiling want v0.3.1 + Compiling brotli v8.0.3 + Compiling inout v0.1.4 + Compiling phf v0.11.3 + Compiling num-integer v0.1.46 + Compiling flate2 v1.1.9 + Compiling rustls-webpki v0.103.13 + Compiling proc-macro-rules v0.4.0 + Compiling string_cache_codegen v0.5.4 + Compiling stringcase v0.4.0 + Compiling funty v2.0.0 + Compiling compression-core v0.4.32 + Compiling uuid v1.23.3 + Compiling atomic-waker v1.1.2 + Compiling mac v0.1.1 + Compiling outref v0.5.2 + Compiling precomputed-hash v0.1.1 + Compiling vsimd v0.8.0 + Compiling futf v0.1.5 + Compiling hyper v1.10.1 + Compiling debugid v0.8.0 + Compiling bitvec v1.0.1 + Compiling markup5ever v0.14.1 + Compiling strum v0.27.2 + Compiling base64-simd v0.8.0 + Compiling deno_ops v0.226.0 + Compiling futures-executor v0.3.32 + Compiling compression-codecs v0.4.38 + Compiling num-bigint v0.4.6 + Compiling cipher v0.4.4 + Compiling sys_traits v0.1.28 + Compiling sync_wrapper v1.0.2 + Compiling tower-layer v0.3.3 + Compiling ipnet v2.12.0 + Compiling dtoa v1.0.11 + Compiling unicode-id-start v1.4.0 + Compiling data-encoding v2.11.0 + Compiling bit-vec v0.8.0 + Compiling base64 v0.22.1 + Compiling if_chain v1.0.3 + Compiling zerocopy v0.8.52 + Compiling rustix v1.1.4 + Compiling getrandom v0.3.4 + Compiling serde_v8 v0.259.0 + Compiling hyper-util v0.1.20 + Compiling bit-set v0.8.0 + Compiling sourcemap v9.3.2 + Compiling dtoa-short v0.3.5 + Compiling tower v0.5.3 + Compiling deno_path_util v0.4.0 + Compiling tokio-rustls v0.26.4 + Compiling async-compression v0.4.42 + Compiling futures v0.3.32 + Compiling tendril v0.4.3 + Compiling string_cache v0.8.9 + Compiling deno_unsync v0.4.4 + Compiling pin-project v1.1.13 + Compiling capacity_builder v0.5.0 + Compiling wasm_dep_analyzer v0.3.0 + Compiling tokio-util v0.7.18 + Compiling http-body-util v0.1.3 + Compiling webpki-roots v1.0.7 + Compiling bincode v1.3.3 + Compiling cssparser-macros v0.6.1 + Compiling selectors v0.26.0 + Compiling universal-hash v0.5.1 + Compiling byteorder v1.5.0 + Compiling deno_core_icudata v0.74.0 + Compiling ryu v1.0.23 + Compiling cooked-waker v5.0.0 + Compiling opaque-debug v0.3.1 + Compiling az v1.3.0 + Compiling static_assertions v1.1.0 + Compiling linux-raw-sys v0.12.1 + Compiling serde_urlencoded v0.7.1 + Compiling polyval v0.6.2 + Compiling fxhash v0.2.1 + Compiling cssparser v0.34.0 + Compiling tower-http v0.6.11 + Compiling hyper-rustls v0.27.9 + Compiling hmac v0.12.1 + Compiling sha1 v0.10.6 + Compiling deno_core v0.350.0 + Compiling derive_more v0.99.20 + Compiling match_token v0.1.0 + Compiling servo_arc v0.4.3 + Compiling fastrand v2.4.1 + Compiling tempfile v3.27.0 + Compiling html5ever v0.29.1 + Compiling rand_core v0.9.5 + Compiling reqwest v0.12.28 + Compiling ghash v0.5.1 + Compiling ctr v0.9.2 + Compiling aes v0.8.4 + Compiling async-trait v0.1.89 + Compiling aead v0.5.2 + Compiling encoding_rs v0.8.35 + Compiling aes-gcm v0.10.3 + Compiling ppv-lite86 v0.2.21 + Compiling obscura-dom v0.1.0 (/root/obscura2/merge471/crates/obscura-dom) + Compiling hkdf v0.12.4 + Compiling pbkdf2 v0.12.2 + Compiling cbc v0.1.2 + Compiling sha2 v0.10.9 + Compiling rand_chacha v0.9.0 + Compiling utf8parse v0.2.2 + Compiling anstyle-parse v1.0.0 + Compiling rand v0.9.4 + Compiling obscura-net v0.1.0 (/root/obscura2/merge471/crates/obscura-net) + Compiling colorchoice v1.0.5 + Compiling anstyle v1.0.14 + Compiling anstyle-query v1.1.5 + Compiling is_terminal_polyfill v1.70.2 + Compiling anstream v1.0.0 + Compiling tungstenite v0.26.2 + Compiling strsim v0.11.1 + Compiling lazy_static v1.5.0 + Compiling clap_lex v1.1.0 + Compiling obscura-js v0.1.0 (/root/obscura2/merge471/crates/obscura-js) + Compiling clap_builder v4.6.0 + Compiling sharded-slab v0.1.7 + Compiling tokio-tungstenite v0.26.2 + Compiling tracing-log v0.2.0 + Compiling clap_derive v4.6.1 + Compiling thread_local v1.1.9 + Compiling nu-ansi-term v0.50.3 + Compiling obscura-cli v0.1.0 (/root/obscura2/merge471/crates/obscura-cli) + Compiling matchers v0.2.0 + Compiling tracing-subscriber v0.3.23 + Compiling clap v4.6.1 + Compiling obscura-browser v0.1.0 (/root/obscura2/merge471/crates/obscura-browser) + Compiling obscura-cdp v0.1.0 (/root/obscura2/merge471/crates/obscura-cdp) + Compiling obscura-mcp v0.1.0 (/root/obscura2/merge471/crates/obscura-mcp) + Finished `release` profile [optimized] target(s) in 1m 52s diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js index 6da3447..4a0f53f 100644 --- a/crates/obscura-js/js/bootstrap.js +++ b/crates/obscura-js/js/bootstrap.js @@ -2710,42 +2710,63 @@ class Document extends Node { } return null; }, - firstChild() { - let child = this.currentNode.firstChild; - while (child) { - if (this._accept(child)) { this.currentNode = child; return child; } - child = child.nextSibling; + // DOM 6.1 "traverse children" (issue #469). The movers used to step + // straight to the next sibling when a node was not accepted, so a + // FILTER_SKIP node hid its children instead of exposing them. `edge` and + // `step` pick the direction: first/next for forward, last/previous for + // backward. + _traverseChildren(edge, step) { + let node = this.currentNode[edge]; + while (node) { + const verdict = this._filter(node); + if (verdict === 1) { this.currentNode = node; return node; } + // Only SKIP leaves the children eligible; REJECT prunes the subtree. + if (verdict === 3) { + const child = node[edge]; + if (child) { node = child; continue; } + } + // Subtree exhausted: step sideways, climbing out without passing + // root or the node the walk started from. + while (node) { + const sibling = node[step]; + if (sibling) { node = sibling; break; } + const parent = node.parentNode; + if (!parent || parent === this.root || parent === this.currentNode) return null; + node = parent; + } } return null; }, - lastChild() { - let child = this.currentNode.lastChild; - while (child) { - if (this._accept(child)) { this.currentNode = child; return child; } - child = child.previousSibling; + // DOM 6.1 "traverse siblings" (issue #469). + _traverseSiblings(edge, step) { + let node = this.currentNode; + if (node === this.root) return null; + for (;;) { + let sibling = node[step]; + while (sibling) { + node = sibling; + const verdict = this._filter(node); + if (verdict === 1) { this.currentNode = node; return node; } + // Descend into a skipped sibling's subtree; a rejected one is + // off-limits, and a childless one has nothing to descend into. + sibling = node[edge]; + if (verdict === 2 || !sibling) sibling = node[step]; + } + node = node.parentNode; + if (!node || node === this.root) return null; + // An accepted parent is where the walk would go next, so there is no + // sibling to return. + if (this._filter(node) === 1) return null; } - return null; - }, - nextSibling() { - let sibling = this.currentNode.nextSibling; - while (sibling) { - if (this._accept(sibling)) { this.currentNode = sibling; return sibling; } - sibling = sibling.nextSibling; - } - return null; - }, - previousSibling() { - let sibling = this.currentNode.previousSibling; - while (sibling) { - if (this._accept(sibling)) { this.currentNode = sibling; return sibling; } - sibling = sibling.previousSibling; - } - return null; }, + firstChild() { return this._traverseChildren('firstChild', 'nextSibling'); }, + lastChild() { return this._traverseChildren('lastChild', 'previousSibling'); }, + nextSibling() { return this._traverseSiblings('firstChild', 'nextSibling'); }, + previousSibling() { return this._traverseSiblings('lastChild', 'previousSibling'); }, // DOM 6.1 "parentNode" (issue #475). The old version looked only at the // immediate parent, so it couldn't climb past a skipped ancestor; it also // excluded `root` as a result yet stepped to root's own parent when - // currentNode was root — returning a node OUTSIDE the walker's subtree. + // currentNode was root, returning a node OUTSIDE the walker's subtree. // The loop's `node !== this.root` guard is what keeps the walk inside // root while still allowing root itself to be returned. parentNode() { diff --git a/crates/obscura-js/src/runtime.rs b/crates/obscura-js/src/runtime.rs index 1871d5b..fdfeb4d 100644 --- a/crates/obscura-js/src/runtime.rs +++ b/crates/obscura-js/src/runtime.rs @@ -1920,6 +1920,99 @@ mod tests { assert_eq!(result.value.unwrap(), serde_json::json!([1, 1, 400])); } + /// Issue #469: FILTER_SKIP leaves a skipped node's children eligible, so + /// firstChild()/lastChild() must descend into them. FILTER_REJECT must not. + #[test] + fn tree_walker_child_movers_descend_on_skip_but_not_on_reject() { + let mut rt = setup_runtime( + r#"
"#, + ); + let result = rt + .evaluate( + r#" + const root = document.getElementById('root'); + function mover(verdict, method) { + const w = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + return node.tagName === 'SECTION' ? verdict : NodeFilter.FILTER_ACCEPT; + } + }); + const found = w[method](); + return found ? found.tagName : null; + } + return [ + mover(NodeFilter.FILTER_SKIP, 'firstChild'), + mover(NodeFilter.FILTER_SKIP, 'lastChild'), + mover(NodeFilter.FILTER_REJECT, 'firstChild'), + mover(NodeFilter.FILTER_REJECT, 'lastChild'), + ]; + "#, + ) + .unwrap(); + // SKIP descends into
; REJECT prunes it and finds nothing else. + assert_eq!(result, serde_json::json!(["A", "B", null, null])); + } + + /// Issue #469: nextSibling()/previousSibling() must descend into a skipped + /// sibling's subtree rather than stepping straight over it. + #[test] + fn tree_walker_sibling_movers_descend_into_skipped_siblings() { + let mut rt = setup_runtime( + r#"

"#, + ); + let result = rt + .evaluate( + r#" + const root = document.getElementById('root'); + function mover(verdict, method, from) { + const w = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + return node.tagName === 'SECTION' ? verdict : NodeFilter.FILTER_ACCEPT; + } + }); + w.currentNode = document.getElementById(from); + const found = w[method](); + return found ? found.tagName : null; + } + return [ + //
is skipped, so its child is the next sibling. + mover(NodeFilter.FILTER_SKIP, 'nextSibling', 'start'), + // Rejected: the subtree is off-limits, so skip past to . + mover(NodeFilter.FILTER_REJECT, 'nextSibling', 'start'), + ]; + "#, + ) + .unwrap(); + assert_eq!(result, serde_json::json!(["A", "Q"])); + } + + /// Issue #469: the backward sibling mover descends to *last* children. + #[test] + fn tree_walker_previous_sibling_descends_to_last_child() { + let mut rt = setup_runtime( + r#"

"#, + ); + let result = rt + .evaluate( + r#" + const root = document.getElementById('root'); + const w = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + return node.tagName === 'SECTION' + ? NodeFilter.FILTER_SKIP + : NodeFilter.FILTER_ACCEPT; + } + }); + w.currentNode = document.getElementById('start'); + const found = w.previousSibling(); + return found ? found.tagName : null; + "#, + ) + .unwrap(); + // Reverse order descends to
's last child, not its first. + assert_eq!(result, serde_json::json!("B")); + } + #[test] fn append_child_flattens_document_fragment() { let mut rt = setup_runtime(r#"
"#);