@miaskiewicz/turbo-dom 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +75 -2
- package/Cargo.toml +1 -1
- package/README.md +1 -1
- package/package.json +3 -3
- package/pkg/package.json +1 -1
- package/pkg/turbo_dom_parser_bg.wasm +0 -0
- package/pkg-web/package.json +1 -1
- package/pkg-web/turbo_dom_parser_bg.wasm +0 -0
- package/src/core.rs +187 -77
- package/turbo-dom-parser.darwin-arm64.node +0 -0
- package/turbo-dom-parser.linux-arm64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-musl.node +0 -0
- package/turbo-dom-parser.win32-x64-msvc.node +0 -0
package/Cargo.lock
CHANGED
|
@@ -17,6 +17,15 @@ version = "2.12.1"
|
|
|
17
17
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
18
|
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
|
|
19
19
|
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "block-buffer"
|
|
22
|
+
version = "0.10.4"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"generic-array",
|
|
27
|
+
]
|
|
28
|
+
|
|
20
29
|
[[package]]
|
|
21
30
|
name = "borsh"
|
|
22
31
|
version = "1.7.0"
|
|
@@ -60,6 +69,25 @@ dependencies = [
|
|
|
60
69
|
"unicode-segmentation",
|
|
61
70
|
]
|
|
62
71
|
|
|
72
|
+
[[package]]
|
|
73
|
+
name = "cpufeatures"
|
|
74
|
+
version = "0.2.17"
|
|
75
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
76
|
+
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
77
|
+
dependencies = [
|
|
78
|
+
"libc",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[[package]]
|
|
82
|
+
name = "crypto-common"
|
|
83
|
+
version = "0.1.7"
|
|
84
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
85
|
+
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
86
|
+
dependencies = [
|
|
87
|
+
"generic-array",
|
|
88
|
+
"typenum",
|
|
89
|
+
]
|
|
90
|
+
|
|
63
91
|
[[package]]
|
|
64
92
|
name = "ctor"
|
|
65
93
|
version = "0.2.9"
|
|
@@ -70,6 +98,16 @@ dependencies = [
|
|
|
70
98
|
"syn",
|
|
71
99
|
]
|
|
72
100
|
|
|
101
|
+
[[package]]
|
|
102
|
+
name = "digest"
|
|
103
|
+
version = "0.10.7"
|
|
104
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
105
|
+
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
106
|
+
dependencies = [
|
|
107
|
+
"block-buffer",
|
|
108
|
+
"crypto-common",
|
|
109
|
+
]
|
|
110
|
+
|
|
73
111
|
[[package]]
|
|
74
112
|
name = "fastrand"
|
|
75
113
|
version = "2.4.1"
|
|
@@ -100,6 +138,16 @@ dependencies = [
|
|
|
100
138
|
"slab",
|
|
101
139
|
]
|
|
102
140
|
|
|
141
|
+
[[package]]
|
|
142
|
+
name = "generic-array"
|
|
143
|
+
version = "0.14.7"
|
|
144
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
145
|
+
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
146
|
+
dependencies = [
|
|
147
|
+
"typenum",
|
|
148
|
+
"version_check",
|
|
149
|
+
]
|
|
150
|
+
|
|
103
151
|
[[package]]
|
|
104
152
|
name = "html5ever"
|
|
105
153
|
version = "0.39.0"
|
|
@@ -465,6 +513,17 @@ dependencies = [
|
|
|
465
513
|
"zmij",
|
|
466
514
|
]
|
|
467
515
|
|
|
516
|
+
[[package]]
|
|
517
|
+
name = "sha2"
|
|
518
|
+
version = "0.10.9"
|
|
519
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
520
|
+
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
521
|
+
dependencies = [
|
|
522
|
+
"cfg-if",
|
|
523
|
+
"cpufeatures",
|
|
524
|
+
"digest",
|
|
525
|
+
]
|
|
526
|
+
|
|
468
527
|
[[package]]
|
|
469
528
|
name = "siphasher"
|
|
470
529
|
version = "1.0.3"
|
|
@@ -541,19 +600,21 @@ dependencies = [
|
|
|
541
600
|
|
|
542
601
|
[[package]]
|
|
543
602
|
name = "turbo-dom"
|
|
544
|
-
version = "0.5.
|
|
603
|
+
version = "0.5.6"
|
|
545
604
|
dependencies = [
|
|
546
605
|
"html5ever",
|
|
606
|
+
"log",
|
|
547
607
|
"markup5ever_rcdom",
|
|
548
608
|
"rustc-hash",
|
|
549
609
|
"serde_json",
|
|
610
|
+
"sha2",
|
|
550
611
|
"smallvec",
|
|
551
612
|
"smol_str",
|
|
552
613
|
]
|
|
553
614
|
|
|
554
615
|
[[package]]
|
|
555
616
|
name = "turbo-dom-parser"
|
|
556
|
-
version = "0.5.
|
|
617
|
+
version = "0.5.6"
|
|
557
618
|
dependencies = [
|
|
558
619
|
"html5ever",
|
|
559
620
|
"markup5ever_rcdom",
|
|
@@ -566,6 +627,12 @@ dependencies = [
|
|
|
566
627
|
"wasm-bindgen",
|
|
567
628
|
]
|
|
568
629
|
|
|
630
|
+
[[package]]
|
|
631
|
+
name = "typenum"
|
|
632
|
+
version = "1.20.1"
|
|
633
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
634
|
+
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
|
635
|
+
|
|
569
636
|
[[package]]
|
|
570
637
|
name = "unicode-ident"
|
|
571
638
|
version = "1.0.24"
|
|
@@ -584,6 +651,12 @@ version = "0.7.6"
|
|
|
584
651
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
585
652
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
|
586
653
|
|
|
654
|
+
[[package]]
|
|
655
|
+
name = "version_check"
|
|
656
|
+
version = "0.9.5"
|
|
657
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
658
|
+
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
659
|
+
|
|
587
660
|
[[package]]
|
|
588
661
|
name = "wasm-bindgen"
|
|
589
662
|
version = "0.2.122"
|
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# turbo-dom
|
|
2
2
|
|
|
3
|
-
Current release: **0.5.
|
|
3
|
+
Current release: **0.5.6** (npm package, native parser and Rust DOM crate).
|
|
4
4
|
|
|
5
5
|
A faster, more spec-correct DOM for test runners — a drop-in-style alternative to
|
|
6
6
|
**jsdom** and **happy-dom** for **vitest** and **jest**.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miaskiewicz/turbo-dom",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Faster, more spec-correct DOM for test runners
|
|
3
|
+
"version": "0.5.6",
|
|
4
|
+
"description": "Faster, more spec-correct DOM for test runners \u2014 native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"bench:scorecard": "node bench/scorecard.mjs",
|
|
96
96
|
"conformance:rtdom": "cargo test -p turbo-dom rtdom::conformance::rtdom_html5lib_conformance_full_document -- --nocapture",
|
|
97
97
|
"build:rtdom": "cargo build -p turbo-dom --release",
|
|
98
|
-
"profile:churn": "cargo build --profile profiling --example churn_profile -p turbo-dom && echo 'built
|
|
98
|
+
"profile:churn": "cargo build --profile profiling --example churn_profile -p turbo-dom && echo 'built \u2014 CPU-profile with: ./target/profiling/examples/churn_profile & sample $! 20 -file target/churn.sample.txt'",
|
|
99
99
|
"bench:compare": "node bench/dom-compare.mjs",
|
|
100
100
|
"bench:compare:all": "RTDOM_JSON=\"$(cargo test -p turbo-dom --release rtdom::bench::compare_all -- --ignored --nocapture 2>/dev/null | sed -n 's/.*RTDOM_JSON //p')\" node --expose-gc --max-old-space-size=8192 bench/compare-all.mjs"
|
|
101
101
|
},
|
package/pkg/package.json
CHANGED
|
Binary file
|
package/pkg-web/package.json
CHANGED
|
Binary file
|
package/src/core.rs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
use html5ever::driver::ParseOpts;
|
|
9
9
|
use html5ever::tendril::TendrilSink;
|
|
10
10
|
use html5ever::tree_builder::TreeBuilderOpts;
|
|
11
|
-
use html5ever::{parse_document, parse_fragment,
|
|
11
|
+
use html5ever::{ns, parse_document, parse_fragment, LocalName, QualName};
|
|
12
12
|
use markup5ever_rcdom::{Handle, NodeData, RcDom};
|
|
13
13
|
|
|
14
14
|
/// DOM nodeType constants (subset html5ever can emit).
|
|
@@ -20,7 +20,11 @@ pub const DOCUMENT_NODE: u8 = 9;
|
|
|
20
20
|
pub const DOCUMENT_TYPE_NODE: u8 = 10;
|
|
21
21
|
pub const DOCUMENT_FRAGMENT_NODE: u8 = 11;
|
|
22
22
|
|
|
23
|
-
#[cfg_attr(
|
|
23
|
+
#[cfg_attr(
|
|
24
|
+
feature = "wasm-bind",
|
|
25
|
+
derive(serde::Serialize),
|
|
26
|
+
serde(rename_all = "camelCase")
|
|
27
|
+
)]
|
|
24
28
|
#[derive(Debug, Clone)]
|
|
25
29
|
pub struct Attr {
|
|
26
30
|
pub name: String,
|
|
@@ -30,7 +34,11 @@ pub struct Attr {
|
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
/// One marshaled DOM node. Nested children = complete tree, no lazy indices.
|
|
33
|
-
#[cfg_attr(
|
|
37
|
+
#[cfg_attr(
|
|
38
|
+
feature = "wasm-bind",
|
|
39
|
+
derive(serde::Serialize),
|
|
40
|
+
serde(rename_all = "camelCase")
|
|
41
|
+
)]
|
|
34
42
|
#[derive(Debug, Clone)]
|
|
35
43
|
pub struct Node {
|
|
36
44
|
/// DOM nodeType.
|
|
@@ -53,7 +61,10 @@ pub struct Node {
|
|
|
53
61
|
/// `<noscript>` content is parsed as markup rather than rawtext).
|
|
54
62
|
fn opts() -> ParseOpts {
|
|
55
63
|
ParseOpts {
|
|
56
|
-
tree_builder: TreeBuilderOpts {
|
|
64
|
+
tree_builder: TreeBuilderOpts {
|
|
65
|
+
scripting_enabled: false,
|
|
66
|
+
..Default::default()
|
|
67
|
+
},
|
|
57
68
|
..Default::default()
|
|
58
69
|
}
|
|
59
70
|
}
|
|
@@ -74,7 +85,11 @@ pub fn parse_html_document(html: &str) -> Node {
|
|
|
74
85
|
// the arrays and inflates node objects only on access (no eager full-tree alloc).
|
|
75
86
|
|
|
76
87
|
/// Index-addressed flat tree. Node index IS its id; node 0 is the document.
|
|
77
|
-
#[cfg_attr(
|
|
88
|
+
#[cfg_attr(
|
|
89
|
+
feature = "wasm-bind",
|
|
90
|
+
derive(serde::Serialize),
|
|
91
|
+
serde(rename_all = "camelCase")
|
|
92
|
+
)]
|
|
78
93
|
#[derive(Default)]
|
|
79
94
|
pub struct Soa {
|
|
80
95
|
pub node_type: Vec<u8>, // DOM nodeType
|
|
@@ -89,15 +104,15 @@ pub struct Soa {
|
|
|
89
104
|
pub attr_start: Vec<i32>, // offset into attr_* tables; -1 if none
|
|
90
105
|
pub attr_count: Vec<u16>, // attrs for this node
|
|
91
106
|
// flat attr tables — names/prefixes interned (highly repetitive), values pooled
|
|
92
|
-
pub attr_name_id: Vec<u32>,
|
|
93
|
-
pub attr_value_id: Vec<u32>,
|
|
107
|
+
pub attr_name_id: Vec<u32>, // index into attr_names
|
|
108
|
+
pub attr_value_id: Vec<u32>, // index into attr_values (interned, deduped)
|
|
94
109
|
pub attr_prefix_id: Vec<u32>, // index into attr_prefixes
|
|
95
110
|
// string tables (interned, deduped)
|
|
96
111
|
pub tag_names: Vec<String>,
|
|
97
112
|
pub attr_names: Vec<String>,
|
|
98
113
|
pub attr_prefixes: Vec<String>,
|
|
99
114
|
pub attr_values: Vec<String>, // interned attr value dictionary
|
|
100
|
-
pub strings: Vec<String>,
|
|
115
|
+
pub strings: Vec<String>, // text/comment/doctype data, pooled
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
struct SoaBuilder {
|
|
@@ -152,7 +167,7 @@ impl SoaBuilder {
|
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
// Allocate this node + descendants; return its index.
|
|
155
|
-
fn
|
|
170
|
+
fn alloc_node(&mut self, handle: &Handle, parent: i32) -> (i32, Option<Handle>) {
|
|
156
171
|
let idx = self.soa.node_type.len();
|
|
157
172
|
// push placeholders; fill scalars below
|
|
158
173
|
self.soa.node_type.push(0);
|
|
@@ -171,7 +186,11 @@ impl SoaBuilder {
|
|
|
171
186
|
|
|
172
187
|
match &handle.data {
|
|
173
188
|
NodeData::Document => self.soa.node_type[idx] = DOCUMENT_NODE,
|
|
174
|
-
NodeData::Doctype {
|
|
189
|
+
NodeData::Doctype {
|
|
190
|
+
name,
|
|
191
|
+
public_id,
|
|
192
|
+
system_id,
|
|
193
|
+
} => {
|
|
175
194
|
self.soa.node_type[idx] = DOCUMENT_TYPE_NODE;
|
|
176
195
|
self.soa.text_id[idx] = self.push_string(name);
|
|
177
196
|
self.soa.pub_id[idx] = self.push_string(public_id);
|
|
@@ -187,12 +206,23 @@ impl SoaBuilder {
|
|
|
187
206
|
}
|
|
188
207
|
NodeData::ProcessingInstruction { target, contents } => {
|
|
189
208
|
self.soa.node_type[idx] = PROCESSING_INSTRUCTION_NODE;
|
|
190
|
-
|
|
191
|
-
self.soa.text_id[idx] = self.push_string(
|
|
209
|
+
self.soa.tag_id[idx] = self.intern_tag(target);
|
|
210
|
+
self.soa.text_id[idx] = self.push_string(contents);
|
|
192
211
|
}
|
|
193
|
-
NodeData::Element {
|
|
212
|
+
NodeData::Element {
|
|
213
|
+
name,
|
|
214
|
+
attrs,
|
|
215
|
+
template_contents,
|
|
216
|
+
..
|
|
217
|
+
} => {
|
|
194
218
|
self.soa.node_type[idx] = ELEMENT_NODE;
|
|
195
|
-
self.soa.ns[idx] = if name.ns == ns!(svg) {
|
|
219
|
+
self.soa.ns[idx] = if name.ns == ns!(svg) {
|
|
220
|
+
1
|
|
221
|
+
} else if name.ns == ns!(mathml) {
|
|
222
|
+
2
|
|
223
|
+
} else {
|
|
224
|
+
0
|
|
225
|
+
};
|
|
196
226
|
self.soa.tag_id[idx] = self.intern_tag(&name.local);
|
|
197
227
|
let borrowed = attrs.borrow();
|
|
198
228
|
if !borrowed.is_empty() {
|
|
@@ -203,7 +233,12 @@ impl SoaBuilder {
|
|
|
203
233
|
self.soa.attr_name_id.push(nid);
|
|
204
234
|
let vid = self.intern_attr_value(&attr.value);
|
|
205
235
|
self.soa.attr_value_id.push(vid);
|
|
206
|
-
let pfx = attr
|
|
236
|
+
let pfx = attr
|
|
237
|
+
.name
|
|
238
|
+
.prefix
|
|
239
|
+
.as_ref()
|
|
240
|
+
.map(std::string::ToString::to_string)
|
|
241
|
+
.unwrap_or_default();
|
|
207
242
|
let pid = self.intern_attr_prefix(&pfx);
|
|
208
243
|
self.soa.attr_prefix_id.push(pid);
|
|
209
244
|
}
|
|
@@ -216,47 +251,49 @@ impl SoaBuilder {
|
|
|
216
251
|
}
|
|
217
252
|
}
|
|
218
253
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
(idx as i32, template_content)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
fn alloc(&mut self, root: &Handle, parent: i32) -> i32 {
|
|
258
|
+
let mut pending = vec![(root.clone(), parent, false)];
|
|
259
|
+
let mut last_child = Vec::new();
|
|
260
|
+
let first = self.soa.node_type.len() as i32;
|
|
261
|
+
while let Some((handle, parent, content)) = pending.pop() {
|
|
262
|
+
let (index, template) = self.alloc_node(&handle, parent);
|
|
263
|
+
last_child.push(-1);
|
|
264
|
+
if content {
|
|
265
|
+
self.soa.node_type[index as usize] = DOCUMENT_FRAGMENT_NODE;
|
|
266
|
+
self.soa.tag_id[index as usize] = self.intern_tag("content");
|
|
267
|
+
}
|
|
268
|
+
if parent >= 0 {
|
|
269
|
+
let previous = last_child[parent as usize];
|
|
270
|
+
if previous < 0 {
|
|
271
|
+
self.soa.first_child[parent as usize] = index;
|
|
272
|
+
} else {
|
|
273
|
+
self.soa.next_sib[previous as usize] = index;
|
|
274
|
+
}
|
|
275
|
+
last_child[parent as usize] = index;
|
|
276
|
+
}
|
|
277
|
+
if let Some(template) = template {
|
|
278
|
+
pending.push((template, index, true));
|
|
279
|
+
}
|
|
280
|
+
for child in handle.children.borrow().iter().rev() {
|
|
281
|
+
pending.push((child.clone(), index, false));
|
|
246
282
|
}
|
|
247
|
-
// link the content fragment as a child of the template
|
|
248
|
-
if prev == -1 { self.soa.first_child[idx] = cidx; } else { self.soa.next_sib[prev as usize] = cidx; }
|
|
249
|
-
prev = cidx;
|
|
250
283
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
idx as i32
|
|
284
|
+
first
|
|
254
285
|
}
|
|
255
286
|
}
|
|
256
287
|
|
|
257
288
|
/// Parse a full document into the `SoA` flat buffer.
|
|
258
289
|
#[must_use]
|
|
259
290
|
pub fn parse_html_soa(html: &str) -> Soa {
|
|
291
|
+
parse_html_soa_with_compat_mode(html).0
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/// Parse once and retain html5ever's document compatibility mode.
|
|
295
|
+
#[must_use]
|
|
296
|
+
pub fn parse_html_soa_with_compat_mode(html: &str) -> (Soa, &'static str) {
|
|
260
297
|
let dom = parse_document(RcDom::default(), opts())
|
|
261
298
|
.from_utf8()
|
|
262
299
|
.read_from(&mut html.as_bytes())
|
|
@@ -266,12 +303,26 @@ pub fn parse_html_soa(html: &str) -> Soa {
|
|
|
266
303
|
// pre-reserve typical intern-table sizes so the build loop doesn't rehash:
|
|
267
304
|
// tag/attr-name sets are small + bounded; values vary more.
|
|
268
305
|
tag_map: rustc_hash::FxHashMap::with_capacity_and_hasher(32, rustc_hash::FxBuildHasher),
|
|
269
|
-
attr_name_map: rustc_hash::FxHashMap::with_capacity_and_hasher(
|
|
270
|
-
|
|
271
|
-
|
|
306
|
+
attr_name_map: rustc_hash::FxHashMap::with_capacity_and_hasher(
|
|
307
|
+
64,
|
|
308
|
+
rustc_hash::FxBuildHasher,
|
|
309
|
+
),
|
|
310
|
+
attr_prefix_map: rustc_hash::FxHashMap::with_capacity_and_hasher(
|
|
311
|
+
8,
|
|
312
|
+
rustc_hash::FxBuildHasher,
|
|
313
|
+
),
|
|
314
|
+
attr_value_map: rustc_hash::FxHashMap::with_capacity_and_hasher(
|
|
315
|
+
128,
|
|
316
|
+
rustc_hash::FxBuildHasher,
|
|
317
|
+
),
|
|
272
318
|
};
|
|
273
319
|
b.alloc(&dom.document, -1);
|
|
274
|
-
|
|
320
|
+
let mode = if dom.quirks_mode.get() == html5ever::tree_builder::QuirksMode::Quirks {
|
|
321
|
+
"BackCompat"
|
|
322
|
+
} else {
|
|
323
|
+
"CSS1Compat"
|
|
324
|
+
};
|
|
325
|
+
(b.soa, mode)
|
|
275
326
|
}
|
|
276
327
|
|
|
277
328
|
/// Parse a document and return only the node count — no `core::Node` tree, no
|
|
@@ -348,18 +399,32 @@ pub fn parse_html_fragment_in(html: &str, context_ns: &str, context_local: &str)
|
|
|
348
399
|
frag
|
|
349
400
|
}
|
|
350
401
|
|
|
351
|
-
fn
|
|
402
|
+
fn shallow_node(handle: &Handle) -> Node {
|
|
352
403
|
let node = handle;
|
|
353
404
|
let mut public_id = String::new();
|
|
354
405
|
let mut system_id = String::new();
|
|
355
406
|
let (node_type, name, value, namespace, attrs) = match &node.data {
|
|
356
|
-
NodeData::Document =>
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
407
|
+
NodeData::Document => (
|
|
408
|
+
DOCUMENT_NODE,
|
|
409
|
+
"#document".to_string(),
|
|
410
|
+
String::new(),
|
|
411
|
+
String::new(),
|
|
412
|
+
Vec::new(),
|
|
413
|
+
),
|
|
414
|
+
NodeData::Doctype {
|
|
415
|
+
name,
|
|
416
|
+
public_id: pub_id,
|
|
417
|
+
system_id: sys_id,
|
|
418
|
+
} => {
|
|
360
419
|
public_id = pub_id.to_string();
|
|
361
420
|
system_id = sys_id.to_string();
|
|
362
|
-
(
|
|
421
|
+
(
|
|
422
|
+
DOCUMENT_TYPE_NODE,
|
|
423
|
+
name.to_string(),
|
|
424
|
+
String::new(),
|
|
425
|
+
String::new(),
|
|
426
|
+
Vec::new(),
|
|
427
|
+
)
|
|
363
428
|
}
|
|
364
429
|
NodeData::Text { contents } => (
|
|
365
430
|
TEXT_NODE,
|
|
@@ -389,7 +454,12 @@ fn walk(handle: &Handle) -> Node {
|
|
|
389
454
|
.map(|attr| Attr {
|
|
390
455
|
name: attr.name.local.to_string(),
|
|
391
456
|
value: attr.value.to_string(),
|
|
392
|
-
prefix: attr
|
|
457
|
+
prefix: attr
|
|
458
|
+
.name
|
|
459
|
+
.prefix
|
|
460
|
+
.as_ref()
|
|
461
|
+
.map(std::string::ToString::to_string)
|
|
462
|
+
.unwrap_or_default(),
|
|
393
463
|
})
|
|
394
464
|
.collect();
|
|
395
465
|
// html5lib short namespace form: "" (html), "svg", "math".
|
|
@@ -404,27 +474,67 @@ fn walk(handle: &Handle) -> Node {
|
|
|
404
474
|
}
|
|
405
475
|
};
|
|
406
476
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
477
|
+
Node {
|
|
478
|
+
node_type,
|
|
479
|
+
name,
|
|
480
|
+
value,
|
|
481
|
+
namespace,
|
|
482
|
+
public_id,
|
|
483
|
+
system_id,
|
|
484
|
+
attrs,
|
|
485
|
+
children: Vec::new(),
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
fn walk(handle: &Handle) -> Node {
|
|
490
|
+
enum Task {
|
|
491
|
+
Enter(Handle, bool),
|
|
492
|
+
Finish(Node, usize),
|
|
493
|
+
}
|
|
494
|
+
let mut tasks = vec![Task::Enter(handle.clone(), false)];
|
|
495
|
+
let mut complete = Vec::new();
|
|
496
|
+
while let Some(task) = tasks.pop() {
|
|
497
|
+
match task {
|
|
498
|
+
Task::Enter(handle, content) => {
|
|
499
|
+
let mut node = shallow_node(&handle);
|
|
500
|
+
if content {
|
|
501
|
+
node.node_type = DOCUMENT_FRAGMENT_NODE;
|
|
502
|
+
node.name = "content".into();
|
|
503
|
+
}
|
|
504
|
+
let children = handle.children.borrow();
|
|
505
|
+
let template = match &handle.data {
|
|
506
|
+
NodeData::Element {
|
|
507
|
+
template_contents, ..
|
|
508
|
+
} => template_contents.borrow().clone(),
|
|
509
|
+
_ => None,
|
|
510
|
+
};
|
|
511
|
+
tasks.push(Task::Finish(
|
|
512
|
+
node,
|
|
513
|
+
children.len() + usize::from(template.is_some()),
|
|
514
|
+
));
|
|
515
|
+
if let Some(template) = template {
|
|
516
|
+
tasks.push(Task::Enter(template, true));
|
|
517
|
+
}
|
|
518
|
+
for child in children.iter().rev() {
|
|
519
|
+
tasks.push(Task::Enter(child.clone(), false));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
Task::Finish(mut node, count) => {
|
|
523
|
+
node.children = complete.split_off(complete.len() - count);
|
|
524
|
+
complete.push(node);
|
|
525
|
+
}
|
|
424
526
|
}
|
|
425
527
|
}
|
|
528
|
+
complete.pop().expect("one parsed root")
|
|
529
|
+
}
|
|
426
530
|
|
|
427
|
-
|
|
531
|
+
/// Drain nested parser intermediates without recursive destructor calls.
|
|
532
|
+
/// This leaves the public marshaled Node representation unchanged.
|
|
533
|
+
pub fn discard_node(mut root: Node) {
|
|
534
|
+
let mut pending = std::mem::take(&mut root.children);
|
|
535
|
+
while let Some(mut node) = pending.pop() {
|
|
536
|
+
pending.append(&mut node.children);
|
|
537
|
+
}
|
|
428
538
|
}
|
|
429
539
|
|
|
430
540
|
#[cfg(test)]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|