rusteerio 0.0.0-stage → 0.1.1
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/CONTRIBUTING.md +73 -0
- package/Cargo.lock +1051 -0
- package/Cargo.toml +36 -0
- package/LICENSE +21 -0
- package/README.md +252 -2
- package/RELEASING.md +114 -0
- package/THIRD_PARTY_NOTICES.md +7443 -0
- package/build.rs +3 -0
- package/dist/chunk-DKULX6VO.mjs +3185 -0
- package/dist/chunk-DKULX6VO.mjs.map +1 -0
- package/dist/chunk-OHPGEY3E.mjs +32 -0
- package/dist/chunk-OHPGEY3E.mjs.map +1 -0
- package/dist/index.cjs +3230 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +113 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.mjs +38 -0
- package/dist/index.mjs.map +1 -0
- package/dist/slim.cjs +2441 -0
- package/dist/slim.cjs.map +1 -0
- package/dist/slim.d.mts +11 -0
- package/dist/slim.d.ts +11 -0
- package/dist/slim.mjs +19 -0
- package/dist/slim.mjs.map +1 -0
- package/dist/types-BP6TpxE5.d.mts +532 -0
- package/dist/types-BP6TpxE5.d.ts +532 -0
- package/dist/utils.cjs +61 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.mts +13 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.mjs +17 -0
- package/dist/utils.mjs.map +1 -0
- package/native/src/arena.rs +1781 -0
- package/native/src/attributes.rs +923 -0
- package/native/src/codec.rs +172 -0
- package/native/src/diagnostics.rs +1249 -0
- package/native/src/extraction.rs +226 -0
- package/native/src/gc.rs +166 -0
- package/native/src/hook_sink.rs +727 -0
- package/native/src/hook_stream.rs +513 -0
- package/native/src/lib.rs +2095 -0
- package/native/src/location.rs +392 -0
- package/native/src/runtime.rs +189 -0
- package/native/src/selector.rs +1287 -0
- package/native/src/sink.rs +261 -0
- package/native/src/stream.rs +334 -0
- package/native/src/tokenizer.rs +355 -0
- package/native/src/tracker.rs +232 -0
- package/native/vendor/README.md +25 -0
- package/native/vendor/html5ever-0.35.0/.cargo_vcs_info.json +6 -0
- package/native/vendor/html5ever-0.35.0/Cargo.lock +828 -0
- package/native/vendor/html5ever-0.35.0/Cargo.toml +85 -0
- package/native/vendor/html5ever-0.35.0/LICENSE-APACHE +201 -0
- package/native/vendor/html5ever-0.35.0/LICENSE-MIT +25 -0
- package/native/vendor/html5ever-0.35.0/README.md +60 -0
- package/native/vendor/html5ever-0.35.0/benches/html5ever.rs +86 -0
- package/native/vendor/html5ever-0.35.0/data/bench/lipsum-zh.html +19 -0
- package/native/vendor/html5ever-0.35.0/data/bench/lipsum.html +40 -0
- package/native/vendor/html5ever-0.35.0/data/bench/medium-fragment.html +24 -0
- package/native/vendor/html5ever-0.35.0/data/bench/small-fragment.html +7 -0
- package/native/vendor/html5ever-0.35.0/data/bench/strong.html +1 -0
- package/native/vendor/html5ever-0.35.0/data/bench/tiny-fragment.html +1 -0
- package/native/vendor/html5ever-0.35.0/examples/arena.rs +356 -0
- package/native/vendor/html5ever-0.35.0/examples/capi/tokenize.c +74 -0
- package/native/vendor/html5ever-0.35.0/examples/noop-tokenize.rs +47 -0
- package/native/vendor/html5ever-0.35.0/examples/noop-tree-builder.rs +132 -0
- package/native/vendor/html5ever-0.35.0/examples/print-tree-actions.rs +185 -0
- package/native/vendor/html5ever-0.35.0/examples/tokenize.rs +111 -0
- package/native/vendor/html5ever-0.35.0/src/driver.rs +148 -0
- package/native/vendor/html5ever-0.35.0/src/lib.rs +30 -0
- package/native/vendor/html5ever-0.35.0/src/macros.rs +34 -0
- package/native/vendor/html5ever-0.35.0/src/serialize/mod.rs +255 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +451 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/interface.rs +99 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +2305 -0
- package/native/vendor/html5ever-0.35.0/src/tokenizer/states.rs +97 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/data.rs +170 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/mod.rs +1848 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/rules.rs +1494 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/tag_sets.rs +114 -0
- package/native/vendor/html5ever-0.35.0/src/tree_builder/types.rs +91 -0
- package/native/vendor/html5ever-0.35.0/src/util/str.rs +52 -0
- package/native/vendor/html5gum-0.8.4/Cargo.lock +1006 -0
- package/native/vendor/html5gum-0.8.4/Cargo.toml +109 -0
- package/native/vendor/html5gum-0.8.4/LICENSE +19 -0
- package/native/vendor/html5gum-0.8.4/README.md +110 -0
- package/native/vendor/html5gum-0.8.4/benches/files/0050.html +2131 -0
- package/native/vendor/html5gum-0.8.4/benches/files/6D30.html +1004 -0
- package/native/vendor/html5gum-0.8.4/benches/files.rs +32 -0
- package/native/vendor/html5gum-0.8.4/benches/patterns.rs +31 -0
- package/native/vendor/html5gum-0.8.4/src/arrayvec.rs +29 -0
- package/native/vendor/html5gum-0.8.4/src/char_validator.rs +134 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/callback.rs +676 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/default.rs +207 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/emitter.rs +344 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/html5ever.rs +306 -0
- package/native/vendor/html5gum-0.8.4/src/emitters/mod.rs +25 -0
- package/native/vendor/html5gum-0.8.4/src/entities.rs +3342 -0
- package/native/vendor/html5gum-0.8.4/src/error.rs +96 -0
- package/native/vendor/html5gum-0.8.4/src/htmlstring.rs +130 -0
- package/native/vendor/html5gum-0.8.4/src/lib.rs +66 -0
- package/native/vendor/html5gum-0.8.4/src/machine.rs +2136 -0
- package/native/vendor/html5gum-0.8.4/src/machine_helper.rs +277 -0
- package/native/vendor/html5gum-0.8.4/src/read_helper.rs +244 -0
- package/native/vendor/html5gum-0.8.4/src/reader.rs +440 -0
- package/native/vendor/html5gum-0.8.4/src/span.rs +98 -0
- package/native/vendor/html5gum-0.8.4/src/state.rs +16 -0
- package/native/vendor/html5gum-0.8.4/src/testutils.rs +71 -0
- package/native/vendor/html5gum-0.8.4/src/tokenizer.rs +189 -0
- package/native/vendor/html5gum-0.8.4/src/utils.rs +88 -0
- package/native/vendor/markup5ever-0.35.0/.cargo_vcs_info.json +6 -0
- package/native/vendor/markup5ever-0.35.0/Cargo.toml +45 -0
- package/native/vendor/markup5ever-0.35.0/LICENSE-APACHE +201 -0
- package/native/vendor/markup5ever-0.35.0/LICENSE-MIT +25 -0
- package/native/vendor/markup5ever-0.35.0/interface/mod.rs +422 -0
- package/native/vendor/markup5ever-0.35.0/interface/tree_builder.rs +280 -0
- package/native/vendor/markup5ever-0.35.0/lib.rs +59 -0
- package/native/vendor/markup5ever-0.35.0/serialize.rs +77 -0
- package/native/vendor/markup5ever-0.35.0/util/buffer_queue.rs +333 -0
- package/native/vendor/markup5ever-0.35.0/util/smallcharset.rs +88 -0
- package/package.json +137 -4
- package/rusteerio.darwin-arm64.node +0 -0
- package/rusteerio.darwin-x64.node +0 -0
- package/rusteerio.linux-arm64-gnu.node +0 -0
- package/rusteerio.linux-arm64-musl.node +0 -0
- package/rusteerio.linux-x64-gnu.node +0 -0
- package/rusteerio.linux-x64-musl.node +0 -0
- package/rusteerio.win32-arm64-msvc.node +0 -0
- package/rusteerio.win32-x64-msvc.node +0 -0
- package/scripts/check-native.mjs +15 -0
- package/scripts/check-release.mjs +235 -0
- package/scripts/fix-declarations.mjs +27 -0
- package/scripts/generate-notices.mjs +176 -0
- package/scripts/notices/README.md +13 -0
- package/scripts/notices/cheerio-MIT.txt +21 -0
- package/scripts/notices/html5ever-AUTHORS.txt +20 -0
- package/scripts/notices/html5ever-COPYRIGHT.txt +8 -0
- package/scripts/notices/html5ever-LICENSE-APACHE.txt +201 -0
- package/scripts/notices/html5ever-LICENSE-MIT.txt +25 -0
- package/scripts/notices/napi-rs-MIT.txt +43 -0
- package/scripts/notices/neon-MIT.txt +19 -0
- package/scripts/notices/runtime-01c266bced4a434da005.txt +3 -0
- package/scripts/notices/runtime-0a54e647fe54104658b5.txt +14 -0
- package/scripts/notices/runtime-0b74dfa0bcee5c420c6b.txt +25 -0
- package/scripts/notices/runtime-0d542e0c8804e39aa7f3.txt +177 -0
- package/scripts/notices/runtime-0f96a83840e146e43c0e.txt +21 -0
- package/scripts/notices/runtime-123a331b5dbf04c30097.txt +25 -0
- package/scripts/notices/runtime-172020dbfd5b53a226df.txt +34 -0
- package/scripts/notices/runtime-209fbbe0ad52d9235e37.txt +26 -0
- package/scripts/notices/runtime-23f18e03dc49df91622f.txt +23 -0
- package/scripts/notices/runtime-268872b9816f90fd8e85.txt +220 -0
- package/scripts/notices/runtime-35242e7a83f69875e6ed.txt +176 -0
- package/scripts/notices/runtime-378f5840b258e2779c39.txt +25 -0
- package/scripts/notices/runtime-4108245a1f2df9d4e94d.txt +25 -0
- package/scripts/notices/runtime-4455bf75a91154108304.txt +21 -0
- package/scripts/notices/runtime-553fffcd9b1cb158bc3e.txt +13 -0
- package/scripts/notices/runtime-62c7a1e35f56406896d7.txt +176 -0
- package/scripts/notices/runtime-6485b8ed310d3f0340bf.txt +25 -0
- package/scripts/notices/runtime-69fef7b0f322a6555415.txt +201 -0
- package/scripts/notices/runtime-6df43f6f4b5d4587f3d8.txt +187 -0
- package/scripts/notices/runtime-799e9ca9d179295ef372.txt +24 -0
- package/scripts/notices/runtime-7b63ecd5f1902af1b637.txt +25 -0
- package/scripts/notices/runtime-7bc7d5de2a0b793d4e1a.txt +21 -0
- package/scripts/notices/runtime-7e12e5df4bae12cb2158.txt +24 -0
- package/scripts/notices/runtime-861399f8c21c042b1105.txt +12 -0
- package/scripts/notices/runtime-8ada45cd9f843acf64e4.txt +201 -0
- package/scripts/notices/runtime-90eb64f0279b0d9432ac.txt +12 -0
- package/scripts/notices/runtime-a60eea817514531668d7.txt +201 -0
- package/scripts/notices/runtime-a7c936ff1ed8fa340172.txt +25 -0
- package/scripts/notices/runtime-ab6eec6caf0fa5775e41.txt +275 -0
- package/scripts/notices/runtime-b1181a40b2a7b25cf66f.txt +19 -0
- package/scripts/notices/runtime-b71bd43a069ca0641a9e.txt +25 -0
- package/scripts/notices/runtime-c16f8dcf1a368b83be78.txt +201 -0
- package/scripts/notices/runtime-c2cfccb812fe482101a8.txt +21 -0
- package/scripts/notices/runtime-e99d88d232bf57d70f0f.txt +25 -0
- package/scripts/notices/runtime-f32fb3b417a194167cfa.txt +9 -0
- package/scripts/notices/runtime-f5062c9a188d81dfe66b.txt +39 -0
- package/scripts/notices/runtime-f9070a832833fa7488fd.txt +4 -0
- package/scripts/notices/runtime-fab3dd6bdab226f1c086.txt +373 -0
- package/scripts/notices/runtime-fba3318d0c600380177e.txt +62 -0
- package/scripts/notices/runtime-ff8f68cb076caf8cefe7.txt +25 -0
- package/scripts/notices/runtime-ff92bed461f50338dd70.txt +74 -0
- package/scripts/notices/runtime.json +1632 -0
- package/scripts/notices/sources.json +555 -0
- package/scripts/notices/xray-MIT.txt +21 -0
- package/src/attributes.ts +271 -0
- package/src/codec.ts +74 -0
- package/src/dom.ts +87 -0
- package/src/extract-plan.ts +97 -0
- package/src/extract.ts +130 -0
- package/src/hooks.ts +220 -0
- package/src/index.ts +223 -0
- package/src/io.ts +491 -0
- package/src/native.ts +135 -0
- package/src/nodes.ts +290 -0
- package/src/parse.ts +53 -0
- package/src/parser-types.ts +157 -0
- package/src/router.ts +327 -0
- package/src/selection.ts +566 -0
- package/src/slim.ts +12 -0
- package/src/transport.ts +104 -0
- package/src/types.ts +186 -0
- package/src/utils.ts +29 -0
- package/src/workers.ts +21 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +16 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Copyright 2014-2017 The html5ever Project Developers. See the
|
|
2
|
+
// COPYRIGHT file at the top-level directory of this distribution.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
5
|
+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
6
|
+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
7
|
+
// option. This file may not be copied, modified, or distributed
|
|
8
|
+
// except according to those terms.
|
|
9
|
+
|
|
10
|
+
use log::warn;
|
|
11
|
+
pub use markup5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
|
|
12
|
+
use markup5ever::{local_name, ns};
|
|
13
|
+
use std::io::{self, Write};
|
|
14
|
+
|
|
15
|
+
use crate::{LocalName, QualName};
|
|
16
|
+
|
|
17
|
+
pub fn serialize<Wr, T>(writer: Wr, node: &T, opts: SerializeOpts) -> io::Result<()>
|
|
18
|
+
where
|
|
19
|
+
Wr: Write,
|
|
20
|
+
T: Serialize,
|
|
21
|
+
{
|
|
22
|
+
let mut ser = HtmlSerializer::new(writer, opts.clone());
|
|
23
|
+
node.serialize(&mut ser, opts.traversal_scope)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[derive(Clone)]
|
|
27
|
+
pub struct SerializeOpts {
|
|
28
|
+
/// Is scripting enabled? Default: true
|
|
29
|
+
pub scripting_enabled: bool,
|
|
30
|
+
|
|
31
|
+
/// Serialize the root node? Default: ChildrenOnly
|
|
32
|
+
pub traversal_scope: TraversalScope,
|
|
33
|
+
|
|
34
|
+
/// If the serializer is asked to serialize an invalid tree, the default
|
|
35
|
+
/// behavior is to panic in the event that an `end_elem` is created without a
|
|
36
|
+
/// matching `start_elem`. Setting this to true will prevent those panics by
|
|
37
|
+
/// creating a default parent on the element stack. No extra start elem will
|
|
38
|
+
/// actually be written. Default: false
|
|
39
|
+
pub create_missing_parent: bool,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl Default for SerializeOpts {
|
|
43
|
+
fn default() -> SerializeOpts {
|
|
44
|
+
SerializeOpts {
|
|
45
|
+
scripting_enabled: true,
|
|
46
|
+
traversal_scope: TraversalScope::ChildrenOnly(None),
|
|
47
|
+
create_missing_parent: false,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[derive(Default)]
|
|
53
|
+
struct ElemInfo {
|
|
54
|
+
html_name: Option<LocalName>,
|
|
55
|
+
ignore_children: bool,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub struct HtmlSerializer<Wr: Write> {
|
|
59
|
+
pub writer: Wr,
|
|
60
|
+
opts: SerializeOpts,
|
|
61
|
+
stack: Vec<ElemInfo>,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn tagname(name: &QualName) -> LocalName {
|
|
65
|
+
match name.ns {
|
|
66
|
+
ns!(html) | ns!(mathml) | ns!(svg) => (),
|
|
67
|
+
ref ns => {
|
|
68
|
+
// FIXME(#122)
|
|
69
|
+
warn!("node with weird namespace {ns:?}");
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
name.local.clone()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
impl<Wr: Write> HtmlSerializer<Wr> {
|
|
77
|
+
pub fn new(writer: Wr, opts: SerializeOpts) -> Self {
|
|
78
|
+
let html_name = match opts.traversal_scope {
|
|
79
|
+
TraversalScope::IncludeNode | TraversalScope::ChildrenOnly(None) => None,
|
|
80
|
+
TraversalScope::ChildrenOnly(Some(ref n)) => Some(tagname(n)),
|
|
81
|
+
};
|
|
82
|
+
HtmlSerializer {
|
|
83
|
+
writer,
|
|
84
|
+
opts,
|
|
85
|
+
stack: vec![ElemInfo {
|
|
86
|
+
html_name,
|
|
87
|
+
ignore_children: false,
|
|
88
|
+
}],
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn parent(&mut self) -> &mut ElemInfo {
|
|
93
|
+
if self.stack.is_empty() {
|
|
94
|
+
if self.opts.create_missing_parent {
|
|
95
|
+
warn!("ElemInfo stack empty, creating new parent");
|
|
96
|
+
self.stack.push(Default::default());
|
|
97
|
+
} else {
|
|
98
|
+
panic!("no parent ElemInfo")
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
self.stack.last_mut().unwrap()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn write_escaped(&mut self, text: &str, attr_mode: bool) -> io::Result<()> {
|
|
105
|
+
for c in text.chars() {
|
|
106
|
+
match c {
|
|
107
|
+
'&' => self.writer.write_all(b"&"),
|
|
108
|
+
'\u{00A0}' => self.writer.write_all(b" "),
|
|
109
|
+
'"' if attr_mode => self.writer.write_all(b"""),
|
|
110
|
+
'<' if !attr_mode => self.writer.write_all(b"<"),
|
|
111
|
+
'>' if !attr_mode => self.writer.write_all(b">"),
|
|
112
|
+
c => self.writer.write_fmt(format_args!("{c}")),
|
|
113
|
+
}?;
|
|
114
|
+
}
|
|
115
|
+
Ok(())
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
impl<Wr: Write> Serializer for HtmlSerializer<Wr> {
|
|
120
|
+
fn start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()>
|
|
121
|
+
where
|
|
122
|
+
AttrIter: Iterator<Item = AttrRef<'a>>,
|
|
123
|
+
{
|
|
124
|
+
let html_name = match name.ns {
|
|
125
|
+
ns!(html) => Some(name.local.clone()),
|
|
126
|
+
_ => None,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if self.parent().ignore_children {
|
|
130
|
+
self.stack.push(ElemInfo {
|
|
131
|
+
html_name,
|
|
132
|
+
ignore_children: true,
|
|
133
|
+
});
|
|
134
|
+
return Ok(());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
self.writer.write_all(b"<")?;
|
|
138
|
+
self.writer.write_all(tagname(&name).as_bytes())?;
|
|
139
|
+
for (name, value) in attrs {
|
|
140
|
+
self.writer.write_all(b" ")?;
|
|
141
|
+
|
|
142
|
+
match name.ns {
|
|
143
|
+
ns!() => (),
|
|
144
|
+
ns!(xml) => self.writer.write_all(b"xml:")?,
|
|
145
|
+
ns!(xmlns) => {
|
|
146
|
+
if name.local != local_name!("xmlns") {
|
|
147
|
+
self.writer.write_all(b"xmlns:")?;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
ns!(xlink) => self.writer.write_all(b"xlink:")?,
|
|
151
|
+
ref ns => {
|
|
152
|
+
// FIXME(#122)
|
|
153
|
+
warn!("attr with weird namespace {ns:?}");
|
|
154
|
+
self.writer.write_all(b"unknown_namespace:")?;
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
self.writer.write_all(name.local.as_bytes())?;
|
|
159
|
+
self.writer.write_all(b"=\"")?;
|
|
160
|
+
self.write_escaped(value, true)?;
|
|
161
|
+
self.writer.write_all(b"\"")?;
|
|
162
|
+
}
|
|
163
|
+
self.writer.write_all(b">")?;
|
|
164
|
+
|
|
165
|
+
let ignore_children = name.ns == ns!(html)
|
|
166
|
+
&& matches!(
|
|
167
|
+
name.local,
|
|
168
|
+
local_name!("area")
|
|
169
|
+
| local_name!("base")
|
|
170
|
+
| local_name!("basefont")
|
|
171
|
+
| local_name!("bgsound")
|
|
172
|
+
| local_name!("br")
|
|
173
|
+
| local_name!("col")
|
|
174
|
+
| local_name!("embed")
|
|
175
|
+
| local_name!("frame")
|
|
176
|
+
| local_name!("hr")
|
|
177
|
+
| local_name!("img")
|
|
178
|
+
| local_name!("input")
|
|
179
|
+
| local_name!("keygen")
|
|
180
|
+
| local_name!("link")
|
|
181
|
+
| local_name!("meta")
|
|
182
|
+
| local_name!("param")
|
|
183
|
+
| local_name!("source")
|
|
184
|
+
| local_name!("track")
|
|
185
|
+
| local_name!("wbr")
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
self.stack.push(ElemInfo {
|
|
189
|
+
html_name,
|
|
190
|
+
ignore_children,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
Ok(())
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fn end_elem(&mut self, name: QualName) -> io::Result<()> {
|
|
197
|
+
let info = match self.stack.pop() {
|
|
198
|
+
Some(info) => info,
|
|
199
|
+
None if self.opts.create_missing_parent => {
|
|
200
|
+
warn!("missing ElemInfo, creating default.");
|
|
201
|
+
Default::default()
|
|
202
|
+
},
|
|
203
|
+
_ => panic!("no ElemInfo"),
|
|
204
|
+
};
|
|
205
|
+
if info.ignore_children {
|
|
206
|
+
return Ok(());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
self.writer.write_all(b"</")?;
|
|
210
|
+
self.writer.write_all(tagname(&name).as_bytes())?;
|
|
211
|
+
self.writer.write_all(b">")
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
fn write_text(&mut self, text: &str) -> io::Result<()> {
|
|
215
|
+
let escape = match self.parent().html_name {
|
|
216
|
+
Some(local_name!("style"))
|
|
217
|
+
| Some(local_name!("script"))
|
|
218
|
+
| Some(local_name!("xmp"))
|
|
219
|
+
| Some(local_name!("iframe"))
|
|
220
|
+
| Some(local_name!("noembed"))
|
|
221
|
+
| Some(local_name!("noframes"))
|
|
222
|
+
| Some(local_name!("plaintext")) => false,
|
|
223
|
+
|
|
224
|
+
Some(local_name!("noscript")) => !self.opts.scripting_enabled,
|
|
225
|
+
|
|
226
|
+
_ => true,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if escape {
|
|
230
|
+
self.write_escaped(text, false)
|
|
231
|
+
} else {
|
|
232
|
+
self.writer.write_all(text.as_bytes())
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
fn write_comment(&mut self, text: &str) -> io::Result<()> {
|
|
237
|
+
self.writer.write_all(b"<!--")?;
|
|
238
|
+
self.writer.write_all(text.as_bytes())?;
|
|
239
|
+
self.writer.write_all(b"-->")
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn write_doctype(&mut self, name: &str) -> io::Result<()> {
|
|
243
|
+
self.writer.write_all(b"<!DOCTYPE ")?;
|
|
244
|
+
self.writer.write_all(name.as_bytes())?;
|
|
245
|
+
self.writer.write_all(b">")
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fn write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()> {
|
|
249
|
+
self.writer.write_all(b"<?")?;
|
|
250
|
+
self.writer.write_all(target.as_bytes())?;
|
|
251
|
+
self.writer.write_all(b" ")?;
|
|
252
|
+
self.writer.write_all(data.as_bytes())?;
|
|
253
|
+
self.writer.write_all(b">")
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
// Copyright 2014-2017 The html5ever Project Developers. See the
|
|
2
|
+
// COPYRIGHT file at the top-level directory of this distribution.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
5
|
+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
6
|
+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
7
|
+
// option. This file may not be copied, modified, or distributed
|
|
8
|
+
// except according to those terms.
|
|
9
|
+
|
|
10
|
+
use super::{TokenSink, Tokenizer};
|
|
11
|
+
use crate::buffer_queue::BufferQueue;
|
|
12
|
+
use crate::data;
|
|
13
|
+
use crate::tendril::StrTendril;
|
|
14
|
+
|
|
15
|
+
use log::debug;
|
|
16
|
+
use std::borrow::Cow::{self, Borrowed};
|
|
17
|
+
use std::char::from_u32;
|
|
18
|
+
|
|
19
|
+
use self::State::*;
|
|
20
|
+
pub(super) use self::Status::*;
|
|
21
|
+
|
|
22
|
+
//§ tokenizing-character-references
|
|
23
|
+
pub(super) struct CharRef {
|
|
24
|
+
/// The resulting character(s)
|
|
25
|
+
pub(super) chars: [char; 2],
|
|
26
|
+
|
|
27
|
+
/// How many slots in `chars` are valid?
|
|
28
|
+
pub(super) num_chars: u8,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub(super) enum Status {
|
|
32
|
+
Stuck,
|
|
33
|
+
Progress,
|
|
34
|
+
Done,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#[derive(Debug)]
|
|
38
|
+
enum State {
|
|
39
|
+
Begin,
|
|
40
|
+
Octothorpe,
|
|
41
|
+
Numeric(u32), // base
|
|
42
|
+
NumericSemicolon,
|
|
43
|
+
Named,
|
|
44
|
+
BogusName,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub(super) struct CharRefTokenizer {
|
|
48
|
+
state: State,
|
|
49
|
+
result: Option<CharRef>,
|
|
50
|
+
is_consumed_in_attribute: bool,
|
|
51
|
+
|
|
52
|
+
num: u32,
|
|
53
|
+
num_too_big: bool,
|
|
54
|
+
seen_digit: bool,
|
|
55
|
+
hex_marker: Option<char>,
|
|
56
|
+
|
|
57
|
+
name_buf_opt: Option<StrTendril>,
|
|
58
|
+
name_match: Option<(u32, u32)>,
|
|
59
|
+
name_len: usize,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl CharRefTokenizer {
|
|
63
|
+
pub(super) fn new(is_consumed_in_attribute: bool) -> CharRefTokenizer {
|
|
64
|
+
CharRefTokenizer {
|
|
65
|
+
is_consumed_in_attribute,
|
|
66
|
+
state: Begin,
|
|
67
|
+
result: None,
|
|
68
|
+
num: 0,
|
|
69
|
+
num_too_big: false,
|
|
70
|
+
seen_digit: false,
|
|
71
|
+
hex_marker: None,
|
|
72
|
+
name_buf_opt: None,
|
|
73
|
+
name_match: None,
|
|
74
|
+
name_len: 0,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// A CharRefTokenizer can only tokenize one character reference,
|
|
79
|
+
// so this method consumes the tokenizer.
|
|
80
|
+
pub(super) fn get_result(self) -> CharRef {
|
|
81
|
+
self.result.expect("get_result called before done")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn name_buf(&self) -> &StrTendril {
|
|
85
|
+
self.name_buf_opt
|
|
86
|
+
.as_ref()
|
|
87
|
+
.expect("name_buf missing in named character reference")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn name_buf_mut(&mut self) -> &mut StrTendril {
|
|
91
|
+
self.name_buf_opt
|
|
92
|
+
.as_mut()
|
|
93
|
+
.expect("name_buf missing in named character reference")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fn finish_none(&mut self) -> Status {
|
|
97
|
+
self.result = Some(CharRef {
|
|
98
|
+
chars: ['\0', '\0'],
|
|
99
|
+
num_chars: 0,
|
|
100
|
+
});
|
|
101
|
+
Done
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn finish_one(&mut self, c: char) -> Status {
|
|
105
|
+
self.result = Some(CharRef {
|
|
106
|
+
chars: [c, '\0'],
|
|
107
|
+
num_chars: 1,
|
|
108
|
+
});
|
|
109
|
+
Done
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
impl CharRefTokenizer {
|
|
114
|
+
pub(super) fn step<Sink: TokenSink>(
|
|
115
|
+
&mut self,
|
|
116
|
+
tokenizer: &Tokenizer<Sink>,
|
|
117
|
+
input: &BufferQueue,
|
|
118
|
+
) -> Status {
|
|
119
|
+
if self.result.is_some() {
|
|
120
|
+
return Done;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
debug!("char ref tokenizer stepping in state {:?}", self.state);
|
|
124
|
+
match self.state {
|
|
125
|
+
Begin => self.do_begin(tokenizer, input),
|
|
126
|
+
Octothorpe => self.do_octothorpe(tokenizer, input),
|
|
127
|
+
Numeric(base) => self.do_numeric(tokenizer, input, base),
|
|
128
|
+
NumericSemicolon => self.do_numeric_semicolon(tokenizer, input),
|
|
129
|
+
Named => self.do_named(tokenizer, input),
|
|
130
|
+
BogusName => self.do_bogus_name(tokenizer, input),
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn do_begin<Sink: TokenSink>(
|
|
135
|
+
&mut self,
|
|
136
|
+
tokenizer: &Tokenizer<Sink>,
|
|
137
|
+
input: &BufferQueue,
|
|
138
|
+
) -> Status {
|
|
139
|
+
match tokenizer.peek(input) {
|
|
140
|
+
Some('a'..='z' | 'A'..='Z' | '0'..='9') => {
|
|
141
|
+
self.state = Named;
|
|
142
|
+
self.name_buf_opt = Some(StrTendril::new());
|
|
143
|
+
Progress
|
|
144
|
+
}
|
|
145
|
+
Some('#') => {
|
|
146
|
+
tokenizer.discard_char(input);
|
|
147
|
+
self.state = Octothorpe;
|
|
148
|
+
Progress
|
|
149
|
+
}
|
|
150
|
+
Some(_) => self.finish_none(),
|
|
151
|
+
None => Stuck,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fn do_octothorpe<Sink: TokenSink>(
|
|
156
|
+
&mut self,
|
|
157
|
+
tokenizer: &Tokenizer<Sink>,
|
|
158
|
+
input: &BufferQueue,
|
|
159
|
+
) -> Status {
|
|
160
|
+
match tokenizer.peek(input) {
|
|
161
|
+
Some(c @ ('x' | 'X')) => {
|
|
162
|
+
tokenizer.discard_char(input);
|
|
163
|
+
self.hex_marker = Some(c);
|
|
164
|
+
self.state = Numeric(16);
|
|
165
|
+
}
|
|
166
|
+
Some(_) => {
|
|
167
|
+
self.hex_marker = None;
|
|
168
|
+
self.state = Numeric(10);
|
|
169
|
+
}
|
|
170
|
+
None => return Stuck,
|
|
171
|
+
}
|
|
172
|
+
Progress
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
fn do_numeric<Sink: TokenSink>(
|
|
176
|
+
&mut self,
|
|
177
|
+
tokenizer: &Tokenizer<Sink>,
|
|
178
|
+
input: &BufferQueue,
|
|
179
|
+
base: u32,
|
|
180
|
+
) -> Status {
|
|
181
|
+
let Some(c) = tokenizer.peek(input) else {
|
|
182
|
+
return Stuck;
|
|
183
|
+
};
|
|
184
|
+
match c.to_digit(base) {
|
|
185
|
+
Some(n) => {
|
|
186
|
+
tokenizer.discard_char(input);
|
|
187
|
+
self.num = self.num.wrapping_mul(base);
|
|
188
|
+
if self.num > 0x10FFFF {
|
|
189
|
+
// We might overflow, and the character is definitely invalid.
|
|
190
|
+
// We still parse digits and semicolon, but don't use the result.
|
|
191
|
+
self.num_too_big = true;
|
|
192
|
+
}
|
|
193
|
+
self.num = self.num.wrapping_add(n);
|
|
194
|
+
self.seen_digit = true;
|
|
195
|
+
Progress
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
None if !self.seen_digit => self.unconsume_numeric(tokenizer, input),
|
|
199
|
+
|
|
200
|
+
None => {
|
|
201
|
+
self.state = NumericSemicolon;
|
|
202
|
+
Progress
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn do_numeric_semicolon<Sink: TokenSink>(
|
|
208
|
+
&mut self,
|
|
209
|
+
tokenizer: &Tokenizer<Sink>,
|
|
210
|
+
input: &BufferQueue,
|
|
211
|
+
) -> Status {
|
|
212
|
+
match tokenizer.peek(input) {
|
|
213
|
+
Some(';') => tokenizer.discard_char(input),
|
|
214
|
+
Some(_) => tokenizer.emit_error(Borrowed(
|
|
215
|
+
"Semicolon missing after numeric character reference",
|
|
216
|
+
)),
|
|
217
|
+
None => return Stuck,
|
|
218
|
+
};
|
|
219
|
+
self.finish_numeric(tokenizer)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn unconsume_numeric<Sink: TokenSink>(
|
|
223
|
+
&mut self,
|
|
224
|
+
tokenizer: &Tokenizer<Sink>,
|
|
225
|
+
input: &BufferQueue,
|
|
226
|
+
) -> Status {
|
|
227
|
+
let mut unconsume = StrTendril::from_char('#');
|
|
228
|
+
if let Some(c) = self.hex_marker {
|
|
229
|
+
unconsume.push_char(c)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
input.push_front(unconsume);
|
|
233
|
+
tokenizer.emit_error(Borrowed("Numeric character reference without digits"));
|
|
234
|
+
self.finish_none()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
fn finish_numeric<Sink: TokenSink>(&mut self, tokenizer: &Tokenizer<Sink>) -> Status {
|
|
238
|
+
fn conv(n: u32) -> char {
|
|
239
|
+
from_u32(n).expect("invalid char missed by error handling cases")
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let (c, error) = match self.num {
|
|
243
|
+
n if (n > 0x10FFFF) || self.num_too_big => ('\u{fffd}', true),
|
|
244
|
+
0x00 | 0xD800..=0xDFFF => ('\u{fffd}', true),
|
|
245
|
+
|
|
246
|
+
0x80..=0x9F => match data::C1_REPLACEMENTS[(self.num - 0x80) as usize] {
|
|
247
|
+
Some(c) => (c, true),
|
|
248
|
+
None => (conv(self.num), true),
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
0x01..=0x08 | 0x0B | 0x0D..=0x1F | 0x7F | 0xFDD0..=0xFDEF => (conv(self.num), true),
|
|
252
|
+
|
|
253
|
+
n if (n & 0xFFFE) == 0xFFFE => (conv(n), true),
|
|
254
|
+
|
|
255
|
+
n => (conv(n), false),
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if error {
|
|
259
|
+
let msg = if tokenizer.opts.exact_errors {
|
|
260
|
+
Cow::from(format!(
|
|
261
|
+
"Invalid numeric character reference value 0x{:06X}",
|
|
262
|
+
self.num
|
|
263
|
+
))
|
|
264
|
+
} else {
|
|
265
|
+
Cow::from("Invalid numeric character reference")
|
|
266
|
+
};
|
|
267
|
+
tokenizer.emit_error(msg);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
self.finish_one(c)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fn do_named<Sink: TokenSink>(
|
|
274
|
+
&mut self,
|
|
275
|
+
tokenizer: &Tokenizer<Sink>,
|
|
276
|
+
input: &BufferQueue,
|
|
277
|
+
) -> Status {
|
|
278
|
+
// peek + discard skips over newline normalization, therefore making it easier to
|
|
279
|
+
// un-consume
|
|
280
|
+
let Some(c) = tokenizer.peek(input) else {
|
|
281
|
+
return Stuck;
|
|
282
|
+
};
|
|
283
|
+
tokenizer.discard_char(input);
|
|
284
|
+
self.name_buf_mut().push_char(c);
|
|
285
|
+
match data::NAMED_ENTITIES.get(&self.name_buf()[..]) {
|
|
286
|
+
// We have either a full match or a prefix of one.
|
|
287
|
+
Some(&m) => {
|
|
288
|
+
if m.0 != 0 {
|
|
289
|
+
// We have a full match, but there might be a longer one to come.
|
|
290
|
+
self.name_match = Some(m);
|
|
291
|
+
self.name_len = self.name_buf().len();
|
|
292
|
+
}
|
|
293
|
+
// Otherwise we just have a prefix match.
|
|
294
|
+
Progress
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Can't continue the match.
|
|
298
|
+
None => self.finish_named(tokenizer, input, Some(c)),
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
fn emit_name_error<Sink: TokenSink>(&mut self, tokenizer: &Tokenizer<Sink>) {
|
|
303
|
+
let msg = if tokenizer.opts.exact_errors {
|
|
304
|
+
Cow::from(format!("Invalid character reference &{}", self.name_buf()))
|
|
305
|
+
} else {
|
|
306
|
+
Cow::from("Invalid character reference")
|
|
307
|
+
};
|
|
308
|
+
tokenizer.emit_error(msg);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
fn unconsume_name(&mut self, input: &BufferQueue) {
|
|
312
|
+
input.push_front(self.name_buf_opt.take().unwrap());
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
fn finish_named<Sink: TokenSink>(
|
|
316
|
+
&mut self,
|
|
317
|
+
tokenizer: &Tokenizer<Sink>,
|
|
318
|
+
input: &BufferQueue,
|
|
319
|
+
end_char: Option<char>,
|
|
320
|
+
) -> Status {
|
|
321
|
+
match self.name_match {
|
|
322
|
+
None => {
|
|
323
|
+
match end_char {
|
|
324
|
+
Some(c) if c.is_ascii_alphanumeric() => {
|
|
325
|
+
// Keep looking for a semicolon, to determine whether
|
|
326
|
+
// we emit a parse error.
|
|
327
|
+
self.state = BogusName;
|
|
328
|
+
return Progress;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check length because &; is not a parse error.
|
|
332
|
+
Some(';') if self.name_buf().len() > 1 => self.emit_name_error(tokenizer),
|
|
333
|
+
|
|
334
|
+
_ => (),
|
|
335
|
+
}
|
|
336
|
+
self.unconsume_name(input);
|
|
337
|
+
self.finish_none()
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
Some((c1, c2)) => {
|
|
341
|
+
// We have a complete match, but we may have consumed
|
|
342
|
+
// additional characters into self.name_buf. Usually
|
|
343
|
+
// at least one, but several in cases like
|
|
344
|
+
//
|
|
345
|
+
// ¬ => match for U+00AC
|
|
346
|
+
// ¬i => valid prefix for ¬in
|
|
347
|
+
// ¬it => can't continue match
|
|
348
|
+
|
|
349
|
+
let name_len = self.name_len;
|
|
350
|
+
assert!(name_len > 0);
|
|
351
|
+
let last_matched = self.name_buf()[name_len - 1..].chars().next().unwrap();
|
|
352
|
+
|
|
353
|
+
// There might not be a next character after the match, if
|
|
354
|
+
// we had a full match and then hit EOF.
|
|
355
|
+
let next_after = if name_len == self.name_buf().len() {
|
|
356
|
+
None
|
|
357
|
+
} else {
|
|
358
|
+
Some(self.name_buf()[name_len..].chars().next().unwrap())
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// If the character reference was consumed as part of an attribute, and the last
|
|
362
|
+
// character matched is not a U+003B SEMICOLON character (;), and the next input
|
|
363
|
+
// character is either a U+003D EQUALS SIGN character (=) or an ASCII alphanumeric,
|
|
364
|
+
// then, for historical reasons, flush code points consumed as a character
|
|
365
|
+
// reference and switch to the return state.
|
|
366
|
+
|
|
367
|
+
let unconsume_all = match (self.is_consumed_in_attribute, last_matched, next_after)
|
|
368
|
+
{
|
|
369
|
+
(_, ';', _) => false,
|
|
370
|
+
(true, _, Some('=')) => true,
|
|
371
|
+
(true, _, Some(c)) if c.is_ascii_alphanumeric() => true,
|
|
372
|
+
_ => {
|
|
373
|
+
// 1. If the last character matched is not a U+003B SEMICOLON character
|
|
374
|
+
// (;), then this is a missing-semicolon-after-character-reference parse
|
|
375
|
+
// error.
|
|
376
|
+
tokenizer.emit_error(Borrowed(
|
|
377
|
+
"Character reference does not end with semicolon",
|
|
378
|
+
));
|
|
379
|
+
false
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
if unconsume_all {
|
|
384
|
+
self.unconsume_name(input);
|
|
385
|
+
self.finish_none()
|
|
386
|
+
} else {
|
|
387
|
+
input.push_front(StrTendril::from_slice(&self.name_buf()[name_len..]));
|
|
388
|
+
tokenizer.ignore_lf.set(false);
|
|
389
|
+
self.result = Some(CharRef {
|
|
390
|
+
chars: [from_u32(c1).unwrap(), from_u32(c2).unwrap()],
|
|
391
|
+
num_chars: if c2 == 0 { 1 } else { 2 },
|
|
392
|
+
});
|
|
393
|
+
Done
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
fn do_bogus_name<Sink: TokenSink>(
|
|
400
|
+
&mut self,
|
|
401
|
+
tokenizer: &Tokenizer<Sink>,
|
|
402
|
+
input: &BufferQueue,
|
|
403
|
+
) -> Status {
|
|
404
|
+
// peek + discard skips over newline normalization, therefore making it easier to
|
|
405
|
+
// un-consume
|
|
406
|
+
let Some(c) = tokenizer.peek(input) else {
|
|
407
|
+
return Stuck;
|
|
408
|
+
};
|
|
409
|
+
tokenizer.discard_char(input);
|
|
410
|
+
self.name_buf_mut().push_char(c);
|
|
411
|
+
match c {
|
|
412
|
+
_ if c.is_ascii_alphanumeric() => return Progress,
|
|
413
|
+
';' => self.emit_name_error(tokenizer),
|
|
414
|
+
_ => (),
|
|
415
|
+
}
|
|
416
|
+
self.unconsume_name(input);
|
|
417
|
+
self.finish_none()
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
pub(super) fn end_of_file<Sink: TokenSink>(
|
|
421
|
+
&mut self,
|
|
422
|
+
tokenizer: &Tokenizer<Sink>,
|
|
423
|
+
input: &BufferQueue,
|
|
424
|
+
) {
|
|
425
|
+
while self.result.is_none() {
|
|
426
|
+
match self.state {
|
|
427
|
+
Begin => drop(self.finish_none()),
|
|
428
|
+
|
|
429
|
+
Numeric(_) if !self.seen_digit => drop(self.unconsume_numeric(tokenizer, input)),
|
|
430
|
+
|
|
431
|
+
Numeric(_) | NumericSemicolon => {
|
|
432
|
+
tokenizer.emit_error(Borrowed("EOF in numeric character reference"));
|
|
433
|
+
self.finish_numeric(tokenizer);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
Named => drop(self.finish_named(tokenizer, input, None)),
|
|
437
|
+
|
|
438
|
+
BogusName => {
|
|
439
|
+
self.unconsume_name(input);
|
|
440
|
+
self.finish_none();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
Octothorpe => {
|
|
444
|
+
input.push_front(StrTendril::from_slice("#"));
|
|
445
|
+
tokenizer.emit_error(Borrowed("EOF after '#' in character reference"));
|
|
446
|
+
self.finish_none();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|