1#![cfg_attr(crown, expect(crown::unrooted_must_root))]
6
7use std::cell::Cell;
8use std::io;
9
10use html5ever::buffer_queue::BufferQueue;
11use html5ever::serialize::TraversalScope::IncludeNode;
12use html5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
13use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts};
14use html5ever::tree_builder::{QuirksMode as HTML5EverQuirksMode, TreeBuilder, TreeBuilderOpts};
15use html5ever::{QualName, local_name, ns};
16use markup5ever::TokenizerResult;
17use script_bindings::script_runtime::temp_cx;
18use script_bindings::trace::CustomTraceable;
19use servo_url::ServoUrl;
20use style::attr::AttrValue;
21use style::context::QuirksMode as StyleContextQuirksMode;
22use xml5ever::LocalName;
23
24use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
25use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode;
26use crate::dom::bindings::codegen::GenericBindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
27use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
28use crate::dom::bindings::root::{Dom, DomRoot};
29use crate::dom::characterdata::CharacterData;
30use crate::dom::document::Document;
31use crate::dom::documentfragment::DocumentFragment;
32use crate::dom::documenttype::DocumentType;
33use crate::dom::element::Element;
34use crate::dom::html::htmlscriptelement::HTMLScriptElement;
35use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
36use crate::dom::node::Node;
37use crate::dom::processinginstruction::ProcessingInstruction;
38use crate::dom::servoparser::{ParsingAlgorithm, Sink};
39use crate::dom::shadowroot::ShadowRoot;
40
41#[derive(JSTraceable, MallocSizeOf)]
42#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
43pub(crate) struct Tokenizer {
44 #[ignore_malloc_size_of = "Defined in html5ever"]
45 inner: HtmlTokenizer<TreeBuilder<Dom<Node>, Sink>>,
46}
47
48impl Tokenizer {
49 pub(crate) fn new(
50 document: &Document,
51 url: ServoUrl,
52 fragment_context: Option<super::FragmentContext>,
53 parsing_algorithm: ParsingAlgorithm,
54 ) -> Self {
55 let custom_element_reaction_stack = document.custom_element_reaction_stack();
56 let sink = Sink {
57 base_url: url,
58 document: Dom::from_ref(document),
59 current_line: Cell::new(1),
60 script: Default::default(),
61 parsing_algorithm,
62 custom_element_reaction_stack,
63 };
64
65 let quirks_mode = match document.quirks_mode() {
66 StyleContextQuirksMode::Quirks => HTML5EverQuirksMode::Quirks,
67 StyleContextQuirksMode::LimitedQuirks => HTML5EverQuirksMode::LimitedQuirks,
68 StyleContextQuirksMode::NoQuirks => HTML5EverQuirksMode::NoQuirks,
69 };
70
71 let options = TreeBuilderOpts {
72 scripting_enabled: document.scripting_enabled(),
73 iframe_srcdoc: document.url().as_str() == "about:srcdoc",
74 quirks_mode,
75 ..Default::default()
76 };
77
78 let inner = if let Some(fragment_context) = fragment_context {
79 let tree_builder = TreeBuilder::new_for_fragment(
80 sink,
81 Dom::from_ref(fragment_context.context_elem),
82 fragment_context.form_elem.map(Dom::from_ref),
83 options,
84 );
85
86 let tokenizer_options = TokenizerOpts {
87 initial_state: Some(tree_builder.tokenizer_state_for_context_elem(
88 fragment_context.context_element_allows_scripting,
89 )),
90 ..Default::default()
91 };
92
93 HtmlTokenizer::new(tree_builder, tokenizer_options)
94 } else {
95 HtmlTokenizer::new(TreeBuilder::new(sink, options), Default::default())
96 };
97
98 Tokenizer { inner }
99 }
100
101 pub(crate) fn feed(&self, input: &BufferQueue) -> TokenizerResult<DomRoot<HTMLScriptElement>> {
102 match self.inner.feed(input) {
103 TokenizerResult::Done => TokenizerResult::Done,
104 TokenizerResult::Script(script) => {
105 TokenizerResult::Script(DomRoot::from_ref(script.downcast().unwrap()))
106 },
107 TokenizerResult::EncodingIndicator(encoding) => {
108 TokenizerResult::EncodingIndicator(encoding)
109 },
110 }
111 }
112
113 pub(crate) fn end(&self) {
114 self.inner.end();
115 }
116
117 pub(crate) fn url(&self) -> &ServoUrl {
118 &self.inner.sink.sink.base_url
119 }
120
121 pub(crate) fn set_plaintext_state(&self) {
122 self.inner.set_plaintext_state();
123 }
124
125 pub(crate) fn get_current_line(&self) -> u32 {
126 self.inner.sink.sink.current_line.get() as u32
127 }
128}
129
130fn start_element<S: Serializer>(element: &Element, serializer: &mut S) -> io::Result<()> {
132 let name = QualName::new(
133 None,
134 element.namespace().clone(),
135 element.local_name().clone(),
136 );
137
138 let mut attributes = vec![];
139
140 if !element.has_attribute(&LocalName::from("is")) &&
143 let Some(is_value) = element.get_is()
144 {
145 let qualified_name = QualName::new(None, ns!(), LocalName::from("is"));
146
147 attributes.push((qualified_name, AttrValue::String(is_value.to_string())));
148 }
149
150 attributes.extend(element.attrs().borrow().iter().map(|attr| {
152 let qname = QualName::new(None, attr.namespace().clone(), attr.local_name().clone());
153 let value = attr.value().clone();
154 (qname, value)
155 }));
156
157 let attr_refs = attributes.iter().map(|(qname, value)| {
158 let ar: AttrRef = (qname, &**value);
159 ar
160 });
161 serializer.start_elem(name, attr_refs)?;
162 Ok(())
163}
164
165enum SerializationCommand {
166 OpenElement(DomRoot<Element>),
167 CloseElement(QualName),
168 SerializeNonelement(DomRoot<Node>),
169 SerializeShadowRoot(DomRoot<ShadowRoot>),
170}
171
172struct SerializationIterator {
173 stack: Vec<SerializationCommand>,
174
175 serialize_shadow_roots: bool,
177
178 shadow_roots: Vec<DomRoot<ShadowRoot>>,
180}
181
182impl SerializationIterator {
183 fn new(
184 cx: &mut js::context::JSContext,
185 node: &Node,
186 skip_first: bool,
187 serialize_shadow_roots: bool,
188 shadow_roots: Vec<DomRoot<ShadowRoot>>,
189 ) -> SerializationIterator {
190 let mut ret = SerializationIterator {
191 stack: vec![],
192 serialize_shadow_roots,
193 shadow_roots,
194 };
195 if skip_first || node.is::<DocumentFragment>() || node.is::<Document>() {
196 ret.handle_node_contents(cx, node);
197 } else {
198 ret.push_node(node);
199 }
200 ret
201 }
202
203 fn handle_node_contents(&mut self, cx: &mut js::context::JSContext, node: &Node) {
204 if node.downcast::<Element>().is_some_and(Element::is_void) {
205 return;
206 }
207
208 if let Some(template_element) = node.downcast::<HTMLTemplateElement>() {
209 for child in template_element.Content(cx).upcast::<Node>().rev_children() {
210 self.push_node(&child);
211 }
212 } else {
213 for child in node.rev_children() {
214 self.push_node(&child);
215 }
216 }
217
218 if let Some(shadow_root) = node.downcast::<Element>().and_then(Element::shadow_root) {
219 let should_be_serialized = (self.serialize_shadow_roots && shadow_root.Serializable()) ||
220 self.shadow_roots.contains(&shadow_root);
221 if !shadow_root.is_user_agent_widget() && should_be_serialized {
222 self.stack
223 .push(SerializationCommand::SerializeShadowRoot(shadow_root));
224 }
225 }
226 }
227
228 fn push_node(&mut self, node: &Node) {
229 let Some(element) = node.downcast::<Element>() else {
230 self.stack.push(SerializationCommand::SerializeNonelement(
231 DomRoot::from_ref(node),
232 ));
233 return;
234 };
235
236 self.stack
237 .push(SerializationCommand::OpenElement(DomRoot::from_ref(
238 element,
239 )));
240 }
241}
242
243impl Iterator for SerializationIterator {
244 type Item = SerializationCommand;
245
246 #[expect(unsafe_code)]
247 fn next(&mut self) -> Option<SerializationCommand> {
248 let mut cx = unsafe { temp_cx() };
250 let cx = &mut cx;
251 let res = self.stack.pop()?;
252
253 match &res {
254 SerializationCommand::OpenElement(element) => {
255 let name = QualName::new(
256 None,
257 element.namespace().clone(),
258 element.local_name().clone(),
259 );
260 self.stack.push(SerializationCommand::CloseElement(name));
261 self.handle_node_contents(cx, element.upcast());
262 },
263 SerializationCommand::SerializeShadowRoot(shadow_root) => {
264 self.stack
265 .push(SerializationCommand::CloseElement(QualName::new(
266 None,
267 ns!(),
268 local_name!("template"),
269 )));
270 self.handle_node_contents(cx, shadow_root.upcast());
271 },
272 _ => {},
273 }
274
275 Some(res)
276 }
277}
278
279pub(crate) fn serialize_html_fragment<S: Serializer>(
281 cx: &mut js::context::JSContext,
282 node: &Node,
283 serializer: &mut S,
284 traversal_scope: TraversalScope,
285 serialize_shadow_roots: bool,
286 shadow_roots: Vec<DomRoot<ShadowRoot>>,
287) -> io::Result<()> {
288 let iter = SerializationIterator::new(
289 cx,
290 node,
291 traversal_scope != IncludeNode,
292 serialize_shadow_roots,
293 shadow_roots,
294 );
295
296 for cmd in iter {
297 match cmd {
298 SerializationCommand::OpenElement(n) => {
299 start_element(&n, serializer)?;
300 },
301 SerializationCommand::CloseElement(name) => {
302 serializer.end_elem(name)?;
303 },
304 SerializationCommand::SerializeNonelement(n) => match n.type_id() {
305 NodeTypeId::DocumentType => {
306 let doctype = n.downcast::<DocumentType>().unwrap();
307 serializer.write_doctype(&doctype.name().str())?;
308 },
309
310 NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
311 let cdata = n.downcast::<CharacterData>().unwrap();
312 serializer.write_text(&cdata.data())?;
313 },
314
315 NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
316 let cdata = n.downcast::<CharacterData>().unwrap();
317 serializer.write_comment(&cdata.data())?;
318 },
319
320 NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
321 let pi = n.downcast::<ProcessingInstruction>().unwrap();
322 let data = pi.upcast::<CharacterData>().data();
323 serializer.write_processing_instruction(&pi.target().str(), &data)?;
324 },
325
326 NodeTypeId::DocumentFragment(_) | NodeTypeId::Attr => {},
327
328 NodeTypeId::Document(_) => panic!("Can't serialize Document node itself"),
329 NodeTypeId::Element(_) => panic!("Element shouldn't appear here"),
330 },
331 SerializationCommand::SerializeShadowRoot(shadow_root) => {
332 let mut attributes = vec![];
336 let mut push_attribute = |name, value| {
337 let qualified_name = QualName::new(None, ns!(), LocalName::from(name));
338 attributes.push((qualified_name, value))
339 };
340
341 let mode = if shadow_root.Mode() == ShadowRootMode::Open {
342 "open"
343 } else {
344 "closed"
345 };
346 push_attribute("shadowrootmode", mode);
347
348 if shadow_root.DelegatesFocus() {
349 push_attribute("shadowrootdelegatesfocus", "");
350 }
351
352 if shadow_root.Serializable() {
353 push_attribute("shadowrootserializable", "");
354 }
355
356 if shadow_root.Clonable() {
357 push_attribute("shadowrootclonable", "");
358 }
359
360 let name = QualName::new(None, ns!(), local_name!("template"));
361 serializer.start_elem(name, attributes.iter().map(|(a, b)| (a, *b)))?;
362 },
363 }
364 }
365
366 Ok(())
367}
368
369pub(crate) struct HtmlSerialize<'a> {
370 node: &'a Node,
371}
372
373impl<'a> HtmlSerialize<'a> {
374 pub(crate) fn new(node: &'a Node) -> HtmlSerialize<'a> {
375 HtmlSerialize { node }
376 }
377}
378
379impl Serialize for HtmlSerialize<'_> {
380 #[expect(unsafe_code)]
381 fn serialize<S>(&self, serializer: &mut S, traversal_scope: TraversalScope) -> io::Result<()>
382 where
383 S: Serializer,
384 {
385 let mut cx = unsafe { temp_cx() };
387 let cx = &mut cx;
388 serialize_html_fragment(cx, self.node, serializer, traversal_scope, false, vec![])
389 }
390}