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::response::RedirectTaint;
33use net_traits::{
34 FetchMetadata, LoadContext, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
35};
36use profile_traits::time::{
37 ProfilerCategory, ProfilerChan, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
38};
39use profile_traits::time_profile;
40use script_bindings::cell::DomRefCell;
41use script_bindings::reflector::{Reflector, reflect_dom_object};
42use script_bindings::script_runtime::temp_cx;
43use script_traits::DocumentActivity;
44use servo_base::id::{PipelineId, WebViewId};
45use servo_config::pref;
46use servo_constellation_traits::TargetSnapshotParams;
47use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
48use style::context::QuirksMode as ServoQuirksMode;
49use tendril::stream::LossyDecoder;
50use tendril::{ByteTendril, TendrilSink};
51
52use crate::dom::SuppressObserver;
53use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
54 DocumentMethods, DocumentReadyState,
55};
56use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
57use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods;
58use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
59use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
60use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
61 ShadowRootMode, SlotAssignmentMode,
62};
63use crate::dom::bindings::inheritance::Castable;
64use crate::dom::bindings::refcounted::Trusted;
65use crate::dom::bindings::reflector::DomGlobal;
66use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
67use crate::dom::bindings::settings_stack::is_execution_stack_empty;
68use crate::dom::bindings::str::{DOMString, USVString};
69use crate::dom::characterdata::CharacterData;
70use crate::dom::comment::Comment;
71use crate::dom::csp::parse_csp_list_from_metadata;
72use crate::dom::customelementregistry::{CustomElementReactionStack, CustomElementRegistry};
73use crate::dom::document::{
74 AbortReason, Document, HasBrowsingContext, IsHTMLDocument, SetParserReason,
75};
76use crate::dom::documentfragment::DocumentFragment;
77use crate::dom::documenttype::DocumentType;
78use crate::dom::domstringlist::DOMStringList;
79use crate::dom::element::create::create_element;
80use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
81use crate::dom::globalscope::GlobalScope;
82use crate::dom::html::document_metadata::processingoptions::{
83 LinkHeader, LinkProcessingPhase, extract_links_from_headers, process_link_headers,
84};
85use crate::dom::html::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
86use crate::dom::html::htmlimageelement::HTMLImageElement;
87use crate::dom::html::htmlscriptelement::{HTMLScriptElement, ScriptResult};
88use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
89use crate::dom::iterators::ShadowIncluding;
90use crate::dom::node::Node;
91use crate::dom::node::virtualmethods::vtable_for;
92use crate::dom::performance::performanceentry::PerformanceEntry;
93use crate::dom::performance::performancenavigationtiming::PerformanceNavigationTiming;
94use crate::dom::processinginstruction::ProcessingInstruction;
95use crate::dom::reporting::reportingendpoint::ReportingEndpoint;
96use crate::dom::security::csp::CspReporting;
97use crate::dom::security::xframeoptions::check_a_navigation_response_adherence_to_x_frame_options;
98use crate::dom::shadowroot::IsUserAgentWidget;
99use crate::dom::text::Text;
100use crate::dom::types::{HTMLElement, HTMLMediaElement, HTMLOptionElement};
101use crate::event_loop::document_loader::{DocumentLoader, LoadType};
102use crate::event_loop::script_thread::ScriptThread;
103use crate::navigation::determine_the_origin;
104use crate::realms::enter_auto_realm;
105use crate::runtime::script_runtime::IntroductionType;
106
107mod async_html;
108pub(crate) mod encoding;
109pub(crate) mod html;
110mod prefetch;
111mod xml;
112
113use encoding::{NetworkDecoderState, NetworkSink};
114pub(crate) use html::serialize_html_fragment;
115
116#[dom_struct]
117pub(crate) struct ServoParser {
130 reflector: Reflector,
131 document: Dom<Document>,
133 network_decoder: DomRefCell<NetworkDecoderState>,
135 #[ignore_malloc_size_of = "Defined in html5ever"]
137 #[no_trace]
138 network_input: BufferQueue,
139 #[ignore_malloc_size_of = "Defined in html5ever"]
141 #[no_trace]
142 script_input: BufferQueue,
143 tokenizer: Tokenizer,
145 last_chunk_received: Cell<bool>,
147 suspended: Cell<bool>,
149 script_nesting_level: Cell<usize>,
151 aborted: Cell<bool>,
153 stopped: Cell<bool>,
155 script_created_parser: bool,
157 #[no_trace]
162 prefetch_decoder: RefCell<LossyDecoder<NetworkSink>>,
163 prefetch_tokenizer: prefetch::Tokenizer,
167 #[ignore_malloc_size_of = "Defined in html5ever"]
168 #[no_trace]
169 prefetch_input: BufferQueue,
170 #[cfg(feature = "devtools")]
173 content_for_devtools: Option<DomRefCell<String>>,
174}
175
176pub(crate) struct ElementAttribute {
177 name: QualName,
178 value: DOMString,
179}
180
181#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
182pub(crate) enum ParsingAlgorithm {
183 Normal,
184 Fragment,
185}
186
187impl ElementAttribute {
188 pub(crate) fn new(name: QualName, value: DOMString) -> ElementAttribute {
189 ElementAttribute { name, value }
190 }
191}
192
193impl ServoParser {
194 pub(crate) fn parse_html_document(
196 cx: &mut JSContext,
197 document: &Document,
198 input: Option<DOMString>,
199 url: ServoUrl,
200 encoding_hint_from_content_type: Option<&'static Encoding>,
201 encoding_of_container_document: Option<&'static Encoding>,
202 ) {
203 assert!(document.is_html_document());
207
208 let parser = ServoParser::new(
210 cx,
211 document,
212 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
213 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
214 } else {
215 Tokenizer::Html(self::html::Tokenizer::new(
216 document,
217 url,
218 None,
219 ParsingAlgorithm::Normal,
220 ))
221 },
222 ParserKind::Normal,
223 encoding_hint_from_content_type,
224 encoding_of_container_document,
225 );
226
227 if let Some(input) = input {
233 parser.parse_complete_string_chunk(
234 cx,
235 String::from(input),
236 SetParserReason::ParsingHtmlDocument,
237 );
238 } else {
239 parser
240 .document
241 .set_current_parser(Some(&parser), SetParserReason::ParsingHtmlDocument);
242 }
243 }
244
245 pub(crate) fn parse_html_fragment<'el>(
247 cx: &mut JSContext,
248 context: &'el Element,
249 input: DOMString,
250 allow_declarative_shadow_roots: bool,
251 ) -> impl Iterator<Item = DomRoot<Node>> + use<'el> {
252 let context_node = context.upcast::<Node>();
253 let context_document = context_node.owner_doc();
254 let window = context_document.window();
255 let url = context_document.url();
256
257 let loader = DocumentLoader::new_with_threads(
259 context_document.loader().resource_threads().clone(),
260 Some(url.clone()),
261 );
262 let document = Document::new(
263 cx,
264 window,
265 HasBrowsingContext::No,
266 Some(url.clone()),
267 context_document.about_base_url(),
268 context_document.origin().clone(),
269 IsHTMLDocument::HTMLDocument,
270 None,
271 None,
272 DocumentActivity::Inactive,
273 loader,
274 None,
275 None,
276 false,
277 allow_declarative_shadow_roots,
278 Some(context_document.insecure_requests_policy()),
279 context_document.has_trustworthy_ancestor_or_current_origin(),
280 context_document.custom_element_reaction_stack(),
281 context_document.creation_sandboxing_flag_set(),
282 context_document.pipeline_id(),
283 context_document.image_cache(),
284 );
285
286 document.set_quirks_mode(context_document.quirks_mode());
290
291 let form = context_node
298 .inclusive_ancestors(ShadowIncluding::No)
299 .find(|element| element.is::<HTMLFormElement>());
300
301 let fragment_context = FragmentContext {
302 context_elem: context_node,
303 form_elem: form.as_deref(),
304 context_element_allows_scripting: context_document.scripting_enabled(),
305 };
306
307 let parser = ServoParser::new(
308 cx,
309 &document,
310 Tokenizer::Html(self::html::Tokenizer::new(
311 &document,
312 url,
313 Some(fragment_context),
314 ParsingAlgorithm::Fragment,
315 )),
316 ParserKind::Normal,
317 None,
318 None,
319 );
320 parser.parse_complete_string_chunk(
321 cx,
322 String::from(input),
323 SetParserReason::ParsingHtmlFragment,
324 );
325
326 let root_element = document.GetDocumentElement().expect("no document element");
328 FragmentParsingResult {
329 inner: root_element.upcast::<Node>().children(),
330 }
331 }
332
333 pub(crate) fn parse_html_script_input(cx: &mut JSContext, document: &Document, url: ServoUrl) {
334 let parser = ServoParser::new(
335 cx,
336 document,
337 if pref!(dom_servoparser_async_html_tokenizer_enabled) {
338 Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None))
339 } else {
340 Tokenizer::Html(self::html::Tokenizer::new(
341 document,
342 url,
343 None,
344 ParsingAlgorithm::Normal,
345 ))
346 },
347 ParserKind::ScriptCreated,
348 None,
349 None,
350 );
351 document.set_current_parser(Some(&parser), SetParserReason::ParsingHtmlScriptInput);
352 }
353
354 pub(crate) fn parse_xml_document(
355 cx: &mut JSContext,
356 document: &Document,
357 input: Option<DOMString>,
358 url: ServoUrl,
359 encoding_hint_from_content_type: Option<&'static Encoding>,
360 ) {
361 let parser = ServoParser::new(
362 cx,
363 document,
364 Tokenizer::Xml(self::xml::Tokenizer::new(document, url)),
365 ParserKind::Normal,
366 encoding_hint_from_content_type,
367 None,
368 );
369
370 if let Some(input) = input {
372 parser.parse_complete_string_chunk(
373 cx,
374 String::from(input),
375 SetParserReason::ParsingXmlDocument,
376 );
377 } else {
378 parser
379 .document
380 .set_current_parser(Some(&parser), SetParserReason::ParsingXmlDocument);
381 }
382 }
383
384 pub(crate) fn script_nesting_level(&self) -> usize {
385 self.script_nesting_level.get()
386 }
387
388 pub(crate) fn is_script_created(&self) -> bool {
389 self.script_created_parser
390 }
391
392 pub(crate) fn resume_with_pending_parsing_blocking_script(
407 &self,
408 cx: &mut JSContext,
409 script: &HTMLScriptElement,
410 result: ScriptResult,
411 ) {
412 assert!(self.suspended.get());
413 self.suspended.set(false);
414
415 self.script_input.swap_with(&self.network_input);
416 while let Some(chunk) = self.script_input.pop_front() {
417 self.network_input.push_back(chunk);
418 }
419
420 let script_nesting_level = self.script_nesting_level.get();
421 assert_eq!(script_nesting_level, 0);
422
423 self.script_nesting_level.set(script_nesting_level + 1);
424 script.execute(cx, result);
425 self.script_nesting_level.set(script_nesting_level);
426
427 if !self.suspended.get() && !self.aborted.get() {
428 self.parse_sync(cx);
429 }
430 }
431
432 pub(crate) fn can_write(&self) -> bool {
433 self.script_created_parser || self.script_nesting_level.get() > 0
434 }
435
436 pub(crate) fn write(&self, cx: &mut JSContext, text: DOMString) {
438 assert!(self.can_write());
439
440 if self.document.has_pending_parsing_blocking_script() {
441 self.script_input.push_back(String::from(text).into());
445 return;
446 }
447
448 assert!(self.script_input.is_empty());
452
453 let input = BufferQueue::default();
454 input.push_back(String::from(text).into());
455
456 let profiler_chan = self
457 .document
458 .window()
459 .as_global_scope()
460 .time_profiler_chan()
461 .clone();
462 let profiler_metadata = TimerMetadata {
463 url: self.document.url().as_str().into(),
464 iframe: TimerMetadataFrameType::RootWindow,
465 incremental: TimerMetadataReflowType::FirstReflow,
466 };
467 self.tokenize(cx, |cx, tokenizer| {
468 tokenizer.feed(cx, &input, profiler_chan.clone(), profiler_metadata.clone())
469 });
470
471 if self.suspended.get() {
472 while let Some(chunk) = input.pop_front() {
476 self.script_input.push_back(chunk);
477 }
478 return;
479 }
480
481 assert!(input.is_empty());
482 }
483
484 pub(crate) fn close(&self, cx: &mut JSContext) {
486 assert!(self.script_created_parser);
487
488 self.last_chunk_received.set(true);
490
491 if self.suspended.get() {
493 return;
494 }
495
496 self.parse_sync(cx);
499 }
500
501 pub(crate) fn abort(&self, cx: &mut JSContext, reason: AbortReason) {
503 if self.aborted.get() {
506 return;
507 }
508
509 self.aborted.set(true);
510
511 self.script_input.replace_with(BufferQueue::default());
514 self.network_input.replace_with(BufferQueue::default());
515
516 self.document
521 .update_the_current_document_readiness(cx, DocumentReadyState::Interactive);
522
523 self.tokenizer.end(cx);
525 self.document
526 .set_current_parser(None, SetParserReason::Abort(reason));
527
528 self.document
530 .update_the_current_document_readiness(cx, DocumentReadyState::Complete);
531 }
532
533 pub(crate) fn get_current_line(&self) -> u32 {
534 self.tokenizer.get_current_line()
535 }
536
537 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
538 fn new_inherited(
539 document: &Document,
540 tokenizer: Tokenizer,
541 kind: ParserKind,
542 encoding_hint_from_content_type: Option<&'static Encoding>,
543 encoding_of_container_document: Option<&'static Encoding>,
544 ) -> Self {
545 #[cfg(feature = "devtools")]
549 let content_for_devtools = (document.global().devtools_chan().is_some() &&
550 document.has_browsing_context())
551 .then_some(DomRefCell::new(String::new()));
552
553 ServoParser {
554 reflector: Reflector::new(),
555 document: Dom::from_ref(document),
556 network_decoder: DomRefCell::new(NetworkDecoderState::new(
557 encoding_hint_from_content_type,
558 encoding_of_container_document,
559 )),
560 network_input: BufferQueue::default(),
561 script_input: BufferQueue::default(),
562 tokenizer,
563 last_chunk_received: Cell::new(false),
564 suspended: Default::default(),
565 script_nesting_level: Default::default(),
566 aborted: Default::default(),
567 stopped: Default::default(),
568 script_created_parser: kind == ParserKind::ScriptCreated,
569 prefetch_decoder: RefCell::new(LossyDecoder::new_encoding_rs(
570 encoding_hint_from_content_type.unwrap_or(UTF_8),
571 Default::default(),
572 )),
573 prefetch_tokenizer: prefetch::Tokenizer::new(document),
574 prefetch_input: BufferQueue::default(),
575 #[cfg(feature = "devtools")]
576 content_for_devtools,
577 }
578 }
579
580 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
581 fn new(
582 cx: &mut JSContext,
583 document: &Document,
584 tokenizer: Tokenizer,
585 kind: ParserKind,
586 encoding_hint_from_content_type: Option<&'static Encoding>,
587 encoding_of_container_document: Option<&'static Encoding>,
588 ) -> DomRoot<Self> {
589 reflect_dom_object(
590 cx,
591 Box::new(ServoParser::new_inherited(
592 document,
593 tokenizer,
594 kind,
595 encoding_hint_from_content_type,
596 encoding_of_container_document,
597 )),
598 document.window(),
599 )
600 }
601
602 fn push_tendril_input_chunk(&self, chunk: StrTendril) {
603 #[cfg(feature = "devtools")]
604 if let Some(mut content_for_devtools) = self
605 .content_for_devtools
606 .as_ref()
607 .map(|content| content.borrow_mut())
608 {
609 content_for_devtools.push_str(chunk.as_ref());
611 }
612
613 if chunk.is_empty() {
614 return;
615 }
616
617 self.network_input.push_back(chunk);
620 }
621
622 fn push_bytes_input_chunk(&self, chunk: &[u8]) {
623 if let Some(decoded_chunk) = self
625 .network_decoder
626 .borrow_mut()
627 .push(chunk, &self.document)
628 {
629 self.push_tendril_input_chunk(decoded_chunk);
630 }
631
632 if self.should_prefetch() {
633 let mut prefetch_decoder = self.prefetch_decoder.borrow_mut();
639 prefetch_decoder.process(ByteTendril::from(chunk));
640
641 self.prefetch_input
642 .push_back(mem::take(&mut prefetch_decoder.inner_sink_mut().output));
643 self.prefetch_tokenizer.feed(&self.prefetch_input);
644 }
645 }
646
647 fn should_prefetch(&self) -> bool {
648 self.document.browsing_context().is_some()
656 }
657
658 fn push_string_input_chunk(&self, chunk: String) {
659 let chunk = StrTendril::from(chunk);
662 self.push_tendril_input_chunk(chunk);
663 }
664
665 fn parse_sync(&self, cx: &mut JSContext) {
666 assert!(self.script_input.is_empty());
667
668 if self.last_chunk_received.get() {
672 let chunk = self.network_decoder.borrow_mut().finish(&self.document);
673 if !chunk.is_empty() {
674 self.push_tendril_input_chunk(chunk);
675 }
676 }
677
678 if self.aborted.get() {
679 return;
680 }
681
682 let profiler_chan = self
683 .document
684 .window()
685 .as_global_scope()
686 .time_profiler_chan()
687 .clone();
688 let profiler_metadata = TimerMetadata {
689 url: self.document.url().as_str().into(),
690 iframe: TimerMetadataFrameType::RootWindow,
691 incremental: TimerMetadataReflowType::FirstReflow,
692 };
693 self.tokenize(cx, |cx, tokenizer| {
694 tokenizer.feed(
695 cx,
696 &self.network_input,
697 profiler_chan.clone(),
698 profiler_metadata.clone(),
699 )
700 });
701
702 if self.suspended.get() || self.aborted.get() {
704 return;
705 }
706
707 assert!(self.network_input.is_empty());
708
709 if self.last_chunk_received.get() {
710 self.finish(cx);
711 }
712 }
713
714 fn parse_complete_string_chunk(
715 &self,
716 cx: &mut JSContext,
717 input: String,
718 reason: SetParserReason,
719 ) {
720 self.document.set_current_parser(Some(self), reason);
721 self.push_string_input_chunk(input);
722 self.last_chunk_received.set(true);
723 if !self.suspended.get() {
724 self.parse_sync(cx);
725 }
726 }
727
728 fn parse_bytes_chunk(&self, cx: &mut JSContext, input: &[u8]) {
729 let mut realm = enter_auto_realm(cx, &*self.document);
730 let cx = &mut realm.current_realm();
731 self.document
732 .set_current_parser(Some(self), SetParserReason::ParsingBytesChunk);
733 self.push_bytes_input_chunk(input.as_ref());
734 if !self.suspended.get() {
735 self.parse_sync(cx);
736 }
737 }
738
739 fn tokenize<F>(&self, cx: &mut JSContext, feed: F)
740 where
741 F: Fn(&mut JSContext, &Tokenizer) -> TokenizerResult<DomRoot<HTMLScriptElement>>,
742 {
743 loop {
744 assert!(!self.suspended.get());
745 assert!(!self.aborted.get());
746
747 self.document.window().reflow_if_reflow_timer_expired(cx);
748 let script = match feed(cx, &self.tokenizer) {
749 TokenizerResult::Done => return,
750 TokenizerResult::EncodingIndicator(_) => continue,
751 TokenizerResult::Script(script) => script,
752 };
753
754 if is_execution_stack_empty() {
761 self.document.window().perform_a_microtask_checkpoint(cx);
762 }
763
764 let script_nesting_level = self.script_nesting_level.get();
765
766 self.script_nesting_level.set(script_nesting_level + 1);
767 script.set_initial_script_text();
768 let introduction_type_override =
769 (script_nesting_level > 0).then_some(IntroductionType::INJECTED_SCRIPT);
770 script.prepare(cx, introduction_type_override);
771 self.script_nesting_level.set(script_nesting_level);
772
773 if self.document.has_pending_parsing_blocking_script() {
774 self.suspended.set(true);
775 return;
776 }
777 if self.aborted.get() {
778 return;
779 }
780 }
781 }
782
783 pub(crate) fn has_aborted(&self) -> bool {
785 self.aborted.get()
786 }
787
788 pub(crate) fn has_stopped(&self) -> bool {
790 self.stopped.get()
791 }
792
793 fn finish(&self, cx: &mut JSContext) {
795 assert!(!self.suspended.get());
796 assert!(self.last_chunk_received.get());
797 assert!(self.script_input.is_empty());
798 assert!(self.network_input.is_empty());
799 assert!(self.network_decoder.borrow().is_finished());
800
801 self.stopped.set(true);
802
803 self.tokenizer.end(cx);
808 self.document
810 .update_the_current_document_readiness(cx, DocumentReadyState::Interactive);
811 self.document
813 .set_current_parser(None, SetParserReason::FinishingParser);
814 self.document.start_the_end_loading_phase();
816 let url = self.tokenizer.url().clone();
817 self.document.finish_load(LoadType::PageSource(url), cx);
818
819 #[cfg(feature = "devtools")]
821 if let Some(content_for_devtools) = self
822 .content_for_devtools
823 .as_ref()
824 .map(|content| content.take())
825 {
826 let global = self.document.global();
827 let chan = global.devtools_chan().expect("Guaranteed by new");
828 let pipeline_id = self.document.global().pipeline_id();
829 let _ = chan.send(ScriptToDevtoolsControlMsg::UpdateSourceContent(
830 pipeline_id,
831 content_for_devtools,
832 ));
833 }
834 }
835}
836
837struct FragmentParsingResult<I>
838where
839 I: Iterator<Item = DomRoot<Node>>,
840{
841 inner: I,
842}
843
844impl<I> Iterator for FragmentParsingResult<I>
845where
846 I: Iterator<Item = DomRoot<Node>>,
847{
848 type Item = DomRoot<Node>;
849
850 #[expect(unsafe_code)]
851 fn next(&mut self) -> Option<DomRoot<Node>> {
852 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
853 let cx = &mut cx;
854
855 let next = self.inner.next()?;
856 next.remove_self(cx);
857 Some(next)
858 }
859
860 fn size_hint(&self) -> (usize, Option<usize>) {
861 self.inner.size_hint()
862 }
863}
864
865#[derive(JSTraceable, MallocSizeOf, PartialEq)]
866enum ParserKind {
867 Normal,
868 ScriptCreated,
869}
870
871#[derive(JSTraceable, MallocSizeOf)]
872#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
873enum Tokenizer {
874 Html(self::html::Tokenizer),
875 AsyncHtml(self::async_html::Tokenizer),
876 Xml(self::xml::Tokenizer),
877}
878
879impl Tokenizer {
880 fn feed(
881 &self,
882 cx: &mut JSContext,
883 input: &BufferQueue,
884 profiler_chan: ProfilerChan,
885 profiler_metadata: TimerMetadata,
886 ) -> TokenizerResult<DomRoot<HTMLScriptElement>> {
887 match *self {
888 Tokenizer::Html(ref tokenizer) => time_profile!(
889 ProfilerCategory::ScriptParseHTML,
890 Some(profiler_metadata),
891 profiler_chan,
892 || tokenizer.feed(input),
893 ),
894 Tokenizer::AsyncHtml(ref tokenizer) => time_profile!(
895 ProfilerCategory::ScriptParseHTML,
896 Some(profiler_metadata),
897 profiler_chan,
898 || tokenizer.feed(input, cx),
899 ),
900 Tokenizer::Xml(ref tokenizer) => time_profile!(
901 ProfilerCategory::ScriptParseXML,
902 Some(profiler_metadata),
903 profiler_chan,
904 || tokenizer.feed(input),
905 ),
906 }
907 }
908
909 fn end(&self, cx: &mut JSContext) {
910 match *self {
911 Tokenizer::Html(ref tokenizer) => tokenizer.end(),
912 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.end(cx),
913 Tokenizer::Xml(ref tokenizer) => tokenizer.end(),
914 }
915 }
916
917 fn url(&self) -> &ServoUrl {
918 match *self {
919 Tokenizer::Html(ref tokenizer) => tokenizer.url(),
920 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.url(),
921 Tokenizer::Xml(ref tokenizer) => tokenizer.url(),
922 }
923 }
924
925 fn set_plaintext_state(&self) {
926 match *self {
927 Tokenizer::Html(ref tokenizer) => tokenizer.set_plaintext_state(),
928 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.set_plaintext_state(),
929 Tokenizer::Xml(_) => unimplemented!(),
930 }
931 }
932
933 fn get_current_line(&self) -> u32 {
934 match *self {
935 Tokenizer::Html(ref tokenizer) => tokenizer.get_current_line(),
936 Tokenizer::AsyncHtml(ref tokenizer) => tokenizer.get_current_line(),
937 Tokenizer::Xml(ref tokenizer) => tokenizer.get_current_line(),
938 }
939 }
940}
941
942struct NavigationParams {
946 policy_container: Arc<PolicyContainer>,
948 content_type: Option<Mime>,
950 link_headers: Vec<LinkHeader>,
952 redirect_taint: RedirectTaint,
954 final_sandboxing_flag_set: SandboxingFlagSet,
956 resource_header: Vec<u8>,
958 about_base_url: Option<ServoUrl>,
960 iframe_element_referrer_policy: ReferrerPolicy,
962}
963
964pub(crate) struct ParserContext {
967 parser: Option<Trusted<ServoParser>>,
969 is_synthesized_document: bool,
971 has_loaded_document: bool,
973 webview_id: WebViewId,
975 pipeline_id: PipelineId,
977 url: ServoUrl,
979 pushed_entry_index: Option<usize>,
981 navigation_params: NavigationParams,
983 parent_info: Option<PipelineId>,
985 target_snapshot_params: TargetSnapshotParams,
986 source_origin: Option<MutableOrigin>,
992 document: Option<Trusted<Document>>,
993}
994
995impl ParserContext {
996 pub(crate) fn new(
997 webview_id: WebViewId,
998 pipeline_id: PipelineId,
999 url: ServoUrl,
1000 creation_sandboxing_flag_set: SandboxingFlagSet,
1001 parent_info: Option<PipelineId>,
1002 target_snapshot_params: TargetSnapshotParams,
1003 source_origin: Option<MutableOrigin>,
1004 ) -> ParserContext {
1005 ParserContext {
1006 parser: None,
1007 is_synthesized_document: false,
1008 has_loaded_document: false,
1009 webview_id,
1010 pipeline_id,
1011 url,
1012 parent_info,
1013 pushed_entry_index: None,
1014 navigation_params: NavigationParams {
1015 policy_container: Default::default(),
1016 content_type: None,
1017 link_headers: vec![],
1018 final_sandboxing_flag_set: creation_sandboxing_flag_set,
1019 resource_header: vec![],
1020 about_base_url: Default::default(),
1021 iframe_element_referrer_policy: Default::default(),
1022 redirect_taint: Default::default(),
1023 },
1024 target_snapshot_params,
1025 source_origin,
1026 document: None,
1027 }
1028 }
1029
1030 pub(crate) fn set_policy_container(&mut self, policy_container: Option<Arc<PolicyContainer>>) {
1031 let Some(policy_container) = policy_container else {
1032 return;
1033 };
1034 self.navigation_params.policy_container = policy_container;
1035 }
1036
1037 pub(crate) fn set_about_base_url(&mut self, about_base_url: Option<ServoUrl>) {
1038 self.navigation_params.about_base_url = about_base_url;
1039 }
1040
1041 pub(crate) fn get_document(&self) -> Option<DomRoot<Document>> {
1042 self.parser
1043 .as_ref()
1044 .map(|parser| parser.root().document.as_rooted())
1045 }
1046
1047 pub(crate) fn parent_info(&self) -> Option<PipelineId> {
1048 self.parent_info
1049 }
1050
1051 fn create_policy_container_from_fetch_response(metadata: &Metadata) -> PolicyContainer {
1053 PolicyContainer {
1060 csp_list: parse_csp_list_from_metadata(&metadata.headers),
1062 embedder_policy: Default::default(),
1066 referrer_policy: ReferrerPolicy::parse_header_for_response(&metadata.headers),
1068 }
1069 }
1070
1071 fn initialize_document_object(&self, cx: &mut JSContext, document: &Document) {
1073 document.set_policy_container(self.navigation_params.policy_container.clone());
1076 document.set_active_sandboxing_flag_set(self.navigation_params.final_sandboxing_flag_set);
1078 document.set_document_readiness_to_loading_for_initialization();
1080 document.set_about_base_url(self.navigation_params.about_base_url.clone());
1082 document.set_internal_ancestor_origin_objects_list(
1086 document.internal_ancestor_origin_objects_list_creation_steps(
1087 &self.navigation_params.iframe_element_referrer_policy,
1088 ),
1089 );
1090 document.set_ancestor_origins_list(&document.ancestor_origins_list_creation_steps(cx));
1093 process_link_headers(
1095 &self.navigation_params.link_headers,
1096 document,
1097 LinkProcessingPhase::PreMedia,
1098 );
1099 }
1100
1101 fn process_link_headers_in_media_phase_with_task(&mut self, document: &Document) {
1103 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1107 if !link_headers.is_empty() {
1108 let window = document.window();
1109 let document = Trusted::new(document);
1110 window
1111 .upcast::<GlobalScope>()
1112 .task_manager()
1113 .networking_task_source()
1114 .queue(task!(process_link_headers_task: move || {
1115 process_link_headers(&link_headers, &document.root(), LinkProcessingPhase::Media);
1116 }));
1117 }
1118 }
1119
1120 fn load_document(
1122 &mut self,
1123 cx: &mut JSContext,
1124 parser: Option<&ServoParser>,
1125 document: &Document,
1126 ) {
1127 assert!(!self.has_loaded_document);
1128 self.has_loaded_document = true;
1129 let content_type = &self.navigation_params.content_type;
1131 let mime_type = MimeClassifier::default().classify(
1132 LoadContext::Browsing,
1133 NoSniffFlag::Off,
1134 ApacheBugFlag::from_content_type(content_type.as_ref()),
1135 content_type,
1136 &self.navigation_params.resource_header,
1137 );
1138 let Some(media_type) = MimeClassifier::get_media_type(&mime_type) else {
1142 let page = format!(
1143 "<html><body><p>Unknown content type ({}).</p></body></html>",
1144 mime_type,
1145 );
1146 self.load_inline_unknown_content(
1147 cx,
1148 parser.expect("Must have a parser for unknown content"),
1149 page,
1150 );
1151 return;
1152 };
1153 match media_type {
1154 MediaType::Html => self.load_html_document(cx, document),
1156 MediaType::Xml => self.load_xml_document(cx, document),
1158 MediaType::JavaScript | MediaType::Text | MediaType::Css => {
1160 self.load_text_document(cx, parser.expect("Must have a parser for text"))
1161 },
1162 MediaType::Json => {
1164 self.load_json_document(cx, parser.expect("Must have a parser for JSON"))
1165 },
1166 MediaType::Image | MediaType::AudioVideo => {
1168 self.load_media_document(
1169 cx,
1170 parser.expect("Must have a parser for media"),
1171 media_type,
1172 &mime_type,
1173 );
1174 return;
1175 },
1176 MediaType::Font => {
1177 let page = format!(
1178 "<html><body><p>Unable to load font with content type ({}).</p></body></html>",
1179 mime_type,
1180 );
1181 self.load_inline_unknown_content(
1182 cx,
1183 parser.expect("Must have a parser for inline unknown"),
1184 page,
1185 );
1186 return;
1187 },
1188 };
1189
1190 if let Some(parser) = parser {
1191 parser.parse_bytes_chunk(
1192 cx,
1193 std::mem::take(&mut self.navigation_params.resource_header).as_ref(),
1194 );
1195 }
1196 }
1197
1198 fn load_html_document(&mut self, cx: &mut JSContext, document: &Document) {
1200 self.initialize_document_object(cx, document);
1203 if document.is_initial_about_blank() {
1205 populate_about_blank(cx, document);
1206 }
1207 self.process_link_headers_in_media_phase_with_task(document);
1211 }
1212
1213 fn load_xml_document(&mut self, cx: &mut JSContext, document: &Document) {
1215 self.initialize_document_object(cx, document);
1221 self.process_link_headers_in_media_phase_with_task(document);
1225 }
1226
1227 fn load_text_document(&mut self, cx: &mut JSContext, parser: &ServoParser) {
1229 self.initialize_document_object(cx, &parser.document);
1232 let page = "<pre>\n".into();
1239 parser.push_string_input_chunk(page);
1240 parser.parse_sync(cx);
1241 parser.tokenizer.set_plaintext_state();
1242 parser.document.set_quirks_mode(ServoQuirksMode::NoQuirks);
1244 self.process_link_headers_in_media_phase_with_task(&parser.document);
1248 }
1249
1250 fn load_media_document(
1252 &mut self,
1253 cx: &mut JSContext,
1254 parser: &ServoParser,
1255 media_type: MediaType,
1256 mime_type: &Mime,
1257 ) {
1258 self.initialize_document_object(cx, &parser.document);
1261 self.is_synthesized_document = true;
1263 parser.last_chunk_received.set(true);
1264 let page = "<html><body></body></html>".into();
1266 parser.push_string_input_chunk(page);
1267 parser.parse_sync(cx);
1268 parser.document.set_quirks_mode(ServoQuirksMode::NoQuirks);
1270
1271 let doc = &parser.document;
1272 let node = if media_type == MediaType::Image {
1275 let img = Element::create(
1276 cx,
1277 QualName::new(None, ns!(html), local_name!("img")),
1278 None,
1279 doc,
1280 ElementCreator::ParserCreated(1),
1281 CustomElementCreationMode::Asynchronous,
1282 None,
1283 );
1284 let img = DomRoot::downcast::<HTMLImageElement>(img).unwrap();
1285 img.SetSrc(cx, USVString(self.url.to_string()));
1286 DomRoot::upcast::<Node>(img)
1287 } else if mime_type.type_() == mime::AUDIO {
1288 let audio = Element::create(
1289 cx,
1290 QualName::new(None, ns!(html), local_name!("audio")),
1291 None,
1292 doc,
1293 ElementCreator::ParserCreated(1),
1294 CustomElementCreationMode::Asynchronous,
1295 None,
1296 );
1297 let audio = DomRoot::downcast::<HTMLMediaElement>(audio).unwrap();
1298 audio.SetControls(cx, true);
1299 audio.SetSrc(cx, USVString(self.url.to_string()));
1300 DomRoot::upcast::<Node>(audio)
1301 } else {
1302 let video = Element::create(
1303 cx,
1304 QualName::new(None, ns!(html), local_name!("video")),
1305 None,
1306 doc,
1307 ElementCreator::ParserCreated(1),
1308 CustomElementCreationMode::Asynchronous,
1309 None,
1310 );
1311 let video = DomRoot::downcast::<HTMLMediaElement>(video).unwrap();
1312 video.SetControls(cx, true);
1313 video.SetSrc(cx, USVString(self.url.to_string()));
1314 DomRoot::upcast::<Node>(video)
1315 };
1316 let doc_body = DomRoot::upcast::<Node>(doc.GetBody().unwrap());
1318 doc_body.AppendChild(cx, &node).expect("Appending failed");
1319 let link_headers = std::mem::take(&mut self.navigation_params.link_headers);
1321 process_link_headers(&link_headers, doc, LinkProcessingPhase::Media);
1322 }
1323
1324 fn load_json_document(&mut self, cx: &mut JSContext, parser: &ServoParser) {
1326 self.initialize_document_object(cx, &parser.document);
1327 parser.push_string_input_chunk(resources::read_string(Resource::JsonViewerHTML));
1328 parser.parse_sync(cx);
1329 parser.tokenizer.set_plaintext_state();
1330 self.process_link_headers_in_media_phase_with_task(&parser.document);
1331 }
1332
1333 fn load_inline_unknown_content(
1335 &mut self,
1336 cx: &mut JSContext,
1337 parser: &ServoParser,
1338 page: String,
1339 ) {
1340 self.is_synthesized_document = true;
1341 parser.document.mark_as_internal();
1342 parser.push_string_input_chunk(page);
1343 parser.last_chunk_received.set(true);
1345 parser.parse_sync(cx);
1346 }
1347
1348 fn submit_resource_timing(&mut self, cx: &mut JSContext) {
1350 let Some(parser) = self.parser.as_ref() else {
1351 return;
1352 };
1353 let parser = parser.root();
1354 if parser.aborted.get() {
1355 return;
1356 }
1357
1358 let document = &parser.document;
1359
1360 let performance_entry = PerformanceNavigationTiming::new(cx, &document.global(), document);
1361 self.pushed_entry_index = document
1362 .global()
1363 .performance(cx)
1364 .queue_entry(performance_entry.upcast::<PerformanceEntry>());
1365 }
1366
1367 fn finish_synchronous_load_for_initial_about_blank(
1368 &self,
1369 cx: &mut JSContext,
1370 document: &Document,
1371 ) {
1372 debug_assert_eq!(document.ReadyState(), DocumentReadyState::Complete);
1376
1377 document.set_current_parser(None, SetParserReason::AboutBlankComplete);
1378 document.finish_load(LoadType::PageSource(self.url.clone()), cx);
1379
1380 document.notify_embedder_of_load_completion();
1381 }
1382
1383 pub(crate) fn process_response(
1386 &mut self,
1387 script_thread: &ScriptThread,
1388 cx: &mut JSContext,
1389 meta_result: Result<FetchMetadata, NetworkError>,
1390 ) {
1391 let (metadata, mut error) = match meta_result {
1392 Ok(meta) => (Some(meta.into()), None),
1393 Err(error) => (
1394 match &error {
1396 NetworkError::LoadCancelled => {
1397 return;
1398 },
1399 _ => {
1400 let mut meta = Metadata::default(self.url.clone());
1401 let mime: Option<Mime> = "text/html".parse().ok();
1402 meta.set_content_type(mime.as_ref());
1403 Some(meta)
1404 },
1405 },
1406 Some(error),
1407 ),
1408 };
1409 let content_type: Option<Mime> = metadata
1410 .clone()
1411 .and_then(|meta| meta.content_type)
1412 .map(Serde::into_inner)
1413 .map(Into::into);
1414
1415 let (policy_container, endpoints_list, link_headers, content_language, redirect_taint) =
1420 metadata
1421 .as_ref()
1422 .map(|metadata| {
1423 (
1424 Self::create_policy_container_from_fetch_response(metadata),
1425 ReportingEndpoint::parse_reporting_endpoints_header(
1426 &self.url,
1427 &metadata.headers,
1428 ),
1429 extract_links_from_headers(&metadata.headers),
1430 metadata
1431 .headers
1432 .as_ref()
1433 .and_then(|headers| headers.get("Content-Language"))
1434 .and_then(|header| header.to_str().ok())
1435 .map(|string| string.to_owned()),
1436 metadata.redirect_taint,
1437 )
1438 })
1439 .unwrap_or_default();
1440
1441 let final_sandboxing_flag_set = policy_container
1445 .csp_list
1446 .as_ref()
1447 .and_then(|csp| csp.get_sandboxing_flag_set_for_document())
1448 .unwrap_or(SandboxingFlagSet::empty())
1449 .union(self.target_snapshot_params.sandboxing_flags);
1450
1451 let origin = determine_the_origin(
1455 metadata.as_ref().map(|metadata| &metadata.final_url),
1456 final_sandboxing_flag_set,
1457 self.source_origin.clone(),
1458 );
1459
1460 let Some(document) = script_thread.handle_page_headers_available(
1461 cx,
1462 self.webview_id,
1463 self.pipeline_id,
1464 metadata.as_ref(),
1465 origin.clone(),
1466 ) else {
1467 return;
1468 };
1469
1470 self.document = Some(Trusted::new(&*document));
1471
1472 if document
1473 .get_current_parser()
1474 .is_some_and(|parser| parser.aborted.get())
1475 {
1476 return;
1477 }
1478
1479 let mut realm = enter_auto_realm(cx, &*document);
1480 let cx = &mut realm;
1481 let window = document.window();
1482
1483 if
1486 policy_container.csp_list.should_navigation_response_to_navigation_request_be_blocked(
1493 cx,
1494 window,
1495 self.url.clone().into_url(),
1496 &origin.immutable().clone().into_url_origin(),
1497 )
1498 || !check_a_navigation_response_adherence_to_x_frame_options(
1506 window,
1507 policy_container.csp_list.as_ref(),
1508 &origin,
1509 metadata
1510 .as_ref()
1511 .and_then(|metadata| metadata.headers.as_ref()),
1512 ) {
1513 error = Some(NetworkError::ContentSecurityPolicy);
1517 document.make_document_unsalvageable();
1519 }
1524
1525 if let Some(endpoints) = endpoints_list {
1526 window.set_endpoints_list(endpoints);
1527 }
1528 document.set_default_language(content_language);
1532 if let Some(parser) = document.get_current_parser() {
1533 self.parser = Some(Trusted::new(&*parser));
1534 }
1535 self.navigation_params = NavigationParams {
1536 policy_container: Arc::new(policy_container),
1537 content_type,
1538 final_sandboxing_flag_set,
1539 link_headers,
1540 about_base_url: document.about_base_url(),
1541 resource_header: vec![],
1542 iframe_element_referrer_policy: self
1543 .target_snapshot_params
1544 .iframe_element_referrer_policy,
1545 redirect_taint,
1546 };
1547 self.submit_resource_timing(cx);
1548
1549 if let Some(error) = error {
1557 let page = match error {
1558 NetworkError::SslValidation(reason, bytes) => {
1559 let page = resources::read_string(Resource::BadCertHTML);
1560 let page = page.replace("${reason}", &reason);
1561 let encoded_bytes = general_purpose::STANDARD_NO_PAD.encode(bytes);
1562 let page = page.replace("${bytes}", encoded_bytes.as_str());
1563 page.replace("${secret}", &net_traits::PRIVILEGED_SECRET.to_string())
1564 },
1565 NetworkError::BlobURLStoreError(reason) |
1566 NetworkError::WebsocketConnectionFailure(reason) |
1567 NetworkError::HttpError(reason) |
1568 NetworkError::ResourceLoadError(reason) |
1569 NetworkError::MimeType(reason) => {
1570 let page = resources::read_string(Resource::NetErrorHTML);
1571 page.replace("${reason}", &reason)
1572 },
1573 NetworkError::Crash(details) => {
1574 let page = resources::read_string(Resource::CrashHTML);
1575 page.replace("${details}", &details)
1576 },
1577 NetworkError::UnsupportedScheme |
1578 NetworkError::CorsGeneral |
1579 NetworkError::CrossOriginResponse |
1580 NetworkError::CorsCredentials |
1581 NetworkError::CorsAllowMethods |
1582 NetworkError::CorsAllowHeaders |
1583 NetworkError::CorsMethod |
1584 NetworkError::CorsAuthorization |
1585 NetworkError::CorsHeaders |
1586 NetworkError::ConnectionFailure |
1587 NetworkError::RedirectError |
1588 NetworkError::TooManyRedirects |
1589 NetworkError::TooManyInFlightKeepAliveRequests |
1590 NetworkError::InvalidMethod |
1591 NetworkError::ContentSecurityPolicy |
1592 NetworkError::Nosniff |
1593 NetworkError::SubresourceIntegrity |
1594 NetworkError::MixedContent |
1595 NetworkError::CacheError |
1596 NetworkError::InvalidPort |
1597 NetworkError::LocalDirectoryError |
1598 NetworkError::PartialResponseToNonRangeRequestError |
1599 NetworkError::ProtocolHandlerSubstitutionError |
1600 NetworkError::DecompressionError => {
1601 let page = resources::read_string(Resource::NetErrorHTML);
1602 page.replace("${reason}", &format!("{:?}", error))
1603 },
1604 NetworkError::LoadCancelled => {
1605 return;
1607 },
1608 };
1609 let parser = document
1610 .get_current_parser()
1611 .expect("Must have a parser for errors");
1612 self.load_inline_unknown_content(cx, &parser, page);
1613 }
1614 }
1615
1616 pub(crate) fn process_response_chunk(&mut self, cx: &mut JSContext, payload: Bytes) {
1617 if self.is_synthesized_document {
1618 return;
1619 }
1620 let Some(parser) = self.parser.as_ref().map(|p| p.root()) else {
1621 return;
1622 };
1623 let Some(document) = self.document.as_ref().map(|document| document.root()) else {
1624 return;
1625 };
1626 if parser.aborted.get() {
1627 return;
1628 }
1629 if !self.has_loaded_document {
1630 self.navigation_params
1632 .resource_header
1633 .extend_from_slice(payload.as_ref());
1634 if self.navigation_params.resource_header.len() >= 1445 {
1636 self.load_document(cx, Some(&parser), &document);
1637 }
1638 } else {
1639 parser.parse_bytes_chunk(cx, payload.as_ref());
1640 }
1641 }
1642
1643 pub(crate) fn process_response_eof(
1647 mut self,
1648 cx: &mut JSContext,
1649 status: Result<(), NetworkError>,
1650 timing: ResourceFetchTiming,
1651 ) {
1652 let parser = self.parser.as_ref().map(|parser| parser.root());
1653 if parser.as_ref().is_some_and(|parser| parser.aborted.get()) ||
1654 self.is_synthesized_document
1655 {
1656 return;
1657 }
1658
1659 if let Err(error) = &status {
1660 debug!("Failed to load page URL {}, error: {error:?}", self.url);
1662 }
1663
1664 let Some(document) = self.document.as_ref().map(|document| document.root()) else {
1665 return;
1666 };
1667
1668 if !self.has_loaded_document {
1672 self.load_document(cx, parser.as_deref(), &document);
1673 }
1674
1675 let mut realm = enter_auto_realm(cx, &*document);
1676 let cx = &mut realm;
1677
1678 if status.is_ok() {
1679 let mut full_timing_info = timing;
1684
1685 let redirect_count =
1692 if self.navigation_params.redirect_taint == RedirectTaint::SameOrigin {
1693 full_timing_info.redirect_count
1694 } else {
1695 0
1697 };
1698
1699 full_timing_info.redirect_count = redirect_count;
1703 document.set_resource_fetch_timing(full_timing_info);
1704 }
1705
1706 if let Some(parser) = parser {
1707 parser.last_chunk_received.set(true);
1708 if !parser.suspended.get() {
1709 parser.parse_sync(cx);
1710 }
1711 }
1712
1713 if let Some(pushed_index) = self.pushed_entry_index {
1715 let performance_entry =
1716 PerformanceNavigationTiming::new(cx, &document.global(), &document);
1717 document
1718 .global()
1719 .performance(cx)
1720 .update_entry(pushed_index, performance_entry.upcast::<PerformanceEntry>());
1721 }
1722
1723 if document.is_initial_about_blank() {
1724 self.finish_synchronous_load_for_initial_about_blank(cx, &document);
1725 }
1726 }
1727}
1728
1729pub(crate) struct FragmentContext<'a> {
1730 pub(crate) context_elem: &'a Node,
1731 pub(crate) form_elem: Option<&'a Node>,
1732 pub(crate) context_element_allows_scripting: bool,
1733}
1734
1735#[cfg_attr(crown, expect(crown::unrooted_must_root))]
1737fn insert_an_element_at_the_adjusted_insertion_location(
1738 cx: &mut JSContext,
1739 node_to_insert: Dom<Node>,
1740 adjusted_insertion_location_parent: &Node,
1741 adjusted_insertion_location_child: Option<&Node>,
1742 parsing_algorithm: ParsingAlgorithm,
1743 custom_element_reaction_stack: &CustomElementReactionStack,
1744) {
1745 if Node::ensure_pre_insertion_validity(
1752 cx.no_gc(),
1753 &node_to_insert,
1754 adjusted_insertion_location_parent,
1755 adjusted_insertion_location_child,
1756 &[],
1757 )
1758 .is_err()
1759 {
1760 return;
1761 }
1762
1763 let element_in_non_fragment =
1767 parsing_algorithm != ParsingAlgorithm::Fragment && node_to_insert.is::<Element>();
1768 if element_in_non_fragment {
1769 custom_element_reaction_stack.push_new_element_queue();
1770 }
1771
1772 Node::insert(
1774 cx,
1775 &node_to_insert,
1776 adjusted_insertion_location_parent,
1777 adjusted_insertion_location_child,
1778 SuppressObserver::Unsuppressed,
1779 );
1780
1781 if element_in_non_fragment {
1787 custom_element_reaction_stack.pop_current_element_queue(cx);
1788 }
1789}
1790
1791#[cfg_attr(crown, expect(crown::unrooted_must_root))]
1792fn insert(
1793 cx: &mut JSContext,
1794 parent: &Node,
1795 reference_child: Option<&Node>,
1796 child: NodeOrText<Dom<Node>>,
1797 parsing_algorithm: ParsingAlgorithm,
1798 custom_element_reaction_stack: &CustomElementReactionStack,
1799) {
1800 match child {
1801 NodeOrText::AppendNode(node) => {
1802 insert_an_element_at_the_adjusted_insertion_location(
1808 cx,
1809 node,
1810 parent,
1811 reference_child,
1812 parsing_algorithm,
1813 custom_element_reaction_stack,
1814 );
1815 },
1816 NodeOrText::AppendText(t) => {
1817 let text = reference_child
1819 .and_then(Node::GetPreviousSibling)
1820 .or_else(|| parent.GetLastChild())
1821 .and_then(DomRoot::downcast::<Text>);
1822
1823 if let Some(text) = text {
1824 text.upcast::<CharacterData>().append_data(cx, &t);
1825 } else {
1826 let text = Text::new(cx, String::from(t).into(), &parent.owner_doc());
1827 parent
1828 .InsertBefore(cx, text.upcast(), reference_child)
1829 .unwrap();
1830 }
1831 },
1832 }
1833}
1834
1835#[derive(JSTraceable, MallocSizeOf)]
1836#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1837pub(crate) struct Sink {
1838 #[no_trace]
1839 base_url: ServoUrl,
1840 document: Dom<Document>,
1841 current_line: Cell<u64>,
1842 script: MutNullableDom<HTMLScriptElement>,
1843 parsing_algorithm: ParsingAlgorithm,
1844 #[conditional_malloc_size_of]
1845 custom_element_reaction_stack: Rc<CustomElementReactionStack>,
1846}
1847
1848impl Sink {
1849 fn same_tree(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1850 let x = x.downcast::<Element>().expect("Element node expected");
1851 let y = y.downcast::<Element>().expect("Element node expected");
1852
1853 x.is_in_same_home_subtree(y)
1854 }
1855
1856 fn has_parent_node(&self, node: &Dom<Node>) -> bool {
1857 node.GetParentNode().is_some()
1858 }
1859}
1860
1861impl TreeSink for Sink {
1862 type Output = Self;
1863
1864 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
1865 fn finish(self) -> Self {
1866 self
1867 }
1868
1869 type Handle = Dom<Node>;
1870 type ElemName<'a>
1871 = ExpandedName<'a>
1872 where
1873 Self: 'a;
1874
1875 fn get_document(&self) -> Dom<Node> {
1876 Dom::from_ref(self.document.upcast())
1877 }
1878
1879 #[expect(unsafe_code)]
1880 fn get_template_contents(&self, target: &Dom<Node>) -> Dom<Node> {
1881 let mut cx = unsafe { temp_cx() };
1883 let cx = &mut cx;
1884 let template = target
1885 .downcast::<HTMLTemplateElement>()
1886 .expect("tried to get template contents of non-HTMLTemplateElement in HTML parsing");
1887 Dom::from_ref(template.Content(cx).upcast())
1888 }
1889
1890 fn same_node(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
1891 x == y
1892 }
1893
1894 fn elem_name<'a>(&self, target: &'a Dom<Node>) -> ExpandedName<'a> {
1895 let elem = target
1896 .downcast::<Element>()
1897 .expect("tried to get name of non-Element in HTML parsing");
1898 ExpandedName {
1899 ns: elem.namespace(),
1900 local: elem.local_name(),
1901 }
1902 }
1903
1904 #[expect(unsafe_code)]
1905 fn create_element(
1906 &self,
1907 name: QualName,
1908 attrs: Vec<Attribute>,
1909 flags: ElementFlags,
1910 ) -> Dom<Node> {
1911 let mut cx = unsafe { temp_cx() };
1913 let cx = &mut cx;
1914 let attrs = attrs
1915 .into_iter()
1916 .map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
1917 .collect();
1918 let parsing_algorithm = if flags.template {
1919 ParsingAlgorithm::Fragment
1920 } else {
1921 self.parsing_algorithm
1922 };
1923 let element = create_element_for_token(
1924 cx,
1925 name,
1926 attrs,
1927 &self.document,
1928 ElementCreator::ParserCreated(self.current_line.get()),
1929 parsing_algorithm,
1930 &self.custom_element_reaction_stack,
1931 flags.had_duplicate_attributes,
1932 );
1933 Dom::from_ref(element.upcast())
1934 }
1935
1936 #[expect(unsafe_code)]
1937 fn create_comment(&self, text: StrTendril) -> Dom<Node> {
1938 let mut cx = unsafe { temp_cx() };
1940 let cx = &mut cx;
1941 let comment = Comment::new(
1942 cx,
1943 DOMString::from(String::from(text)),
1944 &self.document,
1945 None,
1946 );
1947 Dom::from_ref(comment.upcast())
1948 }
1949
1950 #[expect(unsafe_code)]
1951 fn create_pi(&self, target: StrTendril, data: StrTendril) -> Dom<Node> {
1952 let mut cx = unsafe { temp_cx() };
1954 let cx = &mut cx;
1955 let doc = &*self.document;
1956 let pi = ProcessingInstruction::new(
1957 cx,
1958 DOMString::from(String::from(target)),
1959 DOMString::from(String::from(data)),
1960 doc,
1961 );
1962 Dom::from_ref(pi.upcast())
1963 }
1964
1965 #[expect(unsafe_code)]
1966 fn associate_with_form(
1967 &self,
1968 target: &Dom<Node>,
1969 form: &Dom<Node>,
1970 nodes: (&Dom<Node>, Option<&Dom<Node>>),
1971 ) {
1972 let mut cx = unsafe { temp_cx() };
1974 let cx = &mut cx;
1975 let (element, prev_element) = nodes;
1976 let tree_node = prev_element.map_or(element, |prev| {
1977 if self.has_parent_node(element) {
1978 element
1979 } else {
1980 prev
1981 }
1982 });
1983 if !self.same_tree(tree_node, form) {
1984 return;
1985 }
1986
1987 let node = target;
1988 let form = DomRoot::downcast::<HTMLFormElement>(DomRoot::from_ref(&**form))
1989 .expect("Owner must be a form element");
1990
1991 let elem = node.downcast::<Element>();
1992 let control = elem.and_then(|e| e.as_maybe_form_control());
1993
1994 if let Some(control) = control {
1995 control.set_form_owner_from_parser(cx, &form);
1996 }
1997 }
1998
1999 #[expect(unsafe_code)]
2000 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
2001 fn append_before_sibling(&self, sibling: &Dom<Node>, new_node: NodeOrText<Dom<Node>>) {
2002 let mut cx = unsafe { temp_cx() };
2004 let cx = &mut cx;
2005
2006 let parent = sibling
2007 .GetParentNode()
2008 .expect("append_before_sibling called on node without parent");
2009
2010 insert(
2011 cx,
2012 &parent,
2013 Some(sibling),
2014 new_node,
2015 self.parsing_algorithm,
2016 &self.custom_element_reaction_stack,
2017 );
2018 }
2019
2020 fn parse_error(&self, msg: Cow<'static, str>) {
2021 debug!("Parse error: {}", msg);
2022 }
2023
2024 fn set_quirks_mode(&self, mode: QuirksMode) {
2025 let mode = match mode {
2026 QuirksMode::Quirks => ServoQuirksMode::Quirks,
2027 QuirksMode::LimitedQuirks => ServoQuirksMode::LimitedQuirks,
2028 QuirksMode::NoQuirks => ServoQuirksMode::NoQuirks,
2029 };
2030 self.document.set_quirks_mode(mode);
2031 }
2032
2033 #[expect(unsafe_code)]
2034 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
2035 fn append(&self, parent: &Dom<Node>, child: NodeOrText<Dom<Node>>) {
2036 let mut cx = unsafe { temp_cx() };
2038 let cx = &mut cx;
2039
2040 insert(
2041 cx,
2042 parent,
2043 None,
2044 child,
2045 self.parsing_algorithm,
2046 &self.custom_element_reaction_stack,
2047 );
2048 }
2049
2050 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
2051 fn append_based_on_parent_node(
2052 &self,
2053 elem: &Dom<Node>,
2054 prev_elem: &Dom<Node>,
2055 child: NodeOrText<Dom<Node>>,
2056 ) {
2057 if self.has_parent_node(elem) {
2058 self.append_before_sibling(elem, child);
2059 } else {
2060 self.append(prev_elem, child);
2061 }
2062 }
2063
2064 #[expect(unsafe_code)]
2065 fn append_doctype_to_document(
2066 &self,
2067 name: StrTendril,
2068 public_id: StrTendril,
2069 system_id: StrTendril,
2070 ) {
2071 let mut cx = unsafe { temp_cx() };
2073 let cx = &mut cx;
2074
2075 let doc = &*self.document;
2076 let doctype = DocumentType::new(
2077 cx,
2078 DOMString::from(String::from(name)),
2079 Some(DOMString::from(String::from(public_id))),
2080 Some(DOMString::from(String::from(system_id))),
2081 doc,
2082 );
2083 doc.upcast::<Node>()
2084 .AppendChild(cx, doctype.upcast())
2085 .expect("Appending failed");
2086 }
2087
2088 #[expect(unsafe_code)]
2089 fn add_attrs_if_missing(&self, target: &Dom<Node>, attrs: Vec<Attribute>) {
2090 let mut cx = unsafe { temp_cx() };
2092 let cx = &mut cx;
2093
2094 let elem = target
2095 .downcast::<Element>()
2096 .expect("tried to set attrs on non-Element in HTML parsing");
2097 for attr in attrs {
2098 elem.set_attribute_from_parser(
2099 cx,
2100 attr.name,
2101 DOMString::from(String::from(attr.value)),
2102 );
2103 }
2104 }
2105
2106 #[expect(unsafe_code)]
2107 fn remove_from_parent(&self, target: &Dom<Node>) {
2108 let mut cx = unsafe { temp_cx() };
2110 let cx = &mut cx;
2111
2112 if let Some(ref parent) = target.GetParentNode() {
2113 parent.RemoveChild(cx, target).unwrap();
2114 }
2115 }
2116
2117 fn mark_script_already_started(&self, node: &Dom<Node>) {
2118 let script = node.downcast::<HTMLScriptElement>();
2119 if let Some(script) = script {
2120 script.set_already_started(true)
2121 }
2122 }
2123
2124 #[expect(unsafe_code)]
2125 fn reparent_children(&self, node: &Dom<Node>, new_parent: &Dom<Node>) {
2126 let mut cx = unsafe { temp_cx() };
2128 let cx = &mut cx;
2129
2130 while let Some(ref child) = node.GetFirstChild() {
2131 new_parent.AppendChild(cx, child).unwrap();
2132 }
2133 }
2134
2135 fn is_mathml_annotation_xml_integration_point(&self, handle: &Dom<Node>) -> bool {
2138 let elem = handle.downcast::<Element>().unwrap();
2139 elem.get_attribute_string_value(&local_name!("encoding"))
2140 .is_some_and(|value| {
2141 value.eq_ignore_ascii_case("text/html") ||
2142 value.eq_ignore_ascii_case("application/xhtml+xml")
2143 })
2144 }
2145
2146 fn set_current_line(&self, line_number: u64) {
2147 self.current_line.set(line_number);
2148 }
2149
2150 #[expect(unsafe_code)]
2151 fn pop(&self, node: &Dom<Node>) {
2152 let mut cx = unsafe { temp_cx() };
2154 let cx = &mut cx;
2155
2156 let node = DomRoot::from_ref(&**node);
2157 vtable_for(&node).pop(cx);
2158 }
2159
2160 fn allow_declarative_shadow_roots(&self, intended_parent: &Dom<Node>) -> bool {
2161 intended_parent.owner_doc().allow_declarative_shadow_roots()
2162 }
2163
2164 #[expect(unsafe_code)]
2168 fn attach_declarative_shadow(
2169 &self,
2170 host: &Dom<Node>,
2171 template: &Dom<Node>,
2172 attributes: &[Attribute],
2173 ) -> bool {
2174 let mut cx = unsafe { temp_cx() };
2176 let cx = &mut cx;
2177
2178 attach_declarative_shadow_inner(cx, host, template, attributes)
2179 }
2180
2181 #[expect(unsafe_code)]
2182 fn maybe_clone_an_option_into_selectedcontent(&self, option: &Self::Handle) {
2183 let mut cx = unsafe { temp_cx() };
2185 let cx = &mut cx;
2186
2187 let Some(option) = option.downcast::<HTMLOptionElement>() else {
2188 if cfg!(debug_assertions) {
2189 unreachable!();
2190 }
2191 log::error!(
2192 "Received non-option element in maybe_clone_an_option_into_selectedcontent"
2193 );
2194 return;
2195 };
2196
2197 option.maybe_clone_an_option_into_selectedcontent(cx)
2198 }
2199}
2200
2201#[expect(clippy::too_many_arguments)]
2203fn create_element_for_token(
2204 cx: &mut JSContext,
2205 name: QualName,
2206 attrs: Vec<ElementAttribute>,
2207 document: &Document,
2208 creator: ElementCreator,
2209 parsing_algorithm: ParsingAlgorithm,
2210 custom_element_reaction_stack: &CustomElementReactionStack,
2211 had_duplicate_attributes: bool,
2212) -> DomRoot<Element> {
2213 let is = attrs
2231 .iter()
2232 .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
2233 .map(|attr| LocalName::from(&attr.value));
2234
2235 let definition = CustomElementRegistry::lookup_custom_element_definition(
2241 document.custom_element_registry().as_deref(),
2242 &name.ns,
2243 &name.local,
2244 is.as_ref(),
2245 );
2246
2247 let will_execute_script =
2250 definition.is_some() && parsing_algorithm != ParsingAlgorithm::Fragment;
2251
2252 if will_execute_script {
2254 document.increment_throw_on_dynamic_markup_insertion_counter();
2256 if is_execution_stack_empty() {
2259 document.window().perform_a_microtask_checkpoint(cx);
2260 }
2261 custom_element_reaction_stack.push_new_element_queue()
2264 }
2265
2266 let creation_mode = if will_execute_script {
2269 CustomElementCreationMode::Synchronous
2270 } else {
2271 CustomElementCreationMode::Asynchronous
2272 };
2273 let element = Element::create(cx, name, is, document, creator, creation_mode, None);
2274
2275 element.attrs().reserve_exact(attrs.len());
2277 for attr in attrs {
2278 element.set_attribute_from_parser(cx, attr.name, attr.value);
2279 }
2280
2281 if had_duplicate_attributes {
2284 element.set_had_duplicate_attributes(cx.no_gc());
2285 }
2286
2287 if will_execute_script {
2289 custom_element_reaction_stack.pop_current_element_queue(cx);
2294 document.decrement_throw_on_dynamic_markup_insertion_counter();
2296 }
2297
2298 if let Some(html_element) = element.downcast::<HTMLElement>() &&
2308 element.is_resettable() &&
2309 !html_element.is_form_associated_custom_element()
2310 {
2311 element.reset(cx);
2312 }
2313
2314 element
2324}
2325
2326fn attach_declarative_shadow_inner(
2327 cx: &mut JSContext,
2328 host: &Node,
2329 template: &Node,
2330 attributes: &[Attribute],
2331) -> bool {
2332 let host_element = host.downcast::<Element>().unwrap();
2333
2334 if host_element.shadow_root().is_some() {
2335 return false;
2336 }
2337
2338 let template_element = template.downcast::<HTMLTemplateElement>().unwrap();
2339
2340 let mut shadow_root_mode = ShadowRootMode::Open;
2350 let mut slot_assignment_mode = SlotAssignmentMode::Named;
2351 let mut clonable = false;
2352 let mut delegatesfocus = false;
2353 let mut serializable = false;
2354
2355 attributes
2356 .iter()
2357 .for_each(|attr: &Attribute| match attr.name.local {
2358 local_name!("shadowrootmode") => {
2359 if attr.value.eq_ignore_ascii_case("open") {
2360 shadow_root_mode = ShadowRootMode::Open;
2361 } else if attr.value.eq_ignore_ascii_case("closed") {
2362 shadow_root_mode = ShadowRootMode::Closed;
2363 } else {
2364 unreachable!("shadowrootmode value is not open nor closed");
2365 }
2366 },
2367 local_name!("shadowrootclonable") => {
2368 clonable = true;
2369 },
2370 local_name!("shadowrootdelegatesfocus") => {
2371 delegatesfocus = true;
2372 },
2373 local_name!("shadowrootserializable") => {
2374 serializable = true;
2375 },
2376 local_name!("shadowrootslotassignment") => {
2377 if attr.value.eq_ignore_ascii_case("manual") {
2378 slot_assignment_mode = SlotAssignmentMode::Manual;
2379 }
2380 },
2381 _ => {},
2382 });
2383
2384 match host_element.attach_shadow(
2387 cx,
2388 IsUserAgentWidget::No,
2389 shadow_root_mode,
2390 clonable,
2391 serializable,
2392 delegatesfocus,
2393 slot_assignment_mode,
2394 ) {
2395 Ok(shadow_root) => {
2396 shadow_root.set_declarative(true);
2398
2399 let shadow = shadow_root.upcast::<DocumentFragment>();
2401 template_element.set_contents(Some(shadow));
2402
2403 shadow_root.set_available_to_element_internals(true);
2405
2406 true
2407 },
2408 Err(_) => false,
2409 }
2410}
2411
2412fn populate_about_blank(cx: &mut JSContext, document: &Document) {
2414 let mut create_html_element = |name| {
2415 create_element(
2416 cx,
2417 QualName::new(None, ns!(html), name),
2418 None,
2419 document,
2420 ElementCreator::ParserCreated(0),
2421 CustomElementCreationMode::Synchronous,
2422 None,
2423 )
2424 };
2425 let html = create_html_element(local_name!("html"));
2427 let head = create_html_element(local_name!("head"));
2429 let body = create_html_element(local_name!("body"));
2431 let _ = document.upcast::<Node>().AppendChild(cx, html.upcast());
2433 let _ = html.upcast::<Node>().AppendChild(cx, head.upcast());
2435 let _ = html.upcast::<Node>().AppendChild(cx, body.upcast());
2437}
2438
2439impl Document {
2440 fn internal_ancestor_origin_objects_list_creation_steps(
2442 &self,
2443 referrer_policy: &ReferrerPolicy,
2444 ) -> Vec<ImmutableOrigin> {
2445 let mut output = vec![];
2447 let window_proxy = self.window().window_proxy();
2451 let Some((parent_origin, ancestor_origins)) =
2452 window_proxy.parent_origin_and_internal_ancestor_origin_objects_list()
2453 else {
2454 return output;
2456 };
2457 let mut masked =
2459 *referrer_policy == ReferrerPolicy::NoReferrer ||
2461 (*referrer_policy == ReferrerPolicy::SameOrigin && !parent_origin.same_origin(&self.origin()));
2464 if masked {
2466 output.push(ImmutableOrigin::new_opaque());
2467 } else {
2468 output.push(parent_origin.immutable().clone());
2470 }
2471 for ancestor_origin in ancestor_origins {
2473 if masked && ancestor_origin.same_origin(&parent_origin) {
2476 output.push(ImmutableOrigin::new_opaque());
2477 continue;
2478 }
2479 output.push(ancestor_origin.clone());
2481 masked = false;
2482 }
2483 output
2485 }
2486
2487 fn ancestor_origins_list_creation_steps(&self, cx: &mut JSContext) -> DomRoot<DOMStringList> {
2489 let ancestor_origins = self.internal_ancestor_origin_objects_list();
2492 let ancestor_origins = ancestor_origins
2493 .as_ref()
2494 .expect("Must always have initialized ancestor origin objects list");
2495 let mut output = Vec::with_capacity(ancestor_origins.len());
2497 for origin in ancestor_origins {
2499 output.push(origin.ascii_serialization().into());
2501 }
2502 DOMStringList::new(cx, &self.global(), output)
2504 }
2505}