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