1use std::borrow::Cow;
6use std::cell::{Cell, RefCell};
7use std::mem;
8use std::rc::Rc;
9
10use base64::Engine as _;
11use base64::engine::general_purpose;
12use content_security_policy::sandboxing_directive::SandboxingFlagSet;
13use devtools_traits::ScriptToDevtoolsControlMsg;
14use dom_struct::dom_struct;
15use embedder_traits::resources::{self, Resource};
16use encoding_rs::{Encoding, UTF_8};
17use html5ever::buffer_queue::BufferQueue;
18use html5ever::tendril::StrTendril;
19use html5ever::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeSink};
20use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, ns};
21use hyper_serde::Serde;
22use markup5ever::TokenizerResult;
23use mime::{self, Mime};
24use net_traits::mime_classifier::{ApacheBugFlag, MediaType, MimeClassifier, NoSniffFlag};
25use net_traits::policy_container::PolicyContainer;
26use net_traits::request::RequestId;
27use net_traits::{
28 FetchMetadata, LoadContext, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
29};
30use profile_traits::time::{
31 ProfilerCategory, ProfilerChan, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
32};
33use profile_traits::time_profile;
34use script_bindings::cell::DomRefCell;
35use script_bindings::reflector::{Reflector, reflect_dom_object};
36use script_bindings::script_runtime::temp_cx;
37use script_traits::DocumentActivity;
38use servo_base::cross_process_instant::CrossProcessInstant;
39use servo_base::id::{PipelineId, WebViewId};
40use servo_config::pref;
41use servo_constellation_traits::{LoadOrigin, TargetSnapshotParams};
42use servo_url::{MutableOrigin, ServoUrl};
43use style::context::QuirksMode as ServoQuirksMode;
44use tendril::stream::LossyDecoder;
45use tendril::{ByteTendril, TendrilSink};
46
47use crate::document_loader::{DocumentLoader, LoadType};
48use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
49 DocumentMethods, DocumentReadyState,
50};
51use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
52use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods;
53use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
54use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
55use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
56 ShadowRootMode, SlotAssignmentMode,
57};
58use crate::dom::bindings::inheritance::Castable;
59use crate::dom::bindings::refcounted::Trusted;
60use crate::dom::bindings::reflector::DomGlobal;
61use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
62use crate::dom::bindings::settings_stack::is_execution_stack_empty;
63use crate::dom::bindings::str::{DOMString, USVString};
64use crate::dom::characterdata::CharacterData;
65use crate::dom::comment::Comment;
66use crate::dom::csp::{Violation, parse_csp_list_from_metadata};
67use crate::dom::customelementregistry::CustomElementReactionStack;
68use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
69use crate::dom::documentfragment::DocumentFragment;
70use crate::dom::documenttype::DocumentType;
71use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
72use crate::dom::globalscope::GlobalScope;
73use crate::dom::html::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
74use crate::dom::html::htmlimageelement::HTMLImageElement;
75use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
76use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
77use crate::dom::node::{Node, ShadowIncluding};
78use crate::dom::performance::performanceentry::PerformanceEntry;
79use crate::dom::performance::performancenavigationtiming::PerformanceNavigationTiming;
80use crate::dom::processinginstruction::ProcessingInstruction;
81use crate::dom::processingoptions::{
82 LinkHeader, LinkProcessingPhase, extract_links_from_headers, process_link_headers,
83};
84use crate::dom::reporting::reportingendpoint::ReportingEndpoint;
85use crate::dom::security::csp::CspReporting;
86use crate::dom::security::xframeoptions::check_a_navigation_response_adherence_to_x_frame_options;
87use crate::dom::shadowroot::IsUserAgentWidget;
88use crate::dom::text::Text;
89use crate::dom::types::{HTMLElement, HTMLMediaElement, HTMLOptionElement};
90use crate::dom::virtualmethods::vtable_for;
91use crate::navigation::determine_the_origin;
92use crate::network_listener::FetchResponseListener;
93use crate::realms::{enter_auto_realm, enter_realm};
94use crate::script_runtime::{CanGc, IntroductionType};
95use crate::script_thread::ScriptThread;
96
97mod async_html;
98pub(crate) mod encoding;
99pub(crate) mod html;
100mod prefetch;
101mod xml;
102
103use encoding::{NetworkDecoderState, NetworkSink};
104pub(crate) use html::serialize_html_fragment;
105
106#[dom_struct]
107pub(crate) struct ServoParser {
120 reflector: Reflector,
121 document: Dom<Document>,
123 network_decoder: DomRefCell<NetworkDecoderState>,
125 #[ignore_malloc_size_of = "Defined in html5ever"]
127 #[no_trace]
128 network_input: BufferQueue,
129 #[ignore_malloc_size_of = "Defined in html5ever"]
131 #[no_trace]
132 script_input: BufferQueue,
133 tokenizer: Tokenizer,
135 last_chunk_received: Cell<bool>,
137 suspended: Cell<bool>,
139 script_nesting_level: Cell<usize>,
141 aborted: Cell<bool>,
143 stopped: Cell<bool>,
145 script_created_parser: bool,
147 #[no_trace]
152 prefetch_decoder: RefCell<LossyDecoder<NetworkSink>>,
153 prefetch_tokenizer: prefetch::Tokenizer,
157 #[ignore_malloc_size_of = "Defined in html5ever"]
158 #[no_trace]
159 prefetch_input: BufferQueue,
160 content_for_devtools: Option<DomRefCell<String>>,
163}
164
165pub(crate) struct ElementAttribute {
166 name: QualName,
167 value: DOMString,
168}
169
170#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
171pub(crate) enum ParsingAlgorithm {
172 Normal,
173 Fragment,
174}
175
176impl ElementAttribute {
177 pub(crate) fn new(name: QualName, value: DOMString) -> ElementAttribute {
178 ElementAttribute { name, value }
179 }
180}
181
182impl ServoParser {
183 pub(crate) fn parser_is_not_active(&self) -> bool {
184 self.can_write()
185 }
186
187 pub(crate) fn parse_html_document(
189 document: &Document,
190 input: Option<DOMString>,
191 url: ServoUrl,
192 encoding_hint_from_content_type: Option<&'static Encoding>,
193 encoding_of_container_document: Option<&'static Encoding>,
194 cx: &mut js::context::JSContext,
195 ) {
196 assert!(document.is_html_document());
200
201 let parser = ServoParser::new(
203 document,
204 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
205 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
206 } else {
207 Tokenizer::Html(self::html::Tokenizer::new(
208 document,
209 url,
210 None,
211 ParsingAlgorithm::Normal,
212 ))
213 },
214 ParserKind::Normal,
215 encoding_hint_from_content_type,
216 encoding_of_container_document,
217 CanGc::from_cx(cx),
218 );
219
220 if let Some(input) = input {
226 parser.parse_complete_string_chunk(String::from(input), cx);
227 } else {
228 parser.document.set_current_parser(Some(&parser));
229 }
230 }
231
232 pub(crate) fn parse_html_fragment<'el>(
234 context: &'el Element,
235 input: DOMString,
236 allow_declarative_shadow_roots: bool,
237 cx: &mut js::context::JSContext,
238 ) -> impl Iterator<Item = DomRoot<Node>> + use<'el> {
239 let context_node = context.upcast::<Node>();
240 let context_document = context_node.owner_doc();
241 let window = context_document.window();
242 let url = context_document.url();
243
244 let loader = DocumentLoader::new_with_threads(
246 context_document.loader().resource_threads().clone(),
247 Some(url.clone()),
248 );
249 let document = Document::new(
250 window,
251 HasBrowsingContext::No,
252 Some(url.clone()),
253 context_document.about_base_url(),
254 context_document.origin().clone(),
255 IsHTMLDocument::HTMLDocument,
256 None,
257 None,
258 DocumentActivity::Inactive,
259 DocumentSource::FromParser,
260 loader,
261 None,
262 None,
263 Default::default(),
264 false,
265 allow_declarative_shadow_roots,
266 Some(context_document.insecure_requests_policy()),
267 context_document.has_trustworthy_ancestor_or_current_origin(),
268 context_document.custom_element_reaction_stack(),
269 context_document.creation_sandboxing_flag_set(),
270 CanGc::from_cx(cx),
271 );
272
273 document.set_quirks_mode(context_document.quirks_mode());
277
278 let form = context_node
285 .inclusive_ancestors(ShadowIncluding::No)
286 .find(|element| element.is::<HTMLFormElement>());
287
288 let fragment_context = FragmentContext {
289 context_elem: context_node,
290 form_elem: form.as_deref(),
291 context_element_allows_scripting: context_document.scripting_enabled(),
292 };
293
294 let parser = ServoParser::new(
295 &document,
296 Tokenizer::Html(self::html::Tokenizer::new(
297 &document,
298 url,
299 Some(fragment_context),
300 ParsingAlgorithm::Fragment,
301 )),
302 ParserKind::Normal,
303 None,
304 None,
305 CanGc::from_cx(cx),
306 );
307 parser.parse_complete_string_chunk(String::from(input), cx);
308
309 let root_element = document.GetDocumentElement().expect("no document element");
311 FragmentParsingResult {
312 inner: root_element.upcast::<Node>().children(),
313 }
314 }
315
316 pub(crate) fn parse_html_script_input(document: &Document, url: ServoUrl) {
317 let parser = ServoParser::new(
318 document,
319 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
320 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
321 } else {
322 Tokenizer::Html(self::html::Tokenizer::new(
323 document,
324 url,
325 None,
326 ParsingAlgorithm::Normal,
327 ))
328 },
329 ParserKind::ScriptCreated,
330 None,
331 None,
332 CanGc::deprecated_note(),
333 );
334 document.set_current_parser(Some(&parser));
335 }
336
337 pub(crate) fn parse_xml_document(
338 document: &Document,
339 input: Option<DOMString>,
340 url: ServoUrl,
341 encoding_hint_from_content_type: Option<&'static Encoding>,
342 cx: &mut js::context::JSContext,
343 ) {
344 let parser = ServoParser::new(
345 document,
346 Tokenizer::Xml(self::xml::Tokenizer::new(document, url)),
347 ParserKind::Normal,
348 encoding_hint_from_content_type,
349 None,
350 CanGc::from_cx(cx),
351 );
352
353 if let Some(input) = input {
355 parser.parse_complete_string_chunk(String::from(input), cx);
356 } else {
357 parser.document.set_current_parser(Some(&parser));
358 }
359 }
360
361 pub(crate) fn script_nesting_level(&self) -> usize {
362 self.script_nesting_level.get()
363 }
364
365 pub(crate) fn is_script_created(&self) -> bool {
366 self.script_created_parser
367 }
368
369 pub(crate) fn resume_with_pending_parsing_blocking_script(
384 &self,
385 script: &HTMLScriptElement,
386 result: ScriptResult,
387 cx: &mut js::context::JSContext,
388 ) {
389 assert!(self.suspended.get());
390 self.suspended.set(false);
391
392 self.script_input.swap_with(&self.network_input);
393 while let Some(chunk) = self.script_input.pop_front() {
394 self.network_input.push_back(chunk);
395 }
396
397 let script_nesting_level = self.script_nesting_level.get();
398 assert_eq!(script_nesting_level, 0);
399
400 self.script_nesting_level.set(script_nesting_level + 1);
401 script.execute(cx, result);
402 self.script_nesting_level.set(script_nesting_level);
403
404 if !self.suspended.get() && !self.aborted.get() {
405 self.parse_sync(cx);
406 }
407 }
408
409 pub(crate) fn can_write(&self) -> bool {
410 self.script_created_parser || self.script_nesting_level.get() > 0
411 }
412
413 pub(crate) fn write(&self, text: DOMString, cx: &mut js::context::JSContext) {
415 assert!(self.can_write());
416
417 if self.document.has_pending_parsing_blocking_script() {
418 self.script_input.push_back(String::from(text).into());
422 return;
423 }
424
425 assert!(self.script_input.is_empty());
429
430 let input = BufferQueue::default();
431 input.push_back(String::from(text).into());
432
433 let profiler_chan = self
434 .document
435 .window()
436 .as_global_scope()
437 .time_profiler_chan()
438 .clone();
439 let profiler_metadata = TimerMetadata {
440 url: self.document.url().as_str().into(),
441 iframe: TimerMetadataFrameType::RootWindow,
442 incremental: TimerMetadataReflowType::FirstReflow,
443 };
444 self.tokenize(
445 |cx, tokenizer| {
446 tokenizer.feed(&input, cx, profiler_chan.clone(), profiler_metadata.clone())
447 },
448 cx,
449 );
450
451 if self.suspended.get() {
452 while let Some(chunk) = input.pop_front() {
456 self.script_input.push_back(chunk);
457 }
458 return;
459 }
460
461 assert!(input.is_empty());
462 }
463
464 pub(crate) fn close(&self, cx: &mut js::context::JSContext) {
466 assert!(self.script_created_parser);
467
468 self.last_chunk_received.set(true);
470
471 if self.suspended.get() {
473 return;
474 }
475
476 self.parse_sync(cx);
479 }
480
481 pub(crate) fn abort(&self, cx: &mut js::context::JSContext) {
483 assert!(!self.aborted.get());
484 self.aborted.set(true);
485
486 self.script_input.replace_with(BufferQueue::default());
488 self.network_input.replace_with(BufferQueue::default());
489
490 self.document
492 .set_ready_state(cx, DocumentReadyState::Interactive);
493
494 self.tokenizer.end(cx);
496 self.document.set_current_parser(None);
497
498 self.document
500 .set_ready_state(cx, DocumentReadyState::Complete);
501 }
502
503 pub(crate) fn get_current_line(&self) -> u32 {
504 self.tokenizer.get_current_line()
505 }
506
507 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
508 fn new_inherited(
509 document: &Document,
510 tokenizer: Tokenizer,
511 kind: ParserKind,
512 encoding_hint_from_content_type: Option<&'static Encoding>,
513 encoding_of_container_document: Option<&'static Encoding>,
514 ) -> Self {
515 let content_for_devtools = (document.global().devtools_chan().is_some() &&
519 document.has_browsing_context())
520 .then_some(DomRefCell::new(String::new()));
521
522 ServoParser {
523 reflector: Reflector::new(),
524 document: Dom::from_ref(document),
525 network_decoder: DomRefCell::new(NetworkDecoderState::new(
526 encoding_hint_from_content_type,
527 encoding_of_container_document,
528 )),
529 network_input: BufferQueue::default(),
530 script_input: BufferQueue::default(),
531 tokenizer,
532 last_chunk_received: Cell::new(false),
533 suspended: Default::default(),
534 script_nesting_level: Default::default(),
535 aborted: Default::default(),
536 stopped: Default::default(),
537 script_created_parser: kind == ParserKind::ScriptCreated,
538 prefetch_decoder: RefCell::new(LossyDecoder::new_encoding_rs(
539 encoding_hint_from_content_type.unwrap_or(UTF_8),
540 Default::default(),
541 )),
542 prefetch_tokenizer: prefetch::Tokenizer::new(document),
543 prefetch_input: BufferQueue::default(),
544 content_for_devtools,
545 }
546 }
547
548 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
549 fn new(
550 document: &Document,
551 tokenizer: Tokenizer,
552 kind: ParserKind,
553 encoding_hint_from_content_type: Option<&'static Encoding>,
554 encoding_of_container_document: Option<&'static Encoding>,
555 can_gc: CanGc,
556 ) -> DomRoot<Self> {
557 reflect_dom_object(
558 Box::new(ServoParser::new_inherited(
559 document,
560 tokenizer,
561 kind,
562 encoding_hint_from_content_type,
563 encoding_of_container_document,
564 )),
565 document.window(),
566 can_gc,
567 )
568 }
569
570 fn push_tendril_input_chunk(&self, chunk: StrTendril) {
571 if let Some(mut content_for_devtools) = self
572 .content_for_devtools
573 .as_ref()
574 .map(|content| content.borrow_mut())
575 {
576 content_for_devtools.push_str(chunk.as_ref());
578 }
579
580 if chunk.is_empty() {
581 return;
582 }
583
584 self.network_input.push_back(chunk);
587 }
588
589 fn push_bytes_input_chunk(&self, chunk: Vec<u8>) {
590 if let Some(decoded_chunk) = self
592 .network_decoder
593 .borrow_mut()
594 .push(&chunk, &self.document)
595 {
596 self.push_tendril_input_chunk(decoded_chunk);
597 }
598
599 if self.should_prefetch() {
600 let mut prefetch_decoder = self.prefetch_decoder.borrow_mut();
606 prefetch_decoder.process(ByteTendril::from(&*chunk));
607
608 self.prefetch_input
609 .push_back(mem::take(&mut prefetch_decoder.inner_sink_mut().output));
610 self.prefetch_tokenizer.feed(&self.prefetch_input);
611 }
612 }
613
614 fn should_prefetch(&self) -> bool {
615 self.document.browsing_context().is_some()
623 }
624
625 fn push_string_input_chunk(&self, chunk: String) {
626 let chunk = StrTendril::from(chunk);
629 self.push_tendril_input_chunk(chunk);
630 }
631
632 fn parse_sync(&self, cx: &mut js::context::JSContext) {
633 assert!(self.script_input.is_empty());
634
635 if self.last_chunk_received.get() {
639 let chunk = self.network_decoder.borrow_mut().finish(&self.document);
640 if !chunk.is_empty() {
641 self.push_tendril_input_chunk(chunk);
642 }
643 }
644
645 if self.aborted.get() {
646 return;
647 }
648
649 let profiler_chan = self
650 .document
651 .window()
652 .as_global_scope()
653 .time_profiler_chan()
654 .clone();
655 let profiler_metadata = TimerMetadata {
656 url: self.document.url().as_str().into(),
657 iframe: TimerMetadataFrameType::RootWindow,
658 incremental: TimerMetadataReflowType::FirstReflow,
659 };
660 self.tokenize(
661 |cx, tokenizer| {
662 tokenizer.feed(
663 &self.network_input,
664 cx,
665 profiler_chan.clone(),
666 profiler_metadata.clone(),
667 )
668 },
669 cx,
670 );
671
672 if self.suspended.get() {
673 return;
674 }
675
676 assert!(self.network_input.is_empty());
677
678 if self.last_chunk_received.get() {
679 self.finish(cx);
680 }
681 }
682
683 fn parse_complete_string_chunk(&self, input: String, cx: &mut js::context::JSContext) {
684 self.document.set_current_parser(Some(self));
685 self.push_string_input_chunk(input);
686 self.last_chunk_received.set(true);
687 if !self.suspended.get() {
688 self.parse_sync(cx);
689 }
690 }
691
692 fn parse_bytes_chunk(&self, input: Vec<u8>, cx: &mut js::context::JSContext) {
693 let _realm = enter_realm(&*self.document);
694 self.document.set_current_parser(Some(self));
695 self.push_bytes_input_chunk(input);
696 if !self.suspended.get() {
697 self.parse_sync(cx);
698 }
699 }
700
701 fn tokenize<F>(&self, feed: F, cx: &mut js::context::JSContext)
702 where
703 F: Fn(
704 &mut js::context::JSContext,
705 &Tokenizer,
706 ) -> TokenizerResult<DomRoot<HTMLScriptElement>>,
707 {
708 loop {
709 assert!(!self.suspended.get());
710 assert!(!self.aborted.get());
711
712 self.document.window().reflow_if_reflow_timer_expired(cx);
713 let script = match feed(cx, &self.tokenizer) {
714 TokenizerResult::Done => return,
715 TokenizerResult::EncodingIndicator(_) => continue,
716 TokenizerResult::Script(script) => script,
717 };
718
719 if is_execution_stack_empty() {
726 self.document.window().perform_a_microtask_checkpoint(cx);
727 }
728
729 let script_nesting_level = self.script_nesting_level.get();
730
731 self.script_nesting_level.set(script_nesting_level + 1);
732 script.set_initial_script_text();
733 let introduction_type_override =
734 (script_nesting_level > 0).then_some(IntroductionType::INJECTED_SCRIPT);
735 script.prepare(cx, introduction_type_override);
736 self.script_nesting_level.set(script_nesting_level);
737
738 if self.document.has_pending_parsing_blocking_script() {
739 self.suspended.set(true);
740 return;
741 }
742 if self.aborted.get() {
743 return;
744 }
745 }
746 }
747
748 pub(crate) fn has_aborted(&self) -> bool {
750 self.aborted.get()
751 }
752
753 pub(crate) fn has_stopped(&self) -> bool {
755 self.stopped.get()
756 }
757
758 fn finish(&self, cx: &mut js::context::JSContext) {
760 assert!(!self.suspended.get());
761 assert!(self.last_chunk_received.get());
762 assert!(self.script_input.is_empty());
763 assert!(self.network_input.is_empty());
764 assert!(self.network_decoder.borrow().is_finished());
765
766 self.stopped.set(true);
767
768 self.document.set_current_parser(None);
774
775 self.document
777 .set_ready_state(cx, DocumentReadyState::Interactive);
778
779 self.tokenizer.end(cx);
781
782 let url = self.tokenizer.url().clone();
784 self.document.finish_load(LoadType::PageSource(url), cx);
785
786 if let Some(content_for_devtools) = self
788 .content_for_devtools
789 .as_ref()
790 .map(|content| content.take())
791 {
792 let global = self.document.global();
793 let chan = global.devtools_chan().expect("Guaranteed by new");
794 let pipeline_id = self.document.global().pipeline_id();
795 let _ = chan.send(ScriptToDevtoolsControlMsg::UpdateSourceContent(
796 pipeline_id,
797 content_for_devtools,
798 ));
799 }
800 }
801}
802
803struct FragmentParsingResult<I>
804where
805 I: Iterator<Item = DomRoot<Node>>,
806{
807 inner: I,
808}
809
810impl<I> Iterator for FragmentParsingResult<I>
811where
812 I: Iterator<Item = DomRoot<Node>>,
813{
814 type Item = DomRoot<Node>;
815
816 #[expect(unsafe_code)]
817 fn next(&mut self) -> Option<DomRoot<Node>> {
818 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
819 let cx = &mut cx;
820
821 let next = self.inner.next()?;
822 next.remove_self(cx);
823 Some(next)
824 }
825
826 fn size_hint(&self) -> (usize, Option<usize>) {
827 self.inner.size_hint()
828 }
829}
830
831#[derive(JSTraceable, MallocSizeOf, PartialEq)]
832enum ParserKind {
833 Normal,
834 ScriptCreated,
835}
836
837#[derive(JSTraceable, MallocSizeOf)]
838#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
839enum Tokenizer {
840 Html(self::html::Tokenizer),
841 AsyncHtml(self::async_html::Tokenizer),
842 Xml(self::xml::Tokenizer),
843}
844
845impl Tokenizer {
846 fn feed(
847 &self,
848 input: &BufferQueue,
849 cx: &mut js::context::JSContext,
850 profiler_chan: ProfilerChan,
851 profiler_metadata: TimerMetadata,
852 ) -> TokenizerResult<DomRoot<HTMLScriptElement>> {
853 match *self {
854 Tokenizer::Html(ref tokenizer) => time_profile!(
855 ProfilerCategory::ScriptParseHTML,
856 Some(profiler_metadata),
857 profiler_chan,
858 || tokenizer.feed(input),
859 ),
860 Tokenizer::AsyncHtml(ref tokenizer) => time_profile!(
861 ProfilerCategory::ScriptParseHTML,
862 Some(profiler_metadata),
863 profiler_chan,
864 || tokenizer.feed(input, cx),
865 ),
866 Tokenizer::Xml(ref tokenizer) => time_profile!(
867 ProfilerCategory::ScriptParseXML,
868 Some(profiler_metadata),
869 profiler_chan,
870 || tokenizer.feed(input),
871 ),
872 }
873 }
874
875 fn end(&self, cx: &mut js::context::JSContext) {
876 match *self {
877 Tokenizer::Html(ref tokenizer) => tokenizer.end(),
878 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.end(cx),
879 Tokenizer::Xml(ref tokenizer) => tokenizer.end(),
880 }
881 }
882
883 fn url(&self) -> &ServoUrl {
884 match *self {
885 Tokenizer::Html(ref tokenizer) => tokenizer.url(),
886 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.url(),
887 Tokenizer::Xml(ref tokenizer) => tokenizer.url(),
888 }
889 }
890
891 fn set_plaintext_state(&self) {
892 match *self {
893 Tokenizer::Html(ref tokenizer) => tokenizer.set_plaintext_state(),
894 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.set_plaintext_state(),
895 Tokenizer::Xml(_) => unimplemented!(),
896 }
897 }
898
899 fn get_current_line(&self) -> u32 {
900 match *self {
901 Tokenizer::Html(ref tokenizer) => tokenizer.get_current_line(),
902 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.get_current_line(),
903 Tokenizer::Xml(ref tokenizer) => tokenizer.get_current_line(),
904 }
905 }
906}
907
908struct NavigationParams {
912 policy_container: PolicyContainer,
914 content_type: Option<Mime>,
916 link_headers: Vec<LinkHeader>,
918 final_sandboxing_flag_set: SandboxingFlagSet,
920 resource_header: Vec<u8>,
922 about_base_url: Option<ServoUrl>,
924}
925
926pub(crate) struct ParserContext {
929 parser: Option<Trusted<ServoParser>>,
931 is_synthesized_document: bool,
933 has_loaded_document: bool,
935 webview_id: WebViewId,
937 pipeline_id: PipelineId,
939 url: ServoUrl,
941 pushed_entry_index: Option<usize>,
943 navigation_params: NavigationParams,
945 parent_info: Option<PipelineId>,
947 target_snapshot_params: TargetSnapshotParams,
948 load_origin: LoadOrigin,
949}
950
951impl ParserContext {
952 pub(crate) fn new(
953 webview_id: WebViewId,
954 pipeline_id: PipelineId,
955 url: ServoUrl,
956 creation_sandboxing_flag_set: SandboxingFlagSet,
957 parent_info: Option<PipelineId>,
958 target_snapshot_params: TargetSnapshotParams,
959 load_origin: LoadOrigin,
960 ) -> ParserContext {
961 ParserContext {
962 parser: None,
963 is_synthesized_document: false,
964 has_loaded_document: false,
965 webview_id,
966 pipeline_id,
967 url,
968 parent_info,
969 pushed_entry_index: None,
970 navigation_params: NavigationParams {
971 policy_container: Default::default(),
972 content_type: None,
973 link_headers: vec![],
974 final_sandboxing_flag_set: creation_sandboxing_flag_set,
975 resource_header: vec![],
976 about_base_url: Default::default(),
977 },
978 target_snapshot_params,
979 load_origin,
980 }
981 }
982
983 pub(crate) fn set_policy_container(&mut self, policy_container: Option<&PolicyContainer>) {
984 let Some(policy_container) = policy_container else {
985 return;
986 };
987 self.navigation_params.policy_container = policy_container.clone();
988 }
989
990 pub(crate) fn set_about_base_url(&mut self, about_base_url: Option<ServoUrl>) {
991 self.navigation_params.about_base_url = about_base_url;
992 }
993
994 pub(crate) fn get_document(&self) -> Option<DomRoot<Document>> {
995 self.parser
996 .as_ref()
997 .map(|parser| parser.root().document.as_rooted())
998 }
999
1000 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
1001 self.parent_info
1002 }
1003
1004 fn create_policy_container_from_fetch_response(metadata: &Metadata) -> PolicyContainer {
1006 PolicyContainer {
1013 csp_list: parse_csp_list_from_metadata(&metadata.headers),
1015 embedder_policy: Default::default(),
1019 referrer_policy: ReferrerPolicy::parse_header_for_response(&metadata.headers),
1021 }
1022 }
1023
1024 fn initialize_document_object(&self, document: &Document) {
1026 document.set_policy_container(self.navigation_params.policy_container.clone());
1028 document.set_active_sandboxing_flag_set(self.navigation_params.final_sandboxing_flag_set);
1029 document.set_about_base_url(self.navigation_params.about_base_url.clone());
1030 process_link_headers(
1032 &self.navigation_params.link_headers,
1033 document,
1034 LinkProcessingPhase::PreMedia,
1035 );
1036 }
1037
1038 fn process_link_headers_in_media_phase_with_task(&mut self, document: &Document) {
1040 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1044 if !link_headers.is_empty() {
1045 let window = document.window();
1046 let document = Trusted::new(document);
1047 window
1048 .upcast::<GlobalScope>()
1049 .task_manager()
1050 .networking_task_source()
1051 .queue(task!(process_link_headers_task: move || {
1052 process_link_headers(&link_headers, &document.root(), LinkProcessingPhase::Media);
1053 }));
1054 }
1055 }
1056
1057 fn load_document(&mut self, cx: &mut js::context::JSContext) {
1059 assert!(!self.has_loaded_document);
1060 self.has_loaded_document = true;
1061 let Some(ref parser) = self.parser.as_ref().map(|p| p.root()) else {
1062 return;
1063 };
1064 let content_type = &self.navigation_params.content_type;
1066 let mime_type = MimeClassifier::default().classify(
1067 LoadContext::Browsing,
1068 NoSniffFlag::Off,
1069 ApacheBugFlag::from_content_type(content_type.as_ref()),
1070 content_type,
1071 &self.navigation_params.resource_header,
1072 );
1073 let Some(media_type) = MimeClassifier::get_media_type(&mime_type) else {
1077 let page = format!(
1078 "<html><body><p>Unknown content type ({}).</p></body></html>",
1079 &mime_type,
1080 );
1081 self.load_inline_unknown_content(parser, page, cx);
1082 return;
1083 };
1084 match media_type {
1085 MediaType::Html => self.load_html_document(parser),
1087 MediaType::Xml => self.load_xml_document(parser),
1089 MediaType::JavaScript | MediaType::Text | MediaType::Css => {
1091 self.load_text_document(parser, cx)
1092 },
1093 MediaType::Json => self.load_json_document(parser, cx),
1095 MediaType::Image | MediaType::AudioVideo => {
1097 self.load_media_document(parser, media_type, &mime_type, cx);
1098 return;
1099 },
1100 MediaType::Font => {
1101 let page = format!(
1102 "<html><body><p>Unable to load font with content type ({}).</p></body></html>",
1103 &mime_type,
1104 );
1105 self.load_inline_unknown_content(parser, page, cx);
1106 return;
1107 },
1108 };
1109
1110 parser.parse_bytes_chunk(
1111 std::mem::take(&mut self.navigation_params.resource_header),
1112 cx,
1113 );
1114 }
1115
1116 fn load_html_document(&mut self, parser: &ServoParser) {
1118 self.initialize_document_object(&parser.document);
1121 self.process_link_headers_in_media_phase_with_task(&parser.document);
1125 }
1126
1127 fn load_xml_document(&mut self, parser: &ServoParser) {
1129 self.initialize_document_object(&parser.document);
1135 self.process_link_headers_in_media_phase_with_task(&parser.document);
1139 }
1140
1141 fn load_text_document(&mut self, parser: &ServoParser, cx: &mut js::context::JSContext) {
1143 self.initialize_document_object(&parser.document);
1146 let page = "<pre>\n".into();
1153 parser.push_string_input_chunk(page);
1154 parser.parse_sync(cx);
1155 parser.tokenizer.set_plaintext_state();
1156 self.process_link_headers_in_media_phase_with_task(&parser.document);
1160 }
1161
1162 fn load_media_document(
1164 &mut self,
1165 parser: &ServoParser,
1166 media_type: MediaType,
1167 mime_type: &Mime,
1168 cx: &mut js::context::JSContext,
1169 ) {
1170 self.initialize_document_object(&parser.document);
1173 self.is_synthesized_document = true;
1175 parser.last_chunk_received.set(true);
1176 let page = "<html><body></body></html>".into();
1178 parser.push_string_input_chunk(page);
1179 parser.parse_sync(cx);
1180
1181 let doc = &parser.document;
1182 let node = if media_type == MediaType::Image {
1185 let img = Element::create(
1186 cx,
1187 QualName::new(None, ns!(html), local_name!("img")),
1188 None,
1189 doc,
1190 ElementCreator::ParserCreated(1),
1191 CustomElementCreationMode::Asynchronous,
1192 None,
1193 );
1194 let img = DomRoot::downcast::<HTMLImageElement>(img).unwrap();
1195 img.SetSrc(cx, USVString(self.url.to_string()));
1196 DomRoot::upcast::<Node>(img)
1197 } else if mime_type.type_() == mime::AUDIO {
1198 let audio = Element::create(
1199 cx,
1200 QualName::new(None, ns!(html), local_name!("audio")),
1201 None,
1202 doc,
1203 ElementCreator::ParserCreated(1),
1204 CustomElementCreationMode::Asynchronous,
1205 None,
1206 );
1207 let audio = DomRoot::downcast::<HTMLMediaElement>(audio).unwrap();
1208 audio.SetControls(cx, true);
1209 audio.SetSrc(cx, USVString(self.url.to_string()));
1210 DomRoot::upcast::<Node>(audio)
1211 } else {
1212 let video = Element::create(
1213 cx,
1214 QualName::new(None, ns!(html), local_name!("video")),
1215 None,
1216 doc,
1217 ElementCreator::ParserCreated(1),
1218 CustomElementCreationMode::Asynchronous,
1219 None,
1220 );
1221 let video = DomRoot::downcast::<HTMLMediaElement>(video).unwrap();
1222 video.SetControls(cx, true);
1223 video.SetSrc(cx, USVString(self.url.to_string()));
1224 DomRoot::upcast::<Node>(video)
1225 };
1226 let doc_body = DomRoot::upcast::<Node>(doc.GetBody().unwrap());
1228 doc_body.AppendChild(cx, &node).expect("Appending failed");
1229 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1231 process_link_headers(&link_headers, doc, LinkProcessingPhase::Media);
1232 }
1233
1234 fn load_json_document(&mut self, parser: &ServoParser, cx: &mut js::context::JSContext) {
1236 self.initialize_document_object(&parser.document);
1237 parser.push_string_input_chunk(resources::read_string(Resource::JsonViewerHTML));
1238 parser.parse_sync(cx);
1239 parser.tokenizer.set_plaintext_state();
1240 self.process_link_headers_in_media_phase_with_task(&parser.document);
1241 }
1242
1243 fn load_inline_unknown_content(
1245 &mut self,
1246 parser: &ServoParser,
1247 page: String,
1248 cx: &mut js::context::JSContext,
1249 ) {
1250 self.is_synthesized_document = true;
1251 parser.document.mark_as_internal();
1252 parser.push_string_input_chunk(page);
1253 parser.last_chunk_received.set(true);
1255 parser.parse_sync(cx);
1256 }
1257
1258 fn submit_resource_timing(&mut self) {
1260 let Some(parser) = self.parser.as_ref() else {
1261 return;
1262 };
1263 let parser = parser.root();
1264 if parser.aborted.get() {
1265 return;
1266 }
1267
1268 let document = &parser.document;
1269
1270 let performance_entry = PerformanceNavigationTiming::new(
1272 &document.global(),
1273 CrossProcessInstant::now(),
1274 document,
1275 CanGc::deprecated_note(),
1276 );
1277 self.pushed_entry_index = document
1278 .global()
1279 .performance()
1280 .queue_entry(performance_entry.upcast::<PerformanceEntry>());
1281 }
1282}
1283
1284impl FetchResponseListener for ParserContext {
1285 fn process_request_body(&mut self, _: RequestId) {}
1286
1287 fn process_response(
1290 &mut self,
1291 cx: &mut js::context::JSContext,
1292 _: RequestId,
1293 meta_result: Result<FetchMetadata, NetworkError>,
1294 ) {
1295 let (metadata, mut error) = match meta_result {
1296 Ok(meta) => (
1297 Some(match meta {
1298 FetchMetadata::Unfiltered(m) => m,
1299 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
1300 }),
1301 None,
1302 ),
1303 Err(error) => (
1304 match &error {
1306 NetworkError::LoadCancelled => {
1307 return;
1308 },
1309 _ => {
1310 let mut meta = Metadata::default(self.url.clone());
1311 let mime: Option<Mime> = "text/html".parse().ok();
1312 meta.set_content_type(mime.as_ref());
1313 Some(meta)
1314 },
1315 },
1316 Some(error),
1317 ),
1318 };
1319 let content_type: Option<Mime> = metadata
1320 .clone()
1321 .and_then(|meta| meta.content_type)
1322 .map(Serde::into_inner)
1323 .map(Into::into);
1324
1325 let (policy_container, endpoints_list, link_headers) = match metadata.as_ref() {
1330 None => (PolicyContainer::default(), None, vec![]),
1331 Some(metadata) => (
1332 Self::create_policy_container_from_fetch_response(metadata),
1333 ReportingEndpoint::parse_reporting_endpoints_header(
1334 &self.url.clone(),
1335 &metadata.headers,
1336 ),
1337 extract_links_from_headers(&metadata.headers),
1338 ),
1339 };
1340
1341 let final_sandboxing_flag_set = policy_container
1345 .csp_list
1346 .as_ref()
1347 .and_then(|csp| csp.get_sandboxing_flag_set_for_document())
1348 .unwrap_or(SandboxingFlagSet::empty())
1349 .union(self.target_snapshot_params.sandboxing_flags);
1350
1351 let source_origin = match self.load_origin {
1355 LoadOrigin::Script(ref snapshot) => {
1356 Some(MutableOrigin::from_snapshot(snapshot.clone()))
1357 },
1358 _ => None,
1359 };
1360 let origin = determine_the_origin(
1361 metadata.as_ref().map(|metadata| &metadata.final_url),
1362 final_sandboxing_flag_set,
1363 source_origin,
1364 );
1365
1366 let parser = match ScriptThread::page_headers_available(
1367 self.webview_id,
1368 self.pipeline_id,
1369 metadata.as_ref(),
1370 origin.clone(),
1371 cx,
1372 ) {
1373 Some(parser) => parser,
1374 None => return,
1375 };
1376 if parser.aborted.get() {
1377 return;
1378 }
1379
1380 let mut realm = enter_auto_realm(cx, &*parser.document);
1381 let cx = &mut realm;
1382 let document = &parser.document;
1383 let window = document.window();
1384
1385 if
1388 policy_container.csp_list.should_navigation_response_to_navigation_request_be_blocked(
1395 window,
1396 self.url.clone().into_url(),
1397 &origin.immutable().clone().into_url_origin(),
1398 )
1399 || !check_a_navigation_response_adherence_to_x_frame_options(
1407 window,
1408 policy_container.csp_list.as_ref(),
1409 &origin,
1410 metadata
1411 .as_ref()
1412 .and_then(|metadata| metadata.headers.as_ref()),
1413 ) {
1414 error = Some(NetworkError::ContentSecurityPolicy);
1418 document.make_document_unsalvageable();
1420 }
1425
1426 if let Some(endpoints) = endpoints_list {
1427 window.set_endpoints_list(endpoints);
1428 }
1429 self.parser = Some(Trusted::new(&*parser));
1430 self.navigation_params = NavigationParams {
1431 policy_container,
1432 content_type,
1433 final_sandboxing_flag_set,
1434 link_headers,
1435 about_base_url: document.about_base_url(),
1436 resource_header: vec![],
1437 };
1438 self.submit_resource_timing();
1439
1440 if let Some(error) = error {
1448 let page = match error {
1449 NetworkError::SslValidation(reason, bytes) => {
1450 let page = resources::read_string(Resource::BadCertHTML);
1451 let page = page.replace("${reason}", &reason);
1452 let encoded_bytes = general_purpose::STANDARD_NO_PAD.encode(bytes);
1453 let page = page.replace("${bytes}", encoded_bytes.as_str());
1454 page.replace("${secret}", &net_traits::PRIVILEGED_SECRET.to_string())
1455 },
1456 NetworkError::BlobURLStoreError(reason) |
1457 NetworkError::WebsocketConnectionFailure(reason) |
1458 NetworkError::HttpError(reason) |
1459 NetworkError::ResourceLoadError(reason) |
1460 NetworkError::MimeType(reason) => {
1461 let page = resources::read_string(Resource::NetErrorHTML);
1462 page.replace("${reason}", &reason)
1463 },
1464 NetworkError::Crash(details) => {
1465 let page = resources::read_string(Resource::CrashHTML);
1466 page.replace("${details}", &details)
1467 },
1468 NetworkError::UnsupportedScheme |
1469 NetworkError::CorsGeneral |
1470 NetworkError::CrossOriginResponse |
1471 NetworkError::CorsCredentials |
1472 NetworkError::CorsAllowMethods |
1473 NetworkError::CorsAllowHeaders |
1474 NetworkError::CorsMethod |
1475 NetworkError::CorsAuthorization |
1476 NetworkError::CorsHeaders |
1477 NetworkError::ConnectionFailure |
1478 NetworkError::RedirectError |
1479 NetworkError::TooManyRedirects |
1480 NetworkError::TooManyInFlightKeepAliveRequests |
1481 NetworkError::InvalidMethod |
1482 NetworkError::ContentSecurityPolicy |
1483 NetworkError::Nosniff |
1484 NetworkError::SubresourceIntegrity |
1485 NetworkError::MixedContent |
1486 NetworkError::CacheError |
1487 NetworkError::InvalidPort |
1488 NetworkError::LocalDirectoryError |
1489 NetworkError::PartialResponseToNonRangeRequestError |
1490 NetworkError::ProtocolHandlerSubstitutionError |
1491 NetworkError::DecompressionError => {
1492 let page = resources::read_string(Resource::NetErrorHTML);
1493 page.replace("${reason}", &format!("{:?}", error))
1494 },
1495 NetworkError::LoadCancelled => {
1496 return;
1498 },
1499 };
1500 self.load_inline_unknown_content(&parser, page, cx);
1501 }
1502 }
1503
1504 fn process_response_chunk(
1505 &mut self,
1506 cx: &mut js::context::JSContext,
1507 _: RequestId,
1508 payload: Vec<u8>,
1509 ) {
1510 if self.is_synthesized_document {
1511 return;
1512 }
1513 let Some(parser) = self.parser.as_ref().map(|p| p.root()) else {
1514 return;
1515 };
1516 if parser.aborted.get() {
1517 return;
1518 }
1519 if !self.has_loaded_document {
1520 self.navigation_params
1522 .resource_header
1523 .extend_from_slice(&payload);
1524 if self.navigation_params.resource_header.len() >= 1445 {
1526 self.load_document(cx);
1527 }
1528 } else {
1529 parser.parse_bytes_chunk(payload, cx);
1530 }
1531 }
1532
1533 fn process_response_eof(
1537 mut self,
1538 cx: &mut js::context::JSContext,
1539 _: RequestId,
1540 status: Result<(), NetworkError>,
1541 timing: ResourceFetchTiming,
1542 ) {
1543 let parser = match self.parser.as_ref() {
1544 Some(parser) => parser.root(),
1545 None => return,
1546 };
1547 if parser.aborted.get() || self.is_synthesized_document {
1548 return;
1549 }
1550
1551 if let Err(error) = &status {
1552 debug!("Failed to load page URL {}, error: {error:?}", self.url);
1554 }
1555
1556 if !self.has_loaded_document {
1560 self.load_document(cx);
1561 }
1562
1563 let mut realm = enter_auto_realm(cx, &*parser);
1564 let cx = &mut realm;
1565
1566 if status.is_ok() {
1567 parser.document.set_resource_fetch_timing(timing);
1568 }
1569
1570 parser.last_chunk_received.set(true);
1571 if !parser.suspended.get() {
1572 parser.parse_sync(cx);
1573 }
1574
1575 if let Some(pushed_index) = self.pushed_entry_index {
1578 let document = &parser.document;
1579 let performance_entry = PerformanceNavigationTiming::new(
1580 &document.global(),
1581 CrossProcessInstant::now(),
1582 document,
1583 CanGc::from_cx(cx),
1584 );
1585 document
1586 .global()
1587 .performance()
1588 .update_entry(pushed_index, performance_entry.upcast::<PerformanceEntry>());
1589 }
1590 }
1591
1592 fn process_csp_violations(&mut self, _: RequestId, _: Vec<Violation>) {
1593 unreachable!("Script_thread should handle reporting violations for parser contexts");
1594 }
1595}
1596
1597pub(crate) struct FragmentContext<'a> {
1598 pub(crate) context_elem: &'a Node,
1599 pub(crate) form_elem: Option<&'a Node>,
1600 pub(crate) context_element_allows_scripting: bool,
1601}
1602
1603#[cfg_attr(crown, expect(crown::unrooted_must_root))]
1604fn insert(
1605 cx: &mut js::context::JSContext,
1606 parent: &Node,
1607 reference_child: Option<&Node>,
1608 child: NodeOrText<Dom<Node>>,
1609 parsing_algorithm: ParsingAlgorithm,
1610 custom_element_reaction_stack: &CustomElementReactionStack,
1611) {
1612 match child {
1613 NodeOrText::AppendNode(n) => {
1614 let element_in_non_fragment =
1618 parsing_algorithm != ParsingAlgorithm::Fragment && n.is::<Element>();
1619 if element_in_non_fragment {
1620 custom_element_reaction_stack.push_new_element_queue();
1621 }
1622 parent.InsertBefore(cx, &n, reference_child).unwrap();
1623 if element_in_non_fragment {
1624 custom_element_reaction_stack.pop_current_element_queue(cx);
1625 }
1626 },
1627 NodeOrText::AppendText(t) => {
1628 let text = reference_child
1630 .and_then(Node::GetPreviousSibling)
1631 .or_else(|| parent.GetLastChild())
1632 .and_then(DomRoot::downcast::<Text>);
1633
1634 if let Some(text) = text {
1635 text.upcast::<CharacterData>().append_data(&t);
1636 } else {
1637 let text = Text::new(cx, String::from(t).into(), &parent.owner_doc());
1638 parent
1639 .InsertBefore(cx, text.upcast(), reference_child)
1640 .unwrap();
1641 }
1642 },
1643 }
1644}
1645
1646#[derive(JSTraceable, MallocSizeOf)]
1647#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1648pub(crate) struct Sink {
1649 #[no_trace]
1650 base_url: ServoUrl,
1651 document: Dom<Document>,
1652 current_line: Cell<u64>,
1653 script: MutNullableDom<HTMLScriptElement>,
1654 parsing_algorithm: ParsingAlgorithm,
1655 #[conditional_malloc_size_of]
1656 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
1657}
1658
1659impl Sink {
1660 fn same_tree(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1661 let x = x.downcast::<Element>().expect("Element node expected");
1662 let y = y.downcast::<Element>().expect("Element node expected");
1663
1664 x.is_in_same_home_subtree(y)
1665 }
1666
1667 fn has_parent_node(&self, node: &Dom<Node>) -> bool {
1668 node.GetParentNode().is_some()
1669 }
1670}
1671
1672impl TreeSink for Sink {
1673 type Output = Self;
1674
1675 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1676 fn finish(self) -> Self {
1677 self
1678 }
1679
1680 type Handle = Dom<Node>;
1681 type ElemName<'a>
1682 = ExpandedName<'a>
1683 where
1684 Self: 'a;
1685
1686 fn get_document(&self) -> Dom<Node> {
1687 Dom::from_ref(self.document.upcast())
1688 }
1689
1690 #[expect(unsafe_code)]
1691 fn get_template_contents(&self, target: &Dom<Node>) -> Dom<Node> {
1692 let mut cx = unsafe { temp_cx() };
1694 let cx = &mut cx;
1695 let template = target
1696 .downcast::<HTMLTemplateElement>()
1697 .expect("tried to get template contents of non-HTMLTemplateElement in HTML parsing");
1698 Dom::from_ref(template.Content(cx).upcast())
1699 }
1700
1701 fn same_node(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1702 x == y
1703 }
1704
1705 fn elem_name<'a>(&self, target: &'a Dom<Node>) -> ExpandedName<'a> {
1706 let elem = target
1707 .downcast::<Element>()
1708 .expect("tried to get name of non-Element in HTML parsing");
1709 ExpandedName {
1710 ns: elem.namespace(),
1711 local: elem.local_name(),
1712 }
1713 }
1714
1715 #[expect(unsafe_code)]
1716 fn create_element(
1717 &self,
1718 name: QualName,
1719 attrs: Vec<Attribute>,
1720 flags: ElementFlags,
1721 ) -> Dom<Node> {
1722 let mut cx = unsafe { temp_cx() };
1724 let cx = &mut cx;
1725 let attrs = attrs
1726 .into_iter()
1727 .map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
1728 .collect();
1729 let parsing_algorithm = if flags.template {
1730 ParsingAlgorithm::Fragment
1731 } else {
1732 self.parsing_algorithm
1733 };
1734 let element = create_element_for_token(
1735 name,
1736 attrs,
1737 &self.document,
1738 ElementCreator::ParserCreated(self.current_line.get()),
1739 parsing_algorithm,
1740 &self.custom_element_reaction_stack,
1741 flags.had_duplicate_attributes,
1742 cx,
1743 );
1744 Dom::from_ref(element.upcast())
1745 }
1746
1747 #[expect(unsafe_code)]
1748 fn create_comment(&self, text: StrTendril) -> Dom<Node> {
1749 let mut cx = unsafe { temp_cx() };
1751 let cx = &mut cx;
1752 let comment = Comment::new(
1753 cx,
1754 DOMString::from(String::from(text)),
1755 &self.document,
1756 None,
1757 );
1758 Dom::from_ref(comment.upcast())
1759 }
1760
1761 #[expect(unsafe_code)]
1762 fn create_pi(&self, target: StrTendril, data: StrTendril) -> Dom<Node> {
1763 let mut cx = unsafe { temp_cx() };
1765 let cx = &mut cx;
1766 let doc = &*self.document;
1767 let pi = ProcessingInstruction::new(
1768 cx,
1769 DOMString::from(String::from(target)),
1770 DOMString::from(String::from(data)),
1771 doc,
1772 );
1773 Dom::from_ref(pi.upcast())
1774 }
1775
1776 fn associate_with_form(
1777 &self,
1778 target: &Dom<Node>,
1779 form: &Dom<Node>,
1780 nodes: (&Dom<Node>, Option<&Dom<Node>>),
1781 ) {
1782 let (element, prev_element) = nodes;
1783 let tree_node = prev_element.map_or(element, |prev| {
1784 if self.has_parent_node(element) {
1785 element
1786 } else {
1787 prev
1788 }
1789 });
1790 if !self.same_tree(tree_node, form) {
1791 return;
1792 }
1793
1794 let node = target;
1795 let form = DomRoot::downcast::<HTMLFormElement>(DomRoot::from_ref(&**form))
1796 .expect("Owner must be a form element");
1797
1798 let elem = node.downcast::<Element>();
1799 let control = elem.and_then(|e| e.as_maybe_form_control());
1800
1801 if let Some(control) = control {
1802 control.set_form_owner_from_parser(&form, CanGc::deprecated_note());
1803 }
1804 }
1805
1806 #[expect(unsafe_code)]
1807 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1808 fn append_before_sibling(&self, sibling: &Dom<Node>, new_node: NodeOrText<Dom<Node>>) {
1809 let mut cx = unsafe { temp_cx() };
1811 let cx = &mut cx;
1812
1813 let parent = sibling
1814 .GetParentNode()
1815 .expect("append_before_sibling called on node without parent");
1816
1817 insert(
1818 cx,
1819 &parent,
1820 Some(sibling),
1821 new_node,
1822 self.parsing_algorithm,
1823 &self.custom_element_reaction_stack,
1824 );
1825 }
1826
1827 fn parse_error(&self, msg: Cow<'static, str>) {
1828 debug!("Parse error: {}", msg);
1829 }
1830
1831 fn set_quirks_mode(&self, mode: QuirksMode) {
1832 let mode = match mode {
1833 QuirksMode::Quirks => ServoQuirksMode::Quirks,
1834 QuirksMode::LimitedQuirks => ServoQuirksMode::LimitedQuirks,
1835 QuirksMode::NoQuirks => ServoQuirksMode::NoQuirks,
1836 };
1837 self.document.set_quirks_mode(mode);
1838 }
1839
1840 #[expect(unsafe_code)]
1841 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1842 fn append(&self, parent: &Dom<Node>, child: NodeOrText<Dom<Node>>) {
1843 let mut cx = unsafe { temp_cx() };
1845 let cx = &mut cx;
1846
1847 insert(
1848 cx,
1849 parent,
1850 None,
1851 child,
1852 self.parsing_algorithm,
1853 &self.custom_element_reaction_stack,
1854 );
1855 }
1856
1857 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1858 fn append_based_on_parent_node(
1859 &self,
1860 elem: &Dom<Node>,
1861 prev_elem: &Dom<Node>,
1862 child: NodeOrText<Dom<Node>>,
1863 ) {
1864 if self.has_parent_node(elem) {
1865 self.append_before_sibling(elem, child);
1866 } else {
1867 self.append(prev_elem, child);
1868 }
1869 }
1870
1871 #[expect(unsafe_code)]
1872 fn append_doctype_to_document(
1873 &self,
1874 name: StrTendril,
1875 public_id: StrTendril,
1876 system_id: StrTendril,
1877 ) {
1878 let mut cx = unsafe { temp_cx() };
1880 let cx = &mut cx;
1881
1882 let doc = &*self.document;
1883 let doctype = DocumentType::new(
1884 cx,
1885 DOMString::from(String::from(name)),
1886 Some(DOMString::from(String::from(public_id))),
1887 Some(DOMString::from(String::from(system_id))),
1888 doc,
1889 );
1890 doc.upcast::<Node>()
1891 .AppendChild(cx, doctype.upcast())
1892 .expect("Appending failed");
1893 }
1894
1895 #[expect(unsafe_code)]
1896 fn add_attrs_if_missing(&self, target: &Dom<Node>, attrs: Vec<Attribute>) {
1897 let mut cx = unsafe { temp_cx() };
1899 let cx = &mut cx;
1900
1901 let elem = target
1902 .downcast::<Element>()
1903 .expect("tried to set attrs on non-Element in HTML parsing");
1904 for attr in attrs {
1905 elem.set_attribute_from_parser(
1906 cx,
1907 attr.name,
1908 DOMString::from(String::from(attr.value)),
1909 None,
1910 );
1911 }
1912 }
1913
1914 #[expect(unsafe_code)]
1915 fn remove_from_parent(&self, target: &Dom<Node>) {
1916 let mut cx = unsafe { temp_cx() };
1918 let cx = &mut cx;
1919
1920 if let Some(ref parent) = target.GetParentNode() {
1921 parent.RemoveChild(cx, target).unwrap();
1922 }
1923 }
1924
1925 fn mark_script_already_started(&self, node: &Dom<Node>) {
1926 let script = node.downcast::<HTMLScriptElement>();
1927 if let Some(script) = script {
1928 script.set_already_started(true)
1929 }
1930 }
1931
1932 #[expect(unsafe_code)]
1933 fn reparent_children(&self, node: &Dom<Node>, new_parent: &Dom<Node>) {
1934 let mut cx = unsafe { temp_cx() };
1936 let cx = &mut cx;
1937
1938 while let Some(ref child) = node.GetFirstChild() {
1939 new_parent.AppendChild(cx, child).unwrap();
1940 }
1941 }
1942
1943 fn is_mathml_annotation_xml_integration_point(&self, handle: &Dom<Node>) -> bool {
1946 let elem = handle.downcast::<Element>().unwrap();
1947 elem.get_attribute(&local_name!("encoding"))
1948 .is_some_and(|attr| {
1949 attr.value().eq_ignore_ascii_case("text/html") ||
1950 attr.value().eq_ignore_ascii_case("application/xhtml+xml")
1951 })
1952 }
1953
1954 fn set_current_line(&self, line_number: u64) {
1955 self.current_line.set(line_number);
1956 }
1957
1958 #[expect(unsafe_code)]
1959 fn pop(&self, node: &Dom<Node>) {
1960 let mut cx = unsafe { temp_cx() };
1962 let cx = &mut cx;
1963
1964 let node = DomRoot::from_ref(&**node);
1965 vtable_for(&node).pop(cx);
1966 }
1967
1968 fn allow_declarative_shadow_roots(&self, intended_parent: &Dom<Node>) -> bool {
1969 intended_parent.owner_doc().allow_declarative_shadow_roots()
1970 }
1971
1972 #[expect(unsafe_code)]
1976 fn attach_declarative_shadow(
1977 &self,
1978 host: &Dom<Node>,
1979 template: &Dom<Node>,
1980 attributes: &[Attribute],
1981 ) -> bool {
1982 let mut cx = unsafe { temp_cx() };
1984 let cx = &mut cx;
1985
1986 attach_declarative_shadow_inner(cx, host, template, attributes)
1987 }
1988
1989 #[expect(unsafe_code)]
1990 fn maybe_clone_an_option_into_selectedcontent(&self, option: &Self::Handle) {
1991 let mut cx = unsafe { temp_cx() };
1993 let cx = &mut cx;
1994
1995 let Some(option) = option.downcast::<HTMLOptionElement>() else {
1996 if cfg!(debug_assertions) {
1997 unreachable!();
1998 }
1999 log::error!(
2000 "Received non-option element in maybe_clone_an_option_into_selectedcontent"
2001 );
2002 return;
2003 };
2004
2005 option.maybe_clone_an_option_into_selectedcontent(cx)
2006 }
2007}
2008
2009#[expect(clippy::too_many_arguments)]
2011fn create_element_for_token(
2012 name: QualName,
2013 attrs: Vec<ElementAttribute>,
2014 document: &Document,
2015 creator: ElementCreator,
2016 parsing_algorithm: ParsingAlgorithm,
2017 custom_element_reaction_stack: &CustomElementReactionStack,
2018 had_duplicate_attributes: bool,
2019 cx: &mut js::context::JSContext,
2020) -> DomRoot<Element> {
2021 let is = attrs
2039 .iter()
2040 .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
2041 .map(|attr| LocalName::from(&attr.value));
2042
2043 let definition = document.lookup_custom_element_definition(&name.ns, &name.local, is.as_ref());
2049
2050 let will_execute_script =
2053 definition.is_some() && parsing_algorithm != ParsingAlgorithm::Fragment;
2054
2055 if will_execute_script {
2057 document.increment_throw_on_dynamic_markup_insertion_counter();
2059 if is_execution_stack_empty() {
2062 document.window().perform_a_microtask_checkpoint(cx);
2063 }
2064 custom_element_reaction_stack.push_new_element_queue()
2067 }
2068
2069 let creation_mode = if will_execute_script {
2072 CustomElementCreationMode::Synchronous
2073 } else {
2074 CustomElementCreationMode::Asynchronous
2075 };
2076 let element = Element::create(cx, name, is, document, creator, creation_mode, None);
2077
2078 for attr in attrs {
2080 element.set_attribute_from_parser(cx, attr.name, attr.value, None);
2081 }
2082
2083 if had_duplicate_attributes {
2086 element.set_had_duplicate_attributes();
2087 }
2088
2089 if will_execute_script {
2091 custom_element_reaction_stack.pop_current_element_queue(cx);
2096 document.decrement_throw_on_dynamic_markup_insertion_counter();
2098 }
2099
2100 if let Some(html_element) = element.downcast::<HTMLElement>() &&
2110 element.is_resettable() &&
2111 !html_element.is_form_associated_custom_element()
2112 {
2113 element.reset(cx);
2114 }
2115
2116 element
2126}
2127
2128fn attach_declarative_shadow_inner(
2129 cx: &mut js::context::JSContext,
2130 host: &Node,
2131 template: &Node,
2132 attributes: &[Attribute],
2133) -> bool {
2134 let host_element = host.downcast::<Element>().unwrap();
2135
2136 if host_element.shadow_root().is_some() {
2137 return false;
2138 }
2139
2140 let template_element = template.downcast::<HTMLTemplateElement>().unwrap();
2141
2142 let mut shadow_root_mode = ShadowRootMode::Open;
2152 let mut slot_assignment_mode = SlotAssignmentMode::Named;
2153 let mut clonable = false;
2154 let mut delegatesfocus = false;
2155 let mut serializable = false;
2156
2157 attributes
2158 .iter()
2159 .for_each(|attr: &Attribute| match attr.name.local {
2160 local_name!("shadowrootmode") => {
2161 if attr.value.eq_ignore_ascii_case("open") {
2162 shadow_root_mode = ShadowRootMode::Open;
2163 } else if attr.value.eq_ignore_ascii_case("closed") {
2164 shadow_root_mode = ShadowRootMode::Closed;
2165 } else {
2166 unreachable!("shadowrootmode value is not open nor closed");
2167 }
2168 },
2169 local_name!("shadowrootclonable") => {
2170 clonable = true;
2171 },
2172 local_name!("shadowrootdelegatesfocus") => {
2173 delegatesfocus = true;
2174 },
2175 local_name!("shadowrootserializable") => {
2176 serializable = true;
2177 },
2178 local_name!("shadowrootslotassignment") => {
2179 if attr.value.eq_ignore_ascii_case("manual") {
2180 slot_assignment_mode = SlotAssignmentMode::Manual;
2181 }
2182 },
2183 _ => {},
2184 });
2185
2186 match host_element.attach_shadow(
2189 cx,
2190 IsUserAgentWidget::No,
2191 shadow_root_mode,
2192 clonable,
2193 serializable,
2194 delegatesfocus,
2195 slot_assignment_mode,
2196 ) {
2197 Ok(shadow_root) => {
2198 shadow_root.set_declarative(true);
2200
2201 let shadow = shadow_root.upcast::<DocumentFragment>();
2203 template_element.set_contents(Some(shadow));
2204
2205 shadow_root.set_available_to_element_internals(true);
2207
2208 true
2209 },
2210 Err(_) => false,
2211 }
2212}