rusteerio 0.0.0-stage → 0.1.0

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.
Files changed (206) hide show
  1. package/CONTRIBUTING.md +73 -0
  2. package/Cargo.lock +1051 -0
  3. package/Cargo.toml +36 -0
  4. package/LICENSE +21 -0
  5. package/README.md +252 -2
  6. package/RELEASING.md +138 -0
  7. package/THIRD_PARTY_NOTICES.md +7443 -0
  8. package/build.rs +3 -0
  9. package/dist/chunk-DKULX6VO.mjs +3185 -0
  10. package/dist/chunk-DKULX6VO.mjs.map +1 -0
  11. package/dist/chunk-OHPGEY3E.mjs +32 -0
  12. package/dist/chunk-OHPGEY3E.mjs.map +1 -0
  13. package/dist/index.cjs +3230 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.d.mts +113 -0
  16. package/dist/index.d.ts +113 -0
  17. package/dist/index.mjs +38 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/slim.cjs +2441 -0
  20. package/dist/slim.cjs.map +1 -0
  21. package/dist/slim.d.mts +11 -0
  22. package/dist/slim.d.ts +11 -0
  23. package/dist/slim.mjs +19 -0
  24. package/dist/slim.mjs.map +1 -0
  25. package/dist/types-BP6TpxE5.d.mts +532 -0
  26. package/dist/types-BP6TpxE5.d.ts +532 -0
  27. package/dist/utils.cjs +61 -0
  28. package/dist/utils.cjs.map +1 -0
  29. package/dist/utils.d.mts +13 -0
  30. package/dist/utils.d.ts +13 -0
  31. package/dist/utils.mjs +17 -0
  32. package/dist/utils.mjs.map +1 -0
  33. package/native/src/arena.rs +1781 -0
  34. package/native/src/attributes.rs +923 -0
  35. package/native/src/codec.rs +172 -0
  36. package/native/src/diagnostics.rs +1249 -0
  37. package/native/src/extraction.rs +226 -0
  38. package/native/src/gc.rs +166 -0
  39. package/native/src/hook_sink.rs +727 -0
  40. package/native/src/hook_stream.rs +513 -0
  41. package/native/src/lib.rs +2095 -0
  42. package/native/src/location.rs +392 -0
  43. package/native/src/runtime.rs +189 -0
  44. package/native/src/selector.rs +1287 -0
  45. package/native/src/sink.rs +261 -0
  46. package/native/src/stream.rs +334 -0
  47. package/native/src/tokenizer.rs +355 -0
  48. package/native/src/tracker.rs +232 -0
  49. package/native/vendor/README.md +25 -0
  50. package/native/vendor/html5ever-0.35.0/.cargo_vcs_info.json +6 -0
  51. package/native/vendor/html5ever-0.35.0/Cargo.lock +828 -0
  52. package/native/vendor/html5ever-0.35.0/Cargo.toml +85 -0
  53. package/native/vendor/html5ever-0.35.0/LICENSE-APACHE +201 -0
  54. package/native/vendor/html5ever-0.35.0/LICENSE-MIT +25 -0
  55. package/native/vendor/html5ever-0.35.0/README.md +60 -0
  56. package/native/vendor/html5ever-0.35.0/benches/html5ever.rs +86 -0
  57. package/native/vendor/html5ever-0.35.0/data/bench/lipsum-zh.html +19 -0
  58. package/native/vendor/html5ever-0.35.0/data/bench/lipsum.html +40 -0
  59. package/native/vendor/html5ever-0.35.0/data/bench/medium-fragment.html +24 -0
  60. package/native/vendor/html5ever-0.35.0/data/bench/small-fragment.html +7 -0
  61. package/native/vendor/html5ever-0.35.0/data/bench/strong.html +1 -0
  62. package/native/vendor/html5ever-0.35.0/data/bench/tiny-fragment.html +1 -0
  63. package/native/vendor/html5ever-0.35.0/examples/arena.rs +356 -0
  64. package/native/vendor/html5ever-0.35.0/examples/capi/tokenize.c +74 -0
  65. package/native/vendor/html5ever-0.35.0/examples/noop-tokenize.rs +47 -0
  66. package/native/vendor/html5ever-0.35.0/examples/noop-tree-builder.rs +132 -0
  67. package/native/vendor/html5ever-0.35.0/examples/print-tree-actions.rs +185 -0
  68. package/native/vendor/html5ever-0.35.0/examples/tokenize.rs +111 -0
  69. package/native/vendor/html5ever-0.35.0/src/driver.rs +148 -0
  70. package/native/vendor/html5ever-0.35.0/src/lib.rs +30 -0
  71. package/native/vendor/html5ever-0.35.0/src/macros.rs +34 -0
  72. package/native/vendor/html5ever-0.35.0/src/serialize/mod.rs +255 -0
  73. package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +451 -0
  74. package/native/vendor/html5ever-0.35.0/src/tokenizer/interface.rs +99 -0
  75. package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +2305 -0
  76. package/native/vendor/html5ever-0.35.0/src/tokenizer/states.rs +97 -0
  77. package/native/vendor/html5ever-0.35.0/src/tree_builder/data.rs +170 -0
  78. package/native/vendor/html5ever-0.35.0/src/tree_builder/mod.rs +1848 -0
  79. package/native/vendor/html5ever-0.35.0/src/tree_builder/rules.rs +1494 -0
  80. package/native/vendor/html5ever-0.35.0/src/tree_builder/tag_sets.rs +114 -0
  81. package/native/vendor/html5ever-0.35.0/src/tree_builder/types.rs +91 -0
  82. package/native/vendor/html5ever-0.35.0/src/util/str.rs +52 -0
  83. package/native/vendor/html5gum-0.8.4/Cargo.lock +1006 -0
  84. package/native/vendor/html5gum-0.8.4/Cargo.toml +109 -0
  85. package/native/vendor/html5gum-0.8.4/LICENSE +19 -0
  86. package/native/vendor/html5gum-0.8.4/README.md +110 -0
  87. package/native/vendor/html5gum-0.8.4/benches/files/0050.html +2131 -0
  88. package/native/vendor/html5gum-0.8.4/benches/files/6D30.html +1004 -0
  89. package/native/vendor/html5gum-0.8.4/benches/files.rs +32 -0
  90. package/native/vendor/html5gum-0.8.4/benches/patterns.rs +31 -0
  91. package/native/vendor/html5gum-0.8.4/src/arrayvec.rs +29 -0
  92. package/native/vendor/html5gum-0.8.4/src/char_validator.rs +134 -0
  93. package/native/vendor/html5gum-0.8.4/src/emitters/callback.rs +676 -0
  94. package/native/vendor/html5gum-0.8.4/src/emitters/default.rs +207 -0
  95. package/native/vendor/html5gum-0.8.4/src/emitters/emitter.rs +344 -0
  96. package/native/vendor/html5gum-0.8.4/src/emitters/html5ever.rs +306 -0
  97. package/native/vendor/html5gum-0.8.4/src/emitters/mod.rs +25 -0
  98. package/native/vendor/html5gum-0.8.4/src/entities.rs +3342 -0
  99. package/native/vendor/html5gum-0.8.4/src/error.rs +96 -0
  100. package/native/vendor/html5gum-0.8.4/src/htmlstring.rs +130 -0
  101. package/native/vendor/html5gum-0.8.4/src/lib.rs +66 -0
  102. package/native/vendor/html5gum-0.8.4/src/machine.rs +2136 -0
  103. package/native/vendor/html5gum-0.8.4/src/machine_helper.rs +277 -0
  104. package/native/vendor/html5gum-0.8.4/src/read_helper.rs +244 -0
  105. package/native/vendor/html5gum-0.8.4/src/reader.rs +440 -0
  106. package/native/vendor/html5gum-0.8.4/src/span.rs +98 -0
  107. package/native/vendor/html5gum-0.8.4/src/state.rs +16 -0
  108. package/native/vendor/html5gum-0.8.4/src/testutils.rs +71 -0
  109. package/native/vendor/html5gum-0.8.4/src/tokenizer.rs +189 -0
  110. package/native/vendor/html5gum-0.8.4/src/utils.rs +88 -0
  111. package/native/vendor/markup5ever-0.35.0/.cargo_vcs_info.json +6 -0
  112. package/native/vendor/markup5ever-0.35.0/Cargo.toml +45 -0
  113. package/native/vendor/markup5ever-0.35.0/LICENSE-APACHE +201 -0
  114. package/native/vendor/markup5ever-0.35.0/LICENSE-MIT +25 -0
  115. package/native/vendor/markup5ever-0.35.0/interface/mod.rs +422 -0
  116. package/native/vendor/markup5ever-0.35.0/interface/tree_builder.rs +280 -0
  117. package/native/vendor/markup5ever-0.35.0/lib.rs +59 -0
  118. package/native/vendor/markup5ever-0.35.0/serialize.rs +77 -0
  119. package/native/vendor/markup5ever-0.35.0/util/buffer_queue.rs +333 -0
  120. package/native/vendor/markup5ever-0.35.0/util/smallcharset.rs +88 -0
  121. package/package.json +137 -4
  122. package/rusteerio.darwin-arm64.node +0 -0
  123. package/rusteerio.darwin-x64.node +0 -0
  124. package/rusteerio.linux-arm64-gnu.node +0 -0
  125. package/rusteerio.linux-arm64-musl.node +0 -0
  126. package/rusteerio.linux-x64-gnu.node +0 -0
  127. package/rusteerio.linux-x64-musl.node +0 -0
  128. package/rusteerio.win32-arm64-msvc.node +0 -0
  129. package/rusteerio.win32-x64-msvc.node +0 -0
  130. package/scripts/check-native.mjs +15 -0
  131. package/scripts/check-release.mjs +235 -0
  132. package/scripts/fix-declarations.mjs +27 -0
  133. package/scripts/generate-notices.mjs +176 -0
  134. package/scripts/notices/README.md +13 -0
  135. package/scripts/notices/cheerio-MIT.txt +21 -0
  136. package/scripts/notices/html5ever-AUTHORS.txt +20 -0
  137. package/scripts/notices/html5ever-COPYRIGHT.txt +8 -0
  138. package/scripts/notices/html5ever-LICENSE-APACHE.txt +201 -0
  139. package/scripts/notices/html5ever-LICENSE-MIT.txt +25 -0
  140. package/scripts/notices/napi-rs-MIT.txt +43 -0
  141. package/scripts/notices/neon-MIT.txt +19 -0
  142. package/scripts/notices/runtime-01c266bced4a434da005.txt +3 -0
  143. package/scripts/notices/runtime-0a54e647fe54104658b5.txt +14 -0
  144. package/scripts/notices/runtime-0b74dfa0bcee5c420c6b.txt +25 -0
  145. package/scripts/notices/runtime-0d542e0c8804e39aa7f3.txt +177 -0
  146. package/scripts/notices/runtime-0f96a83840e146e43c0e.txt +21 -0
  147. package/scripts/notices/runtime-123a331b5dbf04c30097.txt +25 -0
  148. package/scripts/notices/runtime-172020dbfd5b53a226df.txt +34 -0
  149. package/scripts/notices/runtime-209fbbe0ad52d9235e37.txt +26 -0
  150. package/scripts/notices/runtime-23f18e03dc49df91622f.txt +23 -0
  151. package/scripts/notices/runtime-268872b9816f90fd8e85.txt +220 -0
  152. package/scripts/notices/runtime-35242e7a83f69875e6ed.txt +176 -0
  153. package/scripts/notices/runtime-378f5840b258e2779c39.txt +25 -0
  154. package/scripts/notices/runtime-4108245a1f2df9d4e94d.txt +25 -0
  155. package/scripts/notices/runtime-4455bf75a91154108304.txt +21 -0
  156. package/scripts/notices/runtime-553fffcd9b1cb158bc3e.txt +13 -0
  157. package/scripts/notices/runtime-62c7a1e35f56406896d7.txt +176 -0
  158. package/scripts/notices/runtime-6485b8ed310d3f0340bf.txt +25 -0
  159. package/scripts/notices/runtime-69fef7b0f322a6555415.txt +201 -0
  160. package/scripts/notices/runtime-6df43f6f4b5d4587f3d8.txt +187 -0
  161. package/scripts/notices/runtime-799e9ca9d179295ef372.txt +24 -0
  162. package/scripts/notices/runtime-7b63ecd5f1902af1b637.txt +25 -0
  163. package/scripts/notices/runtime-7bc7d5de2a0b793d4e1a.txt +21 -0
  164. package/scripts/notices/runtime-7e12e5df4bae12cb2158.txt +24 -0
  165. package/scripts/notices/runtime-861399f8c21c042b1105.txt +12 -0
  166. package/scripts/notices/runtime-8ada45cd9f843acf64e4.txt +201 -0
  167. package/scripts/notices/runtime-90eb64f0279b0d9432ac.txt +12 -0
  168. package/scripts/notices/runtime-a60eea817514531668d7.txt +201 -0
  169. package/scripts/notices/runtime-a7c936ff1ed8fa340172.txt +25 -0
  170. package/scripts/notices/runtime-ab6eec6caf0fa5775e41.txt +275 -0
  171. package/scripts/notices/runtime-b1181a40b2a7b25cf66f.txt +19 -0
  172. package/scripts/notices/runtime-b71bd43a069ca0641a9e.txt +25 -0
  173. package/scripts/notices/runtime-c16f8dcf1a368b83be78.txt +201 -0
  174. package/scripts/notices/runtime-c2cfccb812fe482101a8.txt +21 -0
  175. package/scripts/notices/runtime-e99d88d232bf57d70f0f.txt +25 -0
  176. package/scripts/notices/runtime-f32fb3b417a194167cfa.txt +9 -0
  177. package/scripts/notices/runtime-f5062c9a188d81dfe66b.txt +39 -0
  178. package/scripts/notices/runtime-f9070a832833fa7488fd.txt +4 -0
  179. package/scripts/notices/runtime-fab3dd6bdab226f1c086.txt +373 -0
  180. package/scripts/notices/runtime-fba3318d0c600380177e.txt +62 -0
  181. package/scripts/notices/runtime-ff8f68cb076caf8cefe7.txt +25 -0
  182. package/scripts/notices/runtime-ff92bed461f50338dd70.txt +74 -0
  183. package/scripts/notices/runtime.json +1632 -0
  184. package/scripts/notices/sources.json +555 -0
  185. package/scripts/notices/xray-MIT.txt +21 -0
  186. package/src/attributes.ts +271 -0
  187. package/src/codec.ts +74 -0
  188. package/src/dom.ts +87 -0
  189. package/src/extract-plan.ts +97 -0
  190. package/src/extract.ts +130 -0
  191. package/src/hooks.ts +220 -0
  192. package/src/index.ts +223 -0
  193. package/src/io.ts +491 -0
  194. package/src/native.ts +135 -0
  195. package/src/nodes.ts +290 -0
  196. package/src/parse.ts +53 -0
  197. package/src/parser-types.ts +157 -0
  198. package/src/router.ts +327 -0
  199. package/src/selection.ts +566 -0
  200. package/src/slim.ts +12 -0
  201. package/src/transport.ts +104 -0
  202. package/src/types.ts +186 -0
  203. package/src/utils.ts +29 -0
  204. package/src/workers.ts +21 -0
  205. package/tsconfig.json +15 -0
  206. 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"&amp;"),
108
+ '\u{00A0}' => self.writer.write_all(b"&nbsp;"),
109
+ '"' if attr_mode => self.writer.write_all(b"&quot;"),
110
+ '<' if !attr_mode => self.writer.write_all(b"&lt;"),
111
+ '>' if !attr_mode => self.writer.write_all(b"&gt;"),
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
+ // &not => match for U+00AC
346
+ // &noti => valid prefix for &notin
347
+ // &notit => 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
+ }