1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::borrow::Cow;
8use std::cell::{Cell, RefCell};
9use std::mem;
10use std::rc::Rc;
11use std::sync::Arc;
12
13use base64::Engine as _;
14use base64::engine::general_purpose;
15use bytes::Bytes;
16use content_security_policy::sandboxing_directive::SandboxingFlagSet;
17#[cfg(feature = "devtools")]
18use devtools_traits::ScriptToDevtoolsControlMsg;
19use dom_struct::dom_struct;
20use embedder_traits::resources::{self, Resource};
21use encoding_rs::{Encoding, UTF_8};
22use html5ever::buffer_queue::BufferQueue;
23use html5ever::tendril::StrTendril;
24use html5ever::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeSink};
25use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, ns};
26use hyper_serde::Serde;
27use js::context::JSContext;
28use markup5ever::TokenizerResult;
29use mime::{self, Mime};
30use net_traits::mime_classifier::{ApacheBugFlag, MediaType, MimeClassifier, NoSniffFlag};
31use net_traits::policy_container::PolicyContainer;
32use net_traits::{
33 FetchMetadata, LoadContext, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
34};
35use profile_traits::time::{
36 ProfilerCategory, ProfilerChan, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
37};
38use profile_traits::time_profile;
39use script_bindings::cell::DomRefCell;
40use script_bindings::reflector::{Reflector, reflect_dom_object};
41use script_bindings::script_runtime::temp_cx;
42use script_traits::DocumentActivity;
43use servo_base::id::{PipelineId, WebViewId};
44use servo_config::pref;
45use servo_constellation_traits::TargetSnapshotParams;
46use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
47use style::context::QuirksMode as ServoQuirksMode;
48use tendril::stream::LossyDecoder;
49use tendril::{ByteTendril, TendrilSink};
50
51use crate::dom::SuppressObserver;
52use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
53 DocumentMethods, DocumentReadyState,
54};
55use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
56use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods;
57use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
58use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
59use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
60 ShadowRootMode, SlotAssignmentMode,
61};
62use crate::dom::bindings::inheritance::Castable;
63use crate::dom::bindings::refcounted::Trusted;
64use crate::dom::bindings::reflector::DomGlobal;
65use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
66use crate::dom::bindings::settings_stack::is_execution_stack_empty;
67use crate::dom::bindings::str::{DOMString, USVString};
68use crate::dom::characterdata::CharacterData;
69use crate::dom::comment::Comment;
70use crate::dom::csp::parse_csp_list_from_metadata;
71use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
72use crate::dom::document::{
73 AbortReason, Document, HasBrowsingContext, IsHTMLDocument, SetParserReason,
74};
75use crate::dom::documentfragment::DocumentFragment;
76use crate::dom::documenttype::DocumentType;
77use crate::dom::domstringlist::DOMStringList;
78use crate::dom::element::create::create_element;
79use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
80use crate::dom::globalscope::GlobalScope;
81use crate::dom::html::document_metadata::processingoptions::{
82 LinkHeader, LinkProcessingPhase, extract_links_from_headers, process_link_headers,
83};
84use crate::dom::html::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
85use crate::dom::html::htmlimageelement::HTMLImageElement;
86use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
87use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
88use crate::dom::iterators::ShadowIncluding;
89use crate::dom::node::Node;
90use crate::dom::node::virtualmethods::vtable_for;
91use crate::dom::performance::performanceentry::PerformanceEntry;
92use crate::dom::performance::performancenavigationtiming::PerformanceNavigationTiming;
93use crate::dom::processinginstruction::ProcessingInstruction;
94use crate::dom::reporting::reportingendpoint::ReportingEndpoint;
95use crate::dom::security::csp::CspReporting;
96use crate::dom::security::xframeoptions::check_a_navigation_response_adherence_to_x_frame_options;
97use crate::dom::shadowroot::IsUserAgentWidget;
98use crate::dom::text::Text;
99use crate::dom::types::{HTMLElement, HTMLMediaElement, HTMLOptionElement};
100use crate::event_loop::document_loader::{DocumentLoader, LoadType};
101use crate::event_loop::script_thread::ScriptThread;
102use crate::navigation::determine_the_origin;
103use crate::realms::enter_auto_realm;
104use crate::runtime::script_runtime::IntroductionType;
105
106mod async_html;
107pub(crate) mod encoding;
108pub(crate) mod html;
109mod prefetch;
110mod xml;
111
112use encoding::{NetworkDecoderState, NetworkSink};
113pub(crate) use html::serialize_html_fragment;
114
115#[dom_struct]
116pub(crate) struct ServoParser {
129 reflector: Reflector,
130 document: Dom<Document>,
132 network_decoder: DomRefCell<NetworkDecoderState>,
134 #[ignore_malloc_size_of = "Defined in html5ever"]
136 #[no_trace]
137 network_input: BufferQueue,
138 #[ignore_malloc_size_of = "Defined in html5ever"]
140 #[no_trace]
141 script_input: BufferQueue,
142 tokenizer: Tokenizer,
144 last_chunk_received: Cell<bool>,
146 suspended: Cell<bool>,
148 script_nesting_level: Cell<usize>,
150 aborted: Cell<bool>,
152 stopped: Cell<bool>,
154 script_created_parser: bool,
156 #[no_trace]
161 prefetch_decoder: RefCell<LossyDecoder<NetworkSink>>,
162 prefetch_tokenizer: prefetch::Tokenizer,
166 #[ignore_malloc_size_of = "Defined in html5ever"]
167 #[no_trace]
168 prefetch_input: BufferQueue,
169 #[cfg(feature = "devtools")]
172 content_for_devtools: Option<DomRefCell<String>>,
173}
174
175pub(crate) struct ElementAttribute {
176 name: QualName,
177 value: DOMString,
178}
179
180#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
181pub(crate) enum ParsingAlgorithm {
182 Normal,
183 Fragment,
184}
185
186impl ElementAttribute {
187 pub(crate) fn new(name: QualName, value: DOMString) -> ElementAttribute {
188 ElementAttribute { name, value }
189 }
190}
191
192impl ServoParser {
193 pub(crate) fn parse_html_document(
195 cx: &mut JSContext,
196 document: &Document,
197 input: Option<DOMString>,
198 url: ServoUrl,
199 encoding_hint_from_content_type: Option<&'static Encoding>,
200 encoding_of_container_document: Option<&'static Encoding>,
201 ) {
202 assert!(document.is_html_document());
206
207 let parser = ServoParser::new(
209 cx,
210 document,
211 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
212 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
213 } else {
214 Tokenizer::Html(self::html::Tokenizer::new(
215 document,
216 url,
217 None,
218 ParsingAlgorithm::Normal,
219 ))
220 },
221 ParserKind::Normal,
222 encoding_hint_from_content_type,
223 encoding_of_container_document,
224 );
225
226 if let Some(input) = input {
232 parser.parse_complete_string_chunk(
233 cx,
234 String::from(input),
235 SetParserReason::ParsingHtmlDocument,
236 );
237 } else {
238 parser
239 .document
240 .set_current_parser(Some(&parser), SetParserReason::ParsingHtmlDocument);
241 }
242 }
243
244 pub(crate) fn parse_html_fragment<'el>(
246 cx: &mut JSContext,
247 context: &'el Element,
248 input: DOMString,
249 allow_declarative_shadow_roots: bool,
250 ) -> impl Iterator<Item = DomRoot<Node>> + use<'el> {
251 let context_node = context.upcast::<Node>();
252 let context_document = context_node.owner_doc();
253 let window = context_document.window();
254 let url = context_document.url();
255
256 let loader = DocumentLoader::new_with_threads(
258 context_document.loader().resource_threads().clone(),
259 Some(url.clone()),
260 );
261 let document = Document::new(
262 cx,
263 window,
264 HasBrowsingContext::No,
265 Some(url.clone()),
266 context_document.about_base_url(),
267 context_document.origin().clone(),
268 IsHTMLDocument::HTMLDocument,
269 None,
270 None,
271 DocumentActivity::Inactive,
272 loader,
273 None,
274 None,
275 false,
276 allow_declarative_shadow_roots,
277 Some(context_document.insecure_requests_policy()),
278 context_document.has_trustworthy_ancestor_or_current_origin(),
279 context_document.custom_element_reaction_stack(),
280 context_document.creation_sandboxing_flag_set(),
281 context_document.pipeline_id(),
282 context_document.image_cache(),
283 );
284
285 document.set_quirks_mode(context_document.quirks_mode());
289
290 let form = context_node
297 .inclusive_ancestors(ShadowIncluding::No)
298 .find(|element| element.is::<HTMLFormElement>());
299
300 let fragment_context = FragmentContext {
301 context_elem: context_node,
302 form_elem: form.as_deref(),
303 context_element_allows_scripting: context_document.scripting_enabled(),
304 };
305
306 let parser = ServoParser::new(
307 cx,
308 &document,
309 Tokenizer::Html(self::html::Tokenizer::new(
310 &document,
311 url,
312 Some(fragment_context),
313 ParsingAlgorithm::Fragment,
314 )),
315 ParserKind::Normal,
316 None,
317 None,
318 );
319 parser.parse_complete_string_chunk(
320 cx,
321 String::from(input),
322 SetParserReason::ParsingHtmlFragment,
323 );
324
325 let root_element = document.GetDocumentElement().expect("no document element");
327 FragmentParsingResult {
328 inner: root_element.upcast::<Node>().children(),
329 }
330 }
331
332 pub(crate) fn parse_html_script_input(cx: &mut JSContext, document: &Document, url: ServoUrl) {
333 let parser = ServoParser::new(
334 cx,
335 document,
336 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
337 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
338 } else {
339 Tokenizer::Html(self::html::Tokenizer::new(
340 document,
341 url,
342 None,
343 ParsingAlgorithm::Normal,
344 ))
345 },
346 ParserKind::ScriptCreated,
347 None,
348 None,
349 );
350 document.set_current_parser(Some(&parser), SetParserReason::ParsingHtmlScriptInput);
351 }
352
353 pub(crate) fn parse_xml_document(
354 cx: &mut JSContext,
355 document: &Document,
356 input: Option<DOMString>,
357 url: ServoUrl,
358 encoding_hint_from_content_type: Option<&'static Encoding>,
359 ) {
360 let parser = ServoParser::new(
361 cx,
362 document,
363 Tokenizer::Xml(self::xml::Tokenizer::new(document, url)),
364 ParserKind::Normal,
365 encoding_hint_from_content_type,
366 None,
367 );
368
369 if let Some(input) = input {
371 parser.parse_complete_string_chunk(
372 cx,
373 String::from(input),
374 SetParserReason::ParsingXmlDocument,
375 );
376 } else {
377 parser
378 .document
379 .set_current_parser(Some(&parser), SetParserReason::ParsingXmlDocument);
380 }
381 }
382
383 pub(crate) fn script_nesting_level(&self) -> usize {
384 self.script_nesting_level.get()
385 }
386
387 pub(crate) fn is_script_created(&self) -> bool {
388 self.script_created_parser
389 }
390
391 pub(crate) fn resume_with_pending_parsing_blocking_script(
406 &self,
407 cx: &mut JSContext,
408 script: &HTMLScriptElement,
409 result: ScriptResult,
410 ) {
411 assert!(self.suspended.get());
412 self.suspended.set(false);
413
414 self.script_input.swap_with(&self.network_input);
415 while let Some(chunk) = self.script_input.pop_front() {
416 self.network_input.push_back(chunk);
417 }
418
419 let script_nesting_level = self.script_nesting_level.get();
420 assert_eq!(script_nesting_level, 0);
421
422 self.script_nesting_level.set(script_nesting_level + 1);
423 script.execute(cx, result);
424 self.script_nesting_level.set(script_nesting_level);
425
426 if !self.suspended.get() && !self.aborted.get() {
427 self.parse_sync(cx);
428 }
429 }
430
431 pub(crate) fn can_write(&self) -> bool {
432 self.script_created_parser || self.script_nesting_level.get() > 0
433 }
434
435 pub(crate) fn write(&self, cx: &mut JSContext, text: DOMString) {
437 assert!(self.can_write());
438
439 if self.document.has_pending_parsing_blocking_script() {
440 self.script_input.push_back(String::from(text).into());
444 return;
445 }
446
447 assert!(self.script_input.is_empty());
451
452 let input = BufferQueue::default();
453 input.push_back(String::from(text).into());
454
455 let profiler_chan = self
456 .document
457 .window()
458 .as_global_scope()
459 .time_profiler_chan()
460 .clone();
461 let profiler_metadata = TimerMetadata {
462 url: self.document.url().as_str().into(),
463 iframe: TimerMetadataFrameType::RootWindow,
464 incremental: TimerMetadataReflowType::FirstReflow,
465 };
466 self.tokenize(cx, |cx, tokenizer| {
467 tokenizer.feed(cx, &input, profiler_chan.clone(), profiler_metadata.clone())
468 });
469
470 if self.suspended.get() {
471 while let Some(chunk) = input.pop_front() {
475 self.script_input.push_back(chunk);
476 }
477 return;
478 }
479
480 assert!(input.is_empty());
481 }
482
483 pub(crate) fn close(&self, cx: &mut JSContext) {
485 assert!(self.script_created_parser);
486
487 self.last_chunk_received.set(true);
489
490 if self.suspended.get() {
492 return;
493 }
494
495 self.parse_sync(cx);
498 }
499
500 pub(crate) fn abort(&self, cx: &mut JSContext, reason: AbortReason) {
502 assert!(!self.aborted.get());
503 self.aborted.set(true);
504
505 self.script_input.replace_with(BufferQueue::default());
507 self.network_input.replace_with(BufferQueue::default());
508
509 self.document
511 .update_the_current_document_readiness(cx, DocumentReadyState::Interactive);
512
513 self.tokenizer.end(cx);
515 self.document
516 .set_current_parser(None, SetParserReason::Abort(reason));
517
518 self.document
520 .update_the_current_document_readiness(cx, DocumentReadyState::Complete);
521 }
522
523 pub(crate) fn get_current_line(&self) -> u32 {
524 self.tokenizer.get_current_line()
525 }
526
527 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
528 fn new_inherited(
529 document: &Document,
530 tokenizer: Tokenizer,
531 kind: ParserKind,
532 encoding_hint_from_content_type: Option<&'static Encoding>,
533 encoding_of_container_document: Option<&'static Encoding>,
534 ) -> Self {
535 #[cfg(feature = "devtools")]
539 let content_for_devtools = (document.global().devtools_chan().is_some() &&
540 document.has_browsing_context())
541 .then_some(DomRefCell::new(String::new()));
542
543 ServoParser {
544 reflector: Reflector::new(),
545 document: Dom::from_ref(document),
546 network_decoder: DomRefCell::new(NetworkDecoderState::new(
547 encoding_hint_from_content_type,
548 encoding_of_container_document,
549 )),
550 network_input: BufferQueue::default(),
551 script_input: BufferQueue::default(),
552 tokenizer,
553 last_chunk_received: Cell::new(false),
554 suspended: Default::default(),
555 script_nesting_level: Default::default(),
556 aborted: Default::default(),
557 stopped: Default::default(),
558 script_created_parser: kind == ParserKind::ScriptCreated,
559 prefetch_decoder: RefCell::new(LossyDecoder::new_encoding_rs(
560 encoding_hint_from_content_type.unwrap_or(UTF_8),
561 Default::default(),
562 )),
563 prefetch_tokenizer: prefetch::Tokenizer::new(document),
564 prefetch_input: BufferQueue::default(),
565 #[cfg(feature = "devtools")]
566 content_for_devtools,
567 }
568 }
569
570 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
571 fn new(
572 cx: &mut JSContext,
573 document: &Document,
574 tokenizer: Tokenizer,
575 kind: ParserKind,
576 encoding_hint_from_content_type: Option<&'static Encoding>,
577 encoding_of_container_document: Option<&'static Encoding>,
578 ) -> DomRoot<Self> {
579 reflect_dom_object(
580 cx,
581 Box::new(ServoParser::new_inherited(
582 document,
583 tokenizer,
584 kind,
585 encoding_hint_from_content_type,
586 encoding_of_container_document,
587 )),
588 document.window(),
589 )
590 }
591
592 fn push_tendril_input_chunk(&self, chunk: StrTendril) {
593 #[cfg(feature = "devtools")]
594 if let Some(mut content_for_devtools) = self
595 .content_for_devtools
596 .as_ref()
597 .map(|content| content.borrow_mut())
598 {
599 content_for_devtools.push_str(chunk.as_ref());
601 }
602
603 if chunk.is_empty() {
604 return;
605 }
606
607 self.network_input.push_back(chunk);
610 }
611
612 fn push_bytes_input_chunk(&self, chunk: &[u8]) {
613 if let Some(decoded_chunk) = self
615 .network_decoder
616 .borrow_mut()
617 .push(chunk, &self.document)
618 {
619 self.push_tendril_input_chunk(decoded_chunk);
620 }
621
622 if self.should_prefetch() {
623 let mut prefetch_decoder = self.prefetch_decoder.borrow_mut();
629 prefetch_decoder.process(ByteTendril::from(chunk));
630
631 self.prefetch_input
632 .push_back(mem::take(&mut prefetch_decoder.inner_sink_mut().output));
633 self.prefetch_tokenizer.feed(&self.prefetch_input);
634 }
635 }
636
637 fn should_prefetch(&self) -> bool {
638 self.document.browsing_context().is_some()
646 }
647
648 fn push_string_input_chunk(&self, chunk: String) {
649 let chunk = StrTendril::from(chunk);
652 self.push_tendril_input_chunk(chunk);
653 }
654
655 fn parse_sync(&self, cx: &mut JSContext) {
656 assert!(self.script_input.is_empty());
657
658 if self.last_chunk_received.get() {
662 let chunk = self.network_decoder.borrow_mut().finish(&self.document);
663 if !chunk.is_empty() {
664 self.push_tendril_input_chunk(chunk);
665 }
666 }
667
668 if self.aborted.get() {
669 return;
670 }
671
672 let profiler_chan = self
673 .document
674 .window()
675 .as_global_scope()
676 .time_profiler_chan()
677 .clone();
678 let profiler_metadata = TimerMetadata {
679 url: self.document.url().as_str().into(),
680 iframe: TimerMetadataFrameType::RootWindow,
681 incremental: TimerMetadataReflowType::FirstReflow,
682 };
683 self.tokenize(cx, |cx, tokenizer| {
684 tokenizer.feed(
685 cx,
686 &self.network_input,
687 profiler_chan.clone(),
688 profiler_metadata.clone(),
689 )
690 });
691
692 if self.suspended.get() {
693 return;
694 }
695
696 assert!(self.network_input.is_empty());
697
698 if self.last_chunk_received.get() {
699 self.finish(cx);
700 }
701 }
702
703 fn parse_complete_string_chunk(
704 &self,
705 cx: &mut JSContext,
706 input: String,
707 reason: SetParserReason,
708 ) {
709 self.document.set_current_parser(Some(self), reason);
710 self.push_string_input_chunk(input);
711 self.last_chunk_received.set(true);
712 if !self.suspended.get() {
713 self.parse_sync(cx);
714 }
715 }
716
717 fn parse_bytes_chunk(&self, cx: &mut JSContext, input: &[u8]) {
718 let mut realm = enter_auto_realm(cx, &*self.document);
719 let cx = &mut realm.current_realm();
720 self.document
721 .set_current_parser(Some(self), SetParserReason::ParsingBytesChunk);
722 self.push_bytes_input_chunk(input.as_ref());
723 if !self.suspended.get() {
724 self.parse_sync(cx);
725 }
726 }
727
728 fn tokenize<F>(&self, cx: &mut JSContext, feed: F)
729 where
730 F: Fn(&mut JSContext, &Tokenizer) -> TokenizerResult<DomRoot<HTMLScriptElement>>,
731 {
732 loop {
733 assert!(!self.suspended.get());
734 assert!(!self.aborted.get());
735
736 self.document.window().reflow_if_reflow_timer_expired(cx);
737 let script = match feed(cx, &self.tokenizer) {
738 TokenizerResult::Done => return,
739 TokenizerResult::EncodingIndicator(_) => continue,
740 TokenizerResult::Script(script) => script,
741 };
742
743 if is_execution_stack_empty() {
750 self.document.window().perform_a_microtask_checkpoint(cx);
751 }
752
753 let script_nesting_level = self.script_nesting_level.get();
754
755 self.script_nesting_level.set(script_nesting_level + 1);
756 script.set_initial_script_text();
757 let introduction_type_override =
758 (script_nesting_level > 0).then_some(IntroductionType::INJECTED_SCRIPT);
759 script.prepare(cx, introduction_type_override);
760 self.script_nesting_level.set(script_nesting_level);
761
762 if self.document.has_pending_parsing_blocking_script() {
763 self.suspended.set(true);
764 return;
765 }
766 if self.aborted.get() {
767 return;
768 }
769 }
770 }
771
772 pub(crate) fn has_aborted(&self) -> bool {
774 self.aborted.get()
775 }
776
777 pub(crate) fn has_stopped(&self) -> bool {
779 self.stopped.get()
780 }
781
782 fn finish(&self, cx: &mut JSContext) {
784 assert!(!self.suspended.get());
785 assert!(self.last_chunk_received.get());
786 assert!(self.script_input.is_empty());
787 assert!(self.network_input.is_empty());
788 assert!(self.network_decoder.borrow().is_finished());
789
790 self.stopped.set(true);
791
792 self.tokenizer.end(cx);
797 self.document
799 .update_the_current_document_readiness(cx, DocumentReadyState::Interactive);
800 self.document
802 .set_current_parser(None, SetParserReason::FinishingParser);
803 self.document.start_the_end_loading_phase();
805 let url = self.tokenizer.url().clone();
806 self.document.finish_load(LoadType::PageSource(url), cx);
807
808 #[cfg(feature = "devtools")]
810 if let Some(content_for_devtools) = self
811 .content_for_devtools
812 .as_ref()
813 .map(|content| content.take())
814 {
815 let global = self.document.global();
816 let chan = global.devtools_chan().expect("Guaranteed by new");
817 let pipeline_id = self.document.global().pipeline_id();
818 let _ = chan.send(ScriptToDevtoolsControlMsg::UpdateSourceContent(
819 pipeline_id,
820 content_for_devtools,
821 ));
822 }
823 }
824}
825
826struct FragmentParsingResult<I>
827where
828 I: Iterator<Item = DomRoot<Node>>,
829{
830 inner: I,
831}
832
833impl<I> Iterator for FragmentParsingResult<I>
834where
835 I: Iterator<Item = DomRoot<Node>>,
836{
837 type Item = DomRoot<Node>;
838
839 #[expect(unsafe_code)]
840 fn next(&mut self) -> Option<DomRoot<Node>> {
841 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
842 let cx = &mut cx;
843
844 let next = self.inner.next()?;
845 next.remove_self(cx);
846 Some(next)
847 }
848
849 fn size_hint(&self) -> (usize, Option<usize>) {
850 self.inner.size_hint()
851 }
852}
853
854#[derive(JSTraceable, MallocSizeOf, PartialEq)]
855enum ParserKind {
856 Normal,
857 ScriptCreated,
858}
859
860#[derive(JSTraceable, MallocSizeOf)]
861#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
862enum Tokenizer {
863 Html(self::html::Tokenizer),
864 AsyncHtml(self::async_html::Tokenizer),
865 Xml(self::xml::Tokenizer),
866}
867
868impl Tokenizer {
869 fn feed(
870 &self,
871 cx: &mut JSContext,
872 input: &BufferQueue,
873 profiler_chan: ProfilerChan,
874 profiler_metadata: TimerMetadata,
875 ) -> TokenizerResult<DomRoot<HTMLScriptElement>> {
876 match *self {
877 Tokenizer::Html(ref tokenizer) => time_profile!(
878 ProfilerCategory::ScriptParseHTML,
879 Some(profiler_metadata),
880 profiler_chan,
881 || tokenizer.feed(input),
882 ),
883 Tokenizer::AsyncHtml(ref tokenizer) => time_profile!(
884 ProfilerCategory::ScriptParseHTML,
885 Some(profiler_metadata),
886 profiler_chan,
887 || tokenizer.feed(input, cx),
888 ),
889 Tokenizer::Xml(ref tokenizer) => time_profile!(
890 ProfilerCategory::ScriptParseXML,
891 Some(profiler_metadata),
892 profiler_chan,
893 || tokenizer.feed(input),
894 ),
895 }
896 }
897
898 fn end(&self, cx: &mut JSContext) {
899 match *self {
900 Tokenizer::Html(ref tokenizer) => tokenizer.end(),
901 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.end(cx),
902 Tokenizer::Xml(ref tokenizer) => tokenizer.end(),
903 }
904 }
905
906 fn url(&self) -> &ServoUrl {
907 match *self {
908 Tokenizer::Html(ref tokenizer) => tokenizer.url(),
909 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.url(),
910 Tokenizer::Xml(ref tokenizer) => tokenizer.url(),
911 }
912 }
913
914 fn set_plaintext_state(&self) {
915 match *self {
916 Tokenizer::Html(ref tokenizer) => tokenizer.set_plaintext_state(),
917 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.set_plaintext_state(),
918 Tokenizer::Xml(_) => unimplemented!(),
919 }
920 }
921
922 fn get_current_line(&self) -> u32 {
923 match *self {
924 Tokenizer::Html(ref tokenizer) => tokenizer.get_current_line(),
925 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.get_current_line(),
926 Tokenizer::Xml(ref tokenizer) => tokenizer.get_current_line(),
927 }
928 }
929}
930
931struct NavigationParams {
935 policy_container: Arc<PolicyContainer>,
937 content_type: Option<Mime>,
939 link_headers: Vec<LinkHeader>,
941 final_sandboxing_flag_set: SandboxingFlagSet,
943 resource_header: Vec<u8>,
945 about_base_url: Option<ServoUrl>,
947 iframe_element_referrer_policy: ReferrerPolicy,
949}
950
951pub(crate) struct ParserContext {
954 parser: Option<Trusted<ServoParser>>,
956 is_synthesized_document: bool,
958 has_loaded_document: bool,
960 webview_id: WebViewId,
962 pipeline_id: PipelineId,
964 url: ServoUrl,
966 pushed_entry_index: Option<usize>,
968 navigation_params: NavigationParams,
970 parent_info: Option<PipelineId>,
972 target_snapshot_params: TargetSnapshotParams,
973 source_origin: Option<MutableOrigin>,
979 document: Option<Trusted<Document>>,
980}
981
982impl ParserContext {
983 pub(crate) fn new(
984 webview_id: WebViewId,
985 pipeline_id: PipelineId,
986 url: ServoUrl,
987 creation_sandboxing_flag_set: SandboxingFlagSet,
988 parent_info: Option<PipelineId>,
989 target_snapshot_params: TargetSnapshotParams,
990 source_origin: Option<MutableOrigin>,
991 ) -> ParserContext {
992 ParserContext {
993 parser: None,
994 is_synthesized_document: false,
995 has_loaded_document: false,
996 webview_id,
997 pipeline_id,
998 url,
999 parent_info,
1000 pushed_entry_index: None,
1001 navigation_params: NavigationParams {
1002 policy_container: Default::default(),
1003 content_type: None,
1004 link_headers: vec![],
1005 final_sandboxing_flag_set: creation_sandboxing_flag_set,
1006 resource_header: vec![],
1007 about_base_url: Default::default(),
1008 iframe_element_referrer_policy: Default::default(),
1009 },
1010 target_snapshot_params,
1011 source_origin,
1012 document: None,
1013 }
1014 }
1015
1016 pub(crate) fn set_policy_container(&mut self, policy_container: Option<Arc<PolicyContainer>>) {
1017 let Some(policy_container) = policy_container else {
1018 return;
1019 };
1020 self.navigation_params.policy_container = policy_container;
1021 }
1022
1023 pub(crate) fn set_about_base_url(&mut self, about_base_url: Option<ServoUrl>) {
1024 self.navigation_params.about_base_url = about_base_url;
1025 }
1026
1027 pub(crate) fn get_document(&self) -> Option<DomRoot<Document>> {
1028 self.parser
1029 .as_ref()
1030 .map(|parser| parser.root().document.as_rooted())
1031 }
1032
1033 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
1034 self.parent_info
1035 }
1036
1037 fn create_policy_container_from_fetch_response(metadata: &Metadata) -> PolicyContainer {
1039 PolicyContainer {
1046 csp_list: parse_csp_list_from_metadata(&metadata.headers),
1048 embedder_policy: Default::default(),
1052 referrer_policy: ReferrerPolicy::parse_header_for_response(&metadata.headers),
1054 }
1055 }
1056
1057 fn initialize_document_object(&self, cx: &mut JSContext, document: &Document) {
1059 document.set_policy_container(self.navigation_params.policy_container.clone());
1062 document.set_active_sandboxing_flag_set(self.navigation_params.final_sandboxing_flag_set);
1064 document.set_document_readiness_to_loading_for_initialization();
1066 document.set_about_base_url(self.navigation_params.about_base_url.clone());
1068 document.set_internal_ancestor_origin_objects_list(
1072 document.internal_ancestor_origin_objects_list_creation_steps(
1073 &self.navigation_params.iframe_element_referrer_policy,
1074 ),
1075 );
1076 document.set_ancestor_origins_list(&document.ancestor_origins_list_creation_steps(cx));
1079 process_link_headers(
1081 &self.navigation_params.link_headers,
1082 document,
1083 LinkProcessingPhase::PreMedia,
1084 );
1085 }
1086
1087 fn process_link_headers_in_media_phase_with_task(&mut self, document: &Document) {
1089 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1093 if !link_headers.is_empty() {
1094 let window = document.window();
1095 let document = Trusted::new(document);
1096 window
1097 .upcast::<GlobalScope>()
1098 .task_manager()
1099 .networking_task_source()
1100 .queue(task!(process_link_headers_task: move || {
1101 process_link_headers(&link_headers, &document.root(), LinkProcessingPhase::Media);
1102 }));
1103 }
1104 }
1105
1106 fn load_document(
1108 &mut self,
1109 cx: &mut JSContext,
1110 parser: Option<&ServoParser>,
1111 document: &Document,
1112 ) {
1113 assert!(!self.has_loaded_document);
1114 self.has_loaded_document = true;
1115 let content_type = &self.navigation_params.content_type;
1117 let mime_type = MimeClassifier::default().classify(
1118 LoadContext::Browsing,
1119 NoSniffFlag::Off,
1120 ApacheBugFlag::from_content_type(content_type.as_ref()),
1121 content_type,
1122 &self.navigation_params.resource_header,
1123 );
1124 let Some(media_type) = MimeClassifier::get_media_type(&mime_type) else {
1128 let page = format!(
1129 "<html><body><p>Unknown content type ({}).</p></body></html>",
1130 mime_type,
1131 );
1132 self.load_inline_unknown_content(
1133 cx,
1134 parser.expect("Must have a parser for unknown content"),
1135 page,
1136 );
1137 return;
1138 };
1139 match media_type {
1140 MediaType::Html => self.load_html_document(cx, document),
1142 MediaType::Xml => self.load_xml_document(cx, document),
1144 MediaType::JavaScript | MediaType::Text | MediaType::Css => {
1146 self.load_text_document(cx, parser.expect("Must have a parser for text"))
1147 },
1148 MediaType::Json => {
1150 self.load_json_document(cx, parser.expect("Must have a parser for JSON"))
1151 },
1152 MediaType::Image | MediaType::AudioVideo => {
1154 self.load_media_document(
1155 cx,
1156 parser.expect("Must have a parser for media"),
1157 media_type,
1158 &mime_type,
1159 );
1160 return;
1161 },
1162 MediaType::Font => {
1163 let page = format!(
1164 "<html><body><p>Unable to load font with content type ({}).</p></body></html>",
1165 mime_type,
1166 );
1167 self.load_inline_unknown_content(
1168 cx,
1169 parser.expect("Must have a parser for inline unknown"),
1170 page,
1171 );
1172 return;
1173 },
1174 };
1175
1176 if let Some(parser) = parser {
1177 parser.parse_bytes_chunk(
1178 cx,
1179 std::mem::take(&mut self.navigation_params.resource_header).as_ref(),
1180 );
1181 }
1182 }
1183
1184 fn load_html_document(&mut self, cx: &mut JSContext, document: &Document) {
1186 self.initialize_document_object(cx, document);
1189 if document.is_initial_about_blank() {
1191 populate_about_blank(cx, document);
1192 }
1193 self.process_link_headers_in_media_phase_with_task(document);
1197 }
1198
1199 fn load_xml_document(&mut self, cx: &mut JSContext, document: &Document) {
1201 self.initialize_document_object(cx, document);
1207 self.process_link_headers_in_media_phase_with_task(document);
1211 }
1212
1213 fn load_text_document(&mut self, cx: &mut JSContext, parser: &ServoParser) {
1215 self.initialize_document_object(cx, &parser.document);
1218 let page = "<pre>\n".into();
1225 parser.push_string_input_chunk(page);
1226 parser.parse_sync(cx);
1227 parser.tokenizer.set_plaintext_state();
1228 parser.document.set_quirks_mode(ServoQuirksMode::NoQuirks);
1230 self.process_link_headers_in_media_phase_with_task(&parser.document);
1234 }
1235
1236 fn load_media_document(
1238 &mut self,
1239 cx: &mut JSContext,
1240 parser: &ServoParser,
1241 media_type: MediaType,
1242 mime_type: &Mime,
1243 ) {
1244 self.initialize_document_object(cx, &parser.document);
1247 self.is_synthesized_document = true;
1249 parser.last_chunk_received.set(true);
1250 let page = "<html><body></body></html>".into();
1252 parser.push_string_input_chunk(page);
1253 parser.parse_sync(cx);
1254 parser.document.set_quirks_mode(ServoQuirksMode::NoQuirks);
1256
1257 let doc = &parser.document;
1258 let node = if media_type == MediaType::Image {
1261 let img = Element::create(
1262 cx,
1263 QualName::new(None, ns!(html), local_name!("img")),
1264 None,
1265 doc,
1266 ElementCreator::ParserCreated(1),
1267 CustomElementCreationMode::Asynchronous,
1268 None,
1269 );
1270 let img = DomRoot::downcast::<HTMLImageElement>(img).unwrap();
1271 img.SetSrc(cx, USVString(self.url.to_string()));
1272 DomRoot::upcast::<Node>(img)
1273 } else if mime_type.type_() == mime::AUDIO {
1274 let audio = Element::create(
1275 cx,
1276 QualName::new(None, ns!(html), local_name!("audio")),
1277 None,
1278 doc,
1279 ElementCreator::ParserCreated(1),
1280 CustomElementCreationMode::Asynchronous,
1281 None,
1282 );
1283 let audio = DomRoot::downcast::<HTMLMediaElement>(audio).unwrap();
1284 audio.SetControls(cx, true);
1285 audio.SetSrc(cx, USVString(self.url.to_string()));
1286 DomRoot::upcast::<Node>(audio)
1287 } else {
1288 let video = Element::create(
1289 cx,
1290 QualName::new(None, ns!(html), local_name!("video")),
1291 None,
1292 doc,
1293 ElementCreator::ParserCreated(1),
1294 CustomElementCreationMode::Asynchronous,
1295 None,
1296 );
1297 let video = DomRoot::downcast::<HTMLMediaElement>(video).unwrap();
1298 video.SetControls(cx, true);
1299 video.SetSrc(cx, USVString(self.url.to_string()));
1300 DomRoot::upcast::<Node>(video)
1301 };
1302 let doc_body = DomRoot::upcast::<Node>(doc.GetBody().unwrap());
1304 doc_body.AppendChild(cx, &node).expect("Appending failed");
1305 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1307 process_link_headers(&link_headers, doc, LinkProcessingPhase::Media);
1308 }
1309
1310 fn load_json_document(&mut self, cx: &mut JSContext, parser: &ServoParser) {
1312 self.initialize_document_object(cx, &parser.document);
1313 parser.push_string_input_chunk(resources::read_string(Resource::JsonViewerHTML));
1314 parser.parse_sync(cx);
1315 parser.tokenizer.set_plaintext_state();
1316 self.process_link_headers_in_media_phase_with_task(&parser.document);
1317 }
1318
1319 fn load_inline_unknown_content(
1321 &mut self,
1322 cx: &mut JSContext,
1323 parser: &ServoParser,
1324 page: String,
1325 ) {
1326 self.is_synthesized_document = true;
1327 parser.document.mark_as_internal();
1328 parser.push_string_input_chunk(page);
1329 parser.last_chunk_received.set(true);
1331 parser.parse_sync(cx);
1332 }
1333
1334 fn submit_resource_timing(&mut self, cx: &mut JSContext) {
1336 let Some(parser) = self.parser.as_ref() else {
1337 return;
1338 };
1339 let parser = parser.root();
1340 if parser.aborted.get() {
1341 return;
1342 }
1343
1344 let document = &parser.document;
1345
1346 let performance_entry = PerformanceNavigationTiming::new(cx, &document.global(), document);
1347 self.pushed_entry_index = document
1348 .global()
1349 .performance(cx)
1350 .queue_entry(performance_entry.upcast::<PerformanceEntry>());
1351 }
1352
1353 fn finish_synchronous_load_for_initial_about_blank(
1354 &self,
1355 cx: &mut JSContext,
1356 document: &Document,
1357 ) {
1358 debug_assert_eq!(document.ReadyState(), DocumentReadyState::Complete);
1362
1363 document.set_current_parser(None, SetParserReason::AboutBlankComplete);
1364 document.finish_load(LoadType::PageSource(self.url.clone()), cx);
1365
1366 document.notify_embedder_of_load_completion();
1367 }
1368
1369 pub(crate) fn process_response(
1372 &mut self,
1373 script_thread: &ScriptThread,
1374 cx: &mut JSContext,
1375 meta_result: Result<FetchMetadata, NetworkError>,
1376 ) {
1377 let (metadata, mut error) = match meta_result {
1378 Ok(meta) => (Some(meta.into()), None),
1379 Err(error) => (
1380 match &error {
1382 NetworkError::LoadCancelled => {
1383 return;
1384 },
1385 _ => {
1386 let mut meta = Metadata::default(self.url.clone());
1387 let mime: Option<Mime> = "text/html".parse().ok();
1388 meta.set_content_type(mime.as_ref());
1389 Some(meta)
1390 },
1391 },
1392 Some(error),
1393 ),
1394 };
1395 let content_type: Option<Mime> = metadata
1396 .clone()
1397 .and_then(|meta| meta.content_type)
1398 .map(Serde::into_inner)
1399 .map(Into::into);
1400
1401 let (policy_container, endpoints_list, link_headers, content_language) = metadata
1406 .as_ref()
1407 .map(|metadata| {
1408 (
1409 Self::create_policy_container_from_fetch_response(metadata),
1410 ReportingEndpoint::parse_reporting_endpoints_header(
1411 &self.url.clone(),
1412 &metadata.headers,
1413 ),
1414 extract_links_from_headers(&metadata.headers),
1415 metadata
1416 .headers
1417 .as_ref()
1418 .and_then(|headers| headers.get("Content-Language"))
1419 .and_then(|header| header.to_str().ok())
1420 .map(|string| string.to_owned()),
1421 )
1422 })
1423 .unwrap_or_default();
1424
1425 let final_sandboxing_flag_set = policy_container
1429 .csp_list
1430 .as_ref()
1431 .and_then(|csp| csp.get_sandboxing_flag_set_for_document())
1432 .unwrap_or(SandboxingFlagSet::empty())
1433 .union(self.target_snapshot_params.sandboxing_flags);
1434
1435 let origin = determine_the_origin(
1439 metadata.as_ref().map(|metadata| &metadata.final_url),
1440 final_sandboxing_flag_set,
1441 self.source_origin.clone(),
1442 );
1443
1444 let Some(document) = script_thread.handle_page_headers_available(
1445 cx,
1446 self.webview_id,
1447 self.pipeline_id,
1448 metadata.as_ref(),
1449 origin.clone(),
1450 ) else {
1451 return;
1452 };
1453
1454 self.document = Some(Trusted::new(&*document));
1455
1456 if document
1457 .get_current_parser()
1458 .is_some_and(|parser| parser.aborted.get())
1459 {
1460 return;
1461 }
1462
1463 let mut realm = enter_auto_realm(cx, &*document);
1464 let cx = &mut realm;
1465 let window = document.window();
1466
1467 if
1470 policy_container.csp_list.should_navigation_response_to_navigation_request_be_blocked(
1477 cx,
1478 window,
1479 self.url.clone().into_url(),
1480 &origin.immutable().clone().into_url_origin(),
1481 )
1482 || !check_a_navigation_response_adherence_to_x_frame_options(
1490 window,
1491 policy_container.csp_list.as_ref(),
1492 &origin,
1493 metadata
1494 .as_ref()
1495 .and_then(|metadata| metadata.headers.as_ref()),
1496 ) {
1497 error = Some(NetworkError::ContentSecurityPolicy);
1501 document.make_document_unsalvageable();
1503 }
1508
1509 if let Some(endpoints) = endpoints_list {
1510 window.set_endpoints_list(endpoints);
1511 }
1512 document.set_default_language(content_language);
1516 if let Some(parser) = document.get_current_parser() {
1517 self.parser = Some(Trusted::new(&*parser));
1518 }
1519 self.navigation_params = NavigationParams {
1520 policy_container: Arc::new(policy_container),
1521 content_type,
1522 final_sandboxing_flag_set,
1523 link_headers,
1524 about_base_url: document.about_base_url(),
1525 resource_header: vec![],
1526 iframe_element_referrer_policy: self
1527 .target_snapshot_params
1528 .iframe_element_referrer_policy,
1529 };
1530 self.submit_resource_timing(cx);
1531
1532 if let Some(error) = error {
1540 let page = match error {
1541 NetworkError::SslValidation(reason, bytes) => {
1542 let page = resources::read_string(Resource::BadCertHTML);
1543 let page = page.replace("${reason}", &reason);
1544 let encoded_bytes = general_purpose::STANDARD_NO_PAD.encode(bytes);
1545 let page = page.replace("${bytes}", encoded_bytes.as_str());
1546 page.replace("${secret}", &net_traits::PRIVILEGED_SECRET.to_string())
1547 },
1548 NetworkError::BlobURLStoreError(reason) |
1549 NetworkError::WebsocketConnectionFailure(reason) |
1550 NetworkError::HttpError(reason) |
1551 NetworkError::ResourceLoadError(reason) |
1552 NetworkError::MimeType(reason) => {
1553 let page = resources::read_string(Resource::NetErrorHTML);
1554 page.replace("${reason}", &reason)
1555 },
1556 NetworkError::Crash(details) => {
1557 let page = resources::read_string(Resource::CrashHTML);
1558 page.replace("${details}", &details)
1559 },
1560 NetworkError::UnsupportedScheme |
1561 NetworkError::CorsGeneral |
1562 NetworkError::CrossOriginResponse |
1563 NetworkError::CorsCredentials |
1564 NetworkError::CorsAllowMethods |
1565 NetworkError::CorsAllowHeaders |
1566 NetworkError::CorsMethod |
1567 NetworkError::CorsAuthorization |
1568 NetworkError::CorsHeaders |
1569 NetworkError::ConnectionFailure |
1570 NetworkError::RedirectError |
1571 NetworkError::TooManyRedirects |
1572 NetworkError::TooManyInFlightKeepAliveRequests |
1573 NetworkError::InvalidMethod |
1574 NetworkError::ContentSecurityPolicy |
1575 NetworkError::Nosniff |
1576 NetworkError::SubresourceIntegrity |
1577 NetworkError::MixedContent |
1578 NetworkError::CacheError |
1579 NetworkError::InvalidPort |
1580 NetworkError::LocalDirectoryError |
1581 NetworkError::PartialResponseToNonRangeRequestError |
1582 NetworkError::ProtocolHandlerSubstitutionError |
1583 NetworkError::DecompressionError => {
1584 let page = resources::read_string(Resource::NetErrorHTML);
1585 page.replace("${reason}", &format!("{:?}", error))
1586 },
1587 NetworkError::LoadCancelled => {
1588 return;
1590 },
1591 };
1592 let parser = document
1593 .get_current_parser()
1594 .expect("Must have a parser for errors");
1595 self.load_inline_unknown_content(cx, &parser, page);
1596 }
1597 }
1598
1599 pub(crate) fn process_response_chunk(&mut self, cx: &mut JSContext, payload: Bytes) {
1600 if self.is_synthesized_document {
1601 return;
1602 }
1603 let Some(parser) = self.parser.as_ref().map(|p| p.root()) else {
1604 return;
1605 };
1606 let Some(document) = self.document.as_ref().map(|document| document.root()) else {
1607 return;
1608 };
1609 if parser.aborted.get() {
1610 return;
1611 }
1612 if !self.has_loaded_document {
1613 self.navigation_params
1615 .resource_header
1616 .extend_from_slice(payload.as_ref());
1617 if self.navigation_params.resource_header.len() >= 1445 {
1619 self.load_document(cx, Some(&parser), &document);
1620 }
1621 } else {
1622 parser.parse_bytes_chunk(cx, payload.as_ref());
1623 }
1624 }
1625
1626 pub(crate) fn process_response_eof(
1630 mut self,
1631 cx: &mut JSContext,
1632 status: Result<(), NetworkError>,
1633 timing: ResourceFetchTiming,
1634 ) {
1635 let parser = self.parser.as_ref().map(|parser| parser.root());
1636 if parser.as_ref().is_some_and(|parser| parser.aborted.get()) ||
1637 self.is_synthesized_document
1638 {
1639 return;
1640 }
1641
1642 if let Err(error) = &status {
1643 debug!("Failed to load page URL {}, error: {error:?}", self.url);
1645 }
1646
1647 let Some(document) = self.document.as_ref().map(|document| document.root()) else {
1648 return;
1649 };
1650
1651 if !self.has_loaded_document {
1655 self.load_document(cx, parser.as_deref(), &document);
1656 }
1657
1658 let mut realm = enter_auto_realm(cx, &*document);
1659 let cx = &mut realm;
1660
1661 if status.is_ok() {
1662 document.set_resource_fetch_timing(timing);
1663 }
1664
1665 if let Some(parser) = parser {
1666 parser.last_chunk_received.set(true);
1667 if !parser.suspended.get() {
1668 parser.parse_sync(cx);
1669 }
1670 }
1671
1672 if let Some(pushed_index) = self.pushed_entry_index {
1674 let performance_entry =
1675 PerformanceNavigationTiming::new(cx, &document.global(), &document);
1676 document
1677 .global()
1678 .performance(cx)
1679 .update_entry(pushed_index, performance_entry.upcast::<PerformanceEntry>());
1680 }
1681
1682 if document.is_initial_about_blank() {
1683 self.finish_synchronous_load_for_initial_about_blank(cx, &document);
1684 }
1685 }
1686}
1687
1688pub(crate) struct FragmentContext<'a> {
1689 pub(crate) context_elem: &'a Node,
1690 pub(crate) form_elem: Option<&'a Node>,
1691 pub(crate) context_element_allows_scripting: bool,
1692}
1693
1694#[cfg_attr(crown, expect(crown::unrooted_must_root))]
1696fn insert_an_element_at_the_adjusted_insertion_location(
1697 cx: &mut JSContext,
1698 node_to_insert: Dom<Node>,
1699 adjusted_insertion_location_parent: &Node,
1700 adjusted_insertion_location_child: Option<&Node>,
1701 parsing_algorithm: ParsingAlgorithm,
1702 custom_element_reaction_stack: &CustomElementReactionStack,
1703) {
1704 if Node::ensure_pre_insertion_validity(
1711 cx.no_gc(),
1712 &node_to_insert,
1713 adjusted_insertion_location_parent,
1714 adjusted_insertion_location_child,
1715 )
1716 .is_err()
1717 {
1718 return;
1719 }
1720
1721 let element_in_non_fragment =
1725 parsing_algorithm != ParsingAlgorithm::Fragment && node_to_insert.is::<Element>();
1726 if element_in_non_fragment {
1727 custom_element_reaction_stack.push_new_element_queue();
1728 }
1729
1730 Node::insert(
1732 cx,
1733 &node_to_insert,
1734 adjusted_insertion_location_parent,
1735 adjusted_insertion_location_child,
1736 SuppressObserver::Unsuppressed,
1737 );
1738
1739 if element_in_non_fragment {
1745 custom_element_reaction_stack.pop_current_element_queue(cx);
1746 }
1747}
1748
1749#[cfg_attr(crown, expect(crown::unrooted_must_root))]
1750fn insert(
1751 cx: &mut JSContext,
1752 parent: &Node,
1753 reference_child: Option<&Node>,
1754 child: NodeOrText<Dom<Node>>,
1755 parsing_algorithm: ParsingAlgorithm,
1756 custom_element_reaction_stack: &CustomElementReactionStack,
1757) {
1758 match child {
1759 NodeOrText::AppendNode(node) => {
1760 insert_an_element_at_the_adjusted_insertion_location(
1766 cx,
1767 node,
1768 parent,
1769 reference_child,
1770 parsing_algorithm,
1771 custom_element_reaction_stack,
1772 );
1773 },
1774 NodeOrText::AppendText(t) => {
1775 let text = reference_child
1777 .and_then(Node::GetPreviousSibling)
1778 .or_else(|| parent.GetLastChild())
1779 .and_then(DomRoot::downcast::<Text>);
1780
1781 if let Some(text) = text {
1782 text.upcast::<CharacterData>().append_data(cx, &t);
1783 } else {
1784 let text = Text::new(cx, String::from(t).into(), &parent.owner_doc());
1785 parent
1786 .InsertBefore(cx, text.upcast(), reference_child)
1787 .unwrap();
1788 }
1789 },
1790 }
1791}
1792
1793#[derive(JSTraceable, MallocSizeOf)]
1794#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1795pub(crate) struct Sink {
1796 #[no_trace]
1797 base_url: ServoUrl,
1798 document: Dom<Document>,
1799 current_line: Cell<u64>,
1800 script: MutNullableDom<HTMLScriptElement>,
1801 parsing_algorithm: ParsingAlgorithm,
1802 #[conditional_malloc_size_of]
1803 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
1804}
1805
1806impl Sink {
1807 fn same_tree(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1808 let x = x.downcast::<Element>().expect("Element node expected");
1809 let y = y.downcast::<Element>().expect("Element node expected");
1810
1811 x.is_in_same_home_subtree(y)
1812 }
1813
1814 fn has_parent_node(&self, node: &Dom<Node>) -> bool {
1815 node.GetParentNode().is_some()
1816 }
1817}
1818
1819impl TreeSink for Sink {
1820 type Output = Self;
1821
1822 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1823 fn finish(self) -> Self {
1824 self
1825 }
1826
1827 type Handle = Dom<Node>;
1828 type ElemName<'a>
1829 = ExpandedName<'a>
1830 where
1831 Self: 'a;
1832
1833 fn get_document(&self) -> Dom<Node> {
1834 Dom::from_ref(self.document.upcast())
1835 }
1836
1837 #[expect(unsafe_code)]
1838 fn get_template_contents(&self, target: &Dom<Node>) -> Dom<Node> {
1839 let mut cx = unsafe { temp_cx() };
1841 let cx = &mut cx;
1842 let template = target
1843 .downcast::<HTMLTemplateElement>()
1844 .expect("tried to get template contents of non-HTMLTemplateElement in HTML parsing");
1845 Dom::from_ref(template.Content(cx).upcast())
1846 }
1847
1848 fn same_node(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1849 x == y
1850 }
1851
1852 fn elem_name<'a>(&self, target: &'a Dom<Node>) -> ExpandedName<'a> {
1853 let elem = target
1854 .downcast::<Element>()
1855 .expect("tried to get name of non-Element in HTML parsing");
1856 ExpandedName {
1857 ns: elem.namespace(),
1858 local: elem.local_name(),
1859 }
1860 }
1861
1862 #[expect(unsafe_code)]
1863 fn create_element(
1864 &self,
1865 name: QualName,
1866 attrs: Vec<Attribute>,
1867 flags: ElementFlags,
1868 ) -> Dom<Node> {
1869 let mut cx = unsafe { temp_cx() };
1871 let cx = &mut cx;
1872 let attrs = attrs
1873 .into_iter()
1874 .map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
1875 .collect();
1876 let parsing_algorithm = if flags.template {
1877 ParsingAlgorithm::Fragment
1878 } else {
1879 self.parsing_algorithm
1880 };
1881 let element = create_element_for_token(
1882 cx,
1883 name,
1884 attrs,
1885 &self.document,
1886 ElementCreator::ParserCreated(self.current_line.get()),
1887 parsing_algorithm,
1888 &self.custom_element_reaction_stack,
1889 flags.had_duplicate_attributes,
1890 );
1891 Dom::from_ref(element.upcast())
1892 }
1893
1894 #[expect(unsafe_code)]
1895 fn create_comment(&self, text: StrTendril) -> Dom<Node> {
1896 let mut cx = unsafe { temp_cx() };
1898 let cx = &mut cx;
1899 let comment = Comment::new(
1900 cx,
1901 DOMString::from(String::from(text)),
1902 &self.document,
1903 None,
1904 );
1905 Dom::from_ref(comment.upcast())
1906 }
1907
1908 #[expect(unsafe_code)]
1909 fn create_pi(&self, target: StrTendril, data: StrTendril) -> Dom<Node> {
1910 let mut cx = unsafe { temp_cx() };
1912 let cx = &mut cx;
1913 let doc = &*self.document;
1914 let pi = ProcessingInstruction::new(
1915 cx,
1916 DOMString::from(String::from(target)),
1917 DOMString::from(String::from(data)),
1918 doc,
1919 );
1920 Dom::from_ref(pi.upcast())
1921 }
1922
1923 #[expect(unsafe_code)]
1924 fn associate_with_form(
1925 &self,
1926 target: &Dom<Node>,
1927 form: &Dom<Node>,
1928 nodes: (&Dom<Node>, Option<&Dom<Node>>),
1929 ) {
1930 let mut cx = unsafe { temp_cx() };
1932 let cx = &mut cx;
1933 let (element, prev_element) = nodes;
1934 let tree_node = prev_element.map_or(element, |prev| {
1935 if self.has_parent_node(element) {
1936 element
1937 } else {
1938 prev
1939 }
1940 });
1941 if !self.same_tree(tree_node, form) {
1942 return;
1943 }
1944
1945 let node = target;
1946 let form = DomRoot::downcast::<HTMLFormElement>(DomRoot::from_ref(&**form))
1947 .expect("Owner must be a form element");
1948
1949 let elem = node.downcast::<Element>();
1950 let control = elem.and_then(|e| e.as_maybe_form_control());
1951
1952 if let Some(control) = control {
1953 control.set_form_owner_from_parser(cx, &form);
1954 }
1955 }
1956
1957 #[expect(unsafe_code)]
1958 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1959 fn append_before_sibling(&self, sibling: &Dom<Node>, new_node: NodeOrText<Dom<Node>>) {
1960 let mut cx = unsafe { temp_cx() };
1962 let cx = &mut cx;
1963
1964 let parent = sibling
1965 .GetParentNode()
1966 .expect("append_before_sibling called on node without parent");
1967
1968 insert(
1969 cx,
1970 &parent,
1971 Some(sibling),
1972 new_node,
1973 self.parsing_algorithm,
1974 &self.custom_element_reaction_stack,
1975 );
1976 }
1977
1978 fn parse_error(&self, msg: Cow<'static, str>) {
1979 debug!("Parse error: {}", msg);
1980 }
1981
1982 fn set_quirks_mode(&self, mode: QuirksMode) {
1983 let mode = match mode {
1984 QuirksMode::Quirks => ServoQuirksMode::Quirks,
1985 QuirksMode::LimitedQuirks => ServoQuirksMode::LimitedQuirks,
1986 QuirksMode::NoQuirks => ServoQuirksMode::NoQuirks,
1987 };
1988 self.document.set_quirks_mode(mode);
1989 }
1990
1991 #[expect(unsafe_code)]
1992 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1993 fn append(&self, parent: &Dom<Node>, child: NodeOrText<Dom<Node>>) {
1994 let mut cx = unsafe { temp_cx() };
1996 let cx = &mut cx;
1997
1998 insert(
1999 cx,
2000 parent,
2001 None,
2002 child,
2003 self.parsing_algorithm,
2004 &self.custom_element_reaction_stack,
2005 );
2006 }
2007
2008 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
2009 fn append_based_on_parent_node(
2010 &self,
2011 elem: &Dom<Node>,
2012 prev_elem: &Dom<Node>,
2013 child: NodeOrText<Dom<Node>>,
2014 ) {
2015 if self.has_parent_node(elem) {
2016 self.append_before_sibling(elem, child);
2017 } else {
2018 self.append(prev_elem, child);
2019 }
2020 }
2021
2022 #[expect(unsafe_code)]
2023 fn append_doctype_to_document(
2024 &self,
2025 name: StrTendril,
2026 public_id: StrTendril,
2027 system_id: StrTendril,
2028 ) {
2029 let mut cx = unsafe { temp_cx() };
2031 let cx = &mut cx;
2032
2033 let doc = &*self.document;
2034 let doctype = DocumentType::new(
2035 cx,
2036 DOMString::from(String::from(name)),
2037 Some(DOMString::from(String::from(public_id))),
2038 Some(DOMString::from(String::from(system_id))),
2039 doc,
2040 );
2041 doc.upcast::<Node>()
2042 .AppendChild(cx, doctype.upcast())
2043 .expect("Appending failed");
2044 }
2045
2046 #[expect(unsafe_code)]
2047 fn add_attrs_if_missing(&self, target: &Dom<Node>, attrs: Vec<Attribute>) {
2048 let mut cx = unsafe { temp_cx() };
2050 let cx = &mut cx;
2051
2052 let elem = target
2053 .downcast::<Element>()
2054 .expect("tried to set attrs on non-Element in HTML parsing");
2055 for attr in attrs {
2056 elem.set_attribute_from_parser(
2057 cx,
2058 attr.name,
2059 DOMString::from(String::from(attr.value)),
2060 );
2061 }
2062 }
2063
2064 #[expect(unsafe_code)]
2065 fn remove_from_parent(&self, target: &Dom<Node>) {
2066 let mut cx = unsafe { temp_cx() };
2068 let cx = &mut cx;
2069
2070 if let Some(ref parent) = target.GetParentNode() {
2071 parent.RemoveChild(cx, target).unwrap();
2072 }
2073 }
2074
2075 fn mark_script_already_started(&self, node: &Dom<Node>) {
2076 let script = node.downcast::<HTMLScriptElement>();
2077 if let Some(script) = script {
2078 script.set_already_started(true)
2079 }
2080 }
2081
2082 #[expect(unsafe_code)]
2083 fn reparent_children(&self, node: &Dom<Node>, new_parent: &Dom<Node>) {
2084 let mut cx = unsafe { temp_cx() };
2086 let cx = &mut cx;
2087
2088 while let Some(ref child) = node.GetFirstChild() {
2089 new_parent.AppendChild(cx, child).unwrap();
2090 }
2091 }
2092
2093 fn is_mathml_annotation_xml_integration_point(&self, handle: &Dom<Node>) -> bool {
2096 let elem = handle.downcast::<Element>().unwrap();
2097 elem.get_attribute_string_value(&local_name!("encoding"))
2098 .is_some_and(|value| {
2099 value.eq_ignore_ascii_case("text/html") ||
2100 value.eq_ignore_ascii_case("application/xhtml+xml")
2101 })
2102 }
2103
2104 fn set_current_line(&self, line_number: u64) {
2105 self.current_line.set(line_number);
2106 }
2107
2108 #[expect(unsafe_code)]
2109 fn pop(&self, node: &Dom<Node>) {
2110 let mut cx = unsafe { temp_cx() };
2112 let cx = &mut cx;
2113
2114 let node = DomRoot::from_ref(&**node);
2115 vtable_for(&node).pop(cx);
2116 }
2117
2118 fn allow_declarative_shadow_roots(&self, intended_parent: &Dom<Node>) -> bool {
2119 intended_parent.owner_doc().allow_declarative_shadow_roots()
2120 }
2121
2122 #[expect(unsafe_code)]
2126 fn attach_declarative_shadow(
2127 &self,
2128 host: &Dom<Node>,
2129 template: &Dom<Node>,
2130 attributes: &[Attribute],
2131 ) -> bool {
2132 let mut cx = unsafe { temp_cx() };
2134 let cx = &mut cx;
2135
2136 attach_declarative_shadow_inner(cx, host, template, attributes)
2137 }
2138
2139 #[expect(unsafe_code)]
2140 fn maybe_clone_an_option_into_selectedcontent(&self, option: &Self::Handle) {
2141 let mut cx = unsafe { temp_cx() };
2143 let cx = &mut cx;
2144
2145 let Some(option) = option.downcast::<HTMLOptionElement>() else {
2146 if cfg!(debug_assertions) {
2147 unreachable!();
2148 }
2149 log::error!(
2150 "Received non-option element in maybe_clone_an_option_into_selectedcontent"
2151 );
2152 return;
2153 };
2154
2155 option.maybe_clone_an_option_into_selectedcontent(cx)
2156 }
2157}
2158
2159#[expect(clippy::too_many_arguments)]
2161fn create_element_for_token(
2162 cx: &mut JSContext,
2163 name: QualName,
2164 attrs: Vec<ElementAttribute>,
2165 document: &Document,
2166 creator: ElementCreator,
2167 parsing_algorithm: ParsingAlgorithm,
2168 custom_element_reaction_stack: &CustomElementReactionStack,
2169 had_duplicate_attributes: bool,
2170) -> DomRoot<Element> {
2171 let is = attrs
2189 .iter()
2190 .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
2191 .map(|attr| LocalName::from(&attr.value));
2192
2193 let definition = CustomElementRegistry::lookup_custom_element_definition(
2199 document.custom_element_registry().as_deref(),
2200 &name.ns,
2201 &name.local,
2202 is.as_ref(),
2203 );
2204
2205 let will_execute_script =
2208 definition.is_some() && parsing_algorithm != ParsingAlgorithm::Fragment;
2209
2210 if will_execute_script {
2212 document.increment_throw_on_dynamic_markup_insertion_counter();
2214 if is_execution_stack_empty() {
2217 document.window().perform_a_microtask_checkpoint(cx);
2218 }
2219 custom_element_reaction_stack.push_new_element_queue()
2222 }
2223
2224 let creation_mode = if will_execute_script {
2227 CustomElementCreationMode::Synchronous
2228 } else {
2229 CustomElementCreationMode::Asynchronous
2230 };
2231 let element = Element::create(cx, name, is, document, creator, creation_mode, None);
2232
2233 element.attrs().reserve_exact(attrs.len());
2235 for attr in attrs {
2236 element.set_attribute_from_parser(cx, attr.name, attr.value);
2237 }
2238
2239 if had_duplicate_attributes {
2242 element.set_had_duplicate_attributes(cx.no_gc());
2243 }
2244
2245 if will_execute_script {
2247 custom_element_reaction_stack.pop_current_element_queue(cx);
2252 document.decrement_throw_on_dynamic_markup_insertion_counter();
2254 }
2255
2256 if let Some(html_element) = element.downcast::<HTMLElement>() &&
2266 element.is_resettable() &&
2267 !html_element.is_form_associated_custom_element()
2268 {
2269 element.reset(cx);
2270 }
2271
2272 element
2282}
2283
2284fn attach_declarative_shadow_inner(
2285 cx: &mut JSContext,
2286 host: &Node,
2287 template: &Node,
2288 attributes: &[Attribute],
2289) -> bool {
2290 let host_element = host.downcast::<Element>().unwrap();
2291
2292 if host_element.shadow_root().is_some() {
2293 return false;
2294 }
2295
2296 let template_element = template.downcast::<HTMLTemplateElement>().unwrap();
2297
2298 let mut shadow_root_mode = ShadowRootMode::Open;
2308 let mut slot_assignment_mode = SlotAssignmentMode::Named;
2309 let mut clonable = false;
2310 let mut delegatesfocus = false;
2311 let mut serializable = false;
2312
2313 attributes
2314 .iter()
2315 .for_each(|attr: &Attribute| match attr.name.local {
2316 local_name!("shadowrootmode") => {
2317 if attr.value.eq_ignore_ascii_case("open") {
2318 shadow_root_mode = ShadowRootMode::Open;
2319 } else if attr.value.eq_ignore_ascii_case("closed") {
2320 shadow_root_mode = ShadowRootMode::Closed;
2321 } else {
2322 unreachable!("shadowrootmode value is not open nor closed");
2323 }
2324 },
2325 local_name!("shadowrootclonable") => {
2326 clonable = true;
2327 },
2328 local_name!("shadowrootdelegatesfocus") => {
2329 delegatesfocus = true;
2330 },
2331 local_name!("shadowrootserializable") => {
2332 serializable = true;
2333 },
2334 local_name!("shadowrootslotassignment") => {
2335 if attr.value.eq_ignore_ascii_case("manual") {
2336 slot_assignment_mode = SlotAssignmentMode::Manual;
2337 }
2338 },
2339 _ => {},
2340 });
2341
2342 match host_element.attach_shadow(
2345 cx,
2346 IsUserAgentWidget::No,
2347 shadow_root_mode,
2348 clonable,
2349 serializable,
2350 delegatesfocus,
2351 slot_assignment_mode,
2352 ) {
2353 Ok(shadow_root) => {
2354 shadow_root.set_declarative(true);
2356
2357 let shadow = shadow_root.upcast::<DocumentFragment>();
2359 template_element.set_contents(Some(shadow));
2360
2361 shadow_root.set_available_to_element_internals(true);
2363
2364 true
2365 },
2366 Err(_) => false,
2367 }
2368}
2369
2370fn populate_about_blank(cx: &mut JSContext, document: &Document) {
2372 let mut create_html_element = |name| {
2373 create_element(
2374 cx,
2375 QualName::new(None, ns!(html), name),
2376 None,
2377 document,
2378 ElementCreator::ParserCreated(0),
2379 CustomElementCreationMode::Synchronous,
2380 None,
2381 )
2382 };
2383 let html = create_html_element(local_name!("html"));
2385 let head = create_html_element(local_name!("head"));
2387 let body = create_html_element(local_name!("body"));
2389 let _ = document.upcast::<Node>().AppendChild(cx, html.upcast());
2391 let _ = html.upcast::<Node>().AppendChild(cx, head.upcast());
2393 let _ = html.upcast::<Node>().AppendChild(cx, body.upcast());
2395}
2396
2397impl Document {
2398 fn internal_ancestor_origin_objects_list_creation_steps(
2400 &self,
2401 referrer_policy: &ReferrerPolicy,
2402 ) -> Vec<ImmutableOrigin> {
2403 let mut output = vec![];
2405 let window_proxy = self.window().window_proxy();
2409 let Some((parent_origin, ancestor_origins)) =
2410 window_proxy.parent_origin_and_internal_ancestor_origin_objects_list()
2411 else {
2412 return output;
2414 };
2415 let mut masked =
2417 *referrer_policy == ReferrerPolicy::NoReferrer ||
2419 (*referrer_policy == ReferrerPolicy::SameOrigin && !parent_origin.same_origin(&self.origin()));
2422 if masked {
2424 output.push(ImmutableOrigin::new_opaque());
2425 } else {
2426 output.push(parent_origin.immutable().clone());
2428 }
2429 for ancestor_origin in ancestor_origins {
2431 if masked && ancestor_origin.same_origin(&parent_origin) {
2434 output.push(ImmutableOrigin::new_opaque());
2435 continue;
2436 }
2437 output.push(ancestor_origin.clone());
2439 masked = false;
2440 }
2441 output
2443 }
2444
2445 fn ancestor_origins_list_creation_steps(&self, cx: &mut JSContext) -> DomRoot<DOMStringList> {
2447 let ancestor_origins = self.internal_ancestor_origin_objects_list();
2450 let ancestor_origins = ancestor_origins
2451 .as_ref()
2452 .expect("Must always have initialized ancestor origin objects list");
2453 let mut output = Vec::with_capacity(ancestor_origins.len());
2455 for origin in ancestor_origins {
2457 output.push(origin.ascii_serialization().into());
2459 }
2460 DOMStringList::new(cx, &self.global(), output)
2462 }
2463}