1use std::cell::Cell;
5use std::ffi::CStr;
6use std::fs::read_to_string;
7use std::path::PathBuf;
8use std::rc::Rc;
9
10use base::id::{PipelineId, WebViewId};
11use dom_struct::dom_struct;
12use encoding_rs::Encoding;
13use html5ever::{LocalName, Prefix, local_name, ns};
14use js::jsval::UndefinedValue;
15use js::rust::{HandleObject, Stencil};
16use net_traits::http_status::HttpStatus;
17use net_traits::policy_container::PolicyContainer;
18use net_traits::request::{
19 CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata,
20 RequestBuilder, RequestId,
21};
22use net_traits::{
23 FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming,
24 ResourceTimingType,
25};
26use servo_url::{ImmutableOrigin, ServoUrl};
27use style::attr::AttrValue;
28use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
29use stylo_atoms::Atom;
30use uuid::Uuid;
31
32use crate::document_loader::LoadType;
33use crate::dom::attr::Attr;
34use crate::dom::bindings::cell::DomRefCell;
35use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
37use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
38use crate::dom::bindings::codegen::UnionTypes::{
39 TrustedScriptOrString, TrustedScriptURLOrUSVString,
40};
41use crate::dom::bindings::error::{Error, Fallible};
42use crate::dom::bindings::inheritance::Castable;
43use crate::dom::bindings::refcounted::Trusted;
44use crate::dom::bindings::reflector::DomGlobal;
45use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
46use crate::dom::bindings::settings_stack::AutoEntryScript;
47use crate::dom::bindings::str::DOMString;
48use crate::dom::bindings::trace::NoTrace;
49use crate::dom::csp::{CspReporting, GlobalCspReporting, InlineCheckType, Violation};
50use crate::dom::document::Document;
51use crate::dom::element::{
52 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
53 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
54 set_cross_origin_attribute,
55};
56use crate::dom::event::{Event, EventBubbles, EventCancelable};
57use crate::dom::globalscope::GlobalScope;
58use crate::dom::html::htmlelement::HTMLElement;
59use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeTraits};
60use crate::dom::performanceresourcetiming::InitiatorType;
61use crate::dom::trustedscript::TrustedScript;
62use crate::dom::trustedscripturl::TrustedScriptURL;
63use crate::dom::virtualmethods::VirtualMethods;
64use crate::dom::window::Window;
65use crate::fetch::create_a_potential_cors_request;
66use crate::network_listener::{self, PreInvoke, ResourceTimingListener};
67use crate::realms::enter_realm;
68use crate::script_module::{
69 ImportMap, ModuleOwner, ScriptFetchOptions, fetch_external_module_script,
70 fetch_inline_module_script, parse_an_import_map_string, register_import_map,
71};
72use crate::script_runtime::{CanGc, IntroductionType};
73use crate::unminify::{ScriptSource, unminify_js};
74
75impl ScriptSource for ScriptOrigin {
76 fn unminified_dir(&self) -> Option<String> {
77 self.unminified_dir.clone()
78 }
79
80 fn extract_bytes(&self) -> &[u8] {
81 match &self.code {
82 SourceCode::Text(text) => text.as_bytes(),
83 SourceCode::Compiled(compiled_source_code) => {
84 compiled_source_code.original_text.as_bytes()
85 },
86 }
87 }
88
89 fn rewrite_source(&mut self, source: Rc<DOMString>) {
90 self.code = SourceCode::Text(source);
91 }
92
93 fn url(&self) -> ServoUrl {
94 self.url.clone()
95 }
96
97 fn is_external(&self) -> bool {
98 self.external
99 }
100}
101
102#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
104pub(crate) struct ScriptId(#[no_trace] Uuid);
105
106#[dom_struct]
107pub(crate) struct HTMLScriptElement {
108 htmlelement: HTMLElement,
109
110 already_started: Cell<bool>,
112
113 parser_inserted: Cell<bool>,
115
116 non_blocking: Cell<bool>,
120
121 parser_document: Dom<Document>,
124
125 preparation_time_document: MutNullableDom<Document>,
128
129 line_number: u64,
131
132 #[ignore_malloc_size_of = "Defined in uuid"]
134 id: ScriptId,
135
136 script_text: DomRefCell<DOMString>,
138
139 #[no_trace]
142 introduction_type_override: Cell<Option<&'static CStr>>,
143}
144
145impl HTMLScriptElement {
146 fn new_inherited(
147 local_name: LocalName,
148 prefix: Option<Prefix>,
149 document: &Document,
150 creator: ElementCreator,
151 ) -> HTMLScriptElement {
152 HTMLScriptElement {
153 id: ScriptId(Uuid::new_v4()),
154 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
155 already_started: Cell::new(false),
156 parser_inserted: Cell::new(creator.is_parser_created()),
157 non_blocking: Cell::new(!creator.is_parser_created()),
158 parser_document: Dom::from_ref(document),
159 preparation_time_document: MutNullableDom::new(None),
160 line_number: creator.return_line_number(),
161 script_text: DomRefCell::new(DOMString::new()),
162 introduction_type_override: Cell::new(None),
163 }
164 }
165
166 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
167 pub(crate) fn new(
168 local_name: LocalName,
169 prefix: Option<Prefix>,
170 document: &Document,
171 proto: Option<HandleObject>,
172 creator: ElementCreator,
173 can_gc: CanGc,
174 ) -> DomRoot<HTMLScriptElement> {
175 Node::reflect_node_with_proto(
176 Box::new(HTMLScriptElement::new_inherited(
177 local_name, prefix, document, creator,
178 )),
179 document,
180 proto,
181 can_gc,
182 )
183 }
184
185 pub(crate) fn get_script_id(&self) -> ScriptId {
186 self.id
187 }
188}
189
190pub(crate) static SCRIPT_JS_MIMES: StaticStringVec = &[
193 "application/ecmascript",
194 "application/javascript",
195 "application/x-ecmascript",
196 "application/x-javascript",
197 "text/ecmascript",
198 "text/javascript",
199 "text/javascript1.0",
200 "text/javascript1.1",
201 "text/javascript1.2",
202 "text/javascript1.3",
203 "text/javascript1.4",
204 "text/javascript1.5",
205 "text/jscript",
206 "text/livescript",
207 "text/x-ecmascript",
208 "text/x-javascript",
209];
210
211#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
212pub(crate) enum ScriptType {
213 Classic,
214 Module,
215 ImportMap,
216}
217
218#[derive(JSTraceable, MallocSizeOf)]
219pub(crate) struct CompiledSourceCode {
220 #[ignore_malloc_size_of = "SM handles JS values"]
221 pub(crate) source_code: Stencil,
222 #[conditional_malloc_size_of = "Rc is hard"]
223 pub(crate) original_text: Rc<DOMString>,
224}
225
226#[derive(JSTraceable, MallocSizeOf)]
227pub(crate) enum SourceCode {
228 Text(#[conditional_malloc_size_of] Rc<DOMString>),
229 Compiled(CompiledSourceCode),
230}
231
232#[derive(JSTraceable, MallocSizeOf)]
233pub(crate) struct ScriptOrigin {
234 code: SourceCode,
235 #[no_trace]
236 url: ServoUrl,
237 external: bool,
238 fetch_options: ScriptFetchOptions,
239 type_: ScriptType,
240 unminified_dir: Option<String>,
241 import_map: Fallible<ImportMap>,
242}
243
244impl ScriptOrigin {
245 pub(crate) fn internal(
246 text: Rc<DOMString>,
247 url: ServoUrl,
248 fetch_options: ScriptFetchOptions,
249 type_: ScriptType,
250 unminified_dir: Option<String>,
251 import_map: Fallible<ImportMap>,
252 ) -> ScriptOrigin {
253 ScriptOrigin {
254 code: SourceCode::Text(text),
255 url,
256 external: false,
257 fetch_options,
258 type_,
259 unminified_dir,
260 import_map,
261 }
262 }
263
264 pub(crate) fn external(
265 text: Rc<DOMString>,
266 url: ServoUrl,
267 fetch_options: ScriptFetchOptions,
268 type_: ScriptType,
269 unminified_dir: Option<String>,
270 ) -> ScriptOrigin {
271 ScriptOrigin {
272 code: SourceCode::Text(text),
273 url,
274 external: true,
275 fetch_options,
276 type_,
277 unminified_dir,
278 import_map: Err(Error::NotFound),
279 }
280 }
281
282 pub(crate) fn text(&self) -> Rc<DOMString> {
283 match &self.code {
284 SourceCode::Text(text) => Rc::clone(text),
285 SourceCode::Compiled(compiled_script) => Rc::clone(&compiled_script.original_text),
286 }
287 }
288}
289
290fn finish_fetching_a_classic_script(
292 elem: &HTMLScriptElement,
293 script_kind: ExternalScriptKind,
294 url: ServoUrl,
295 load: ScriptResult,
296 can_gc: CanGc,
297) {
298 let document;
301
302 match script_kind {
303 ExternalScriptKind::Asap => {
304 document = elem.preparation_time_document.get().unwrap();
305 document.asap_script_loaded(elem, load, can_gc)
306 },
307 ExternalScriptKind::AsapInOrder => {
308 document = elem.preparation_time_document.get().unwrap();
309 document.asap_in_order_script_loaded(elem, load, can_gc)
310 },
311 ExternalScriptKind::Deferred => {
312 document = elem.parser_document.as_rooted();
313 document.deferred_script_loaded(elem, load, can_gc);
314 },
315 ExternalScriptKind::ParsingBlocking => {
316 document = elem.parser_document.as_rooted();
317 document.pending_parsing_blocking_script_loaded(elem, load, can_gc);
318 },
319 }
320
321 document.finish_load(LoadType::Script(url), can_gc);
322}
323
324pub(crate) type ScriptResult = Result<ScriptOrigin, NoTrace<NetworkError>>;
325
326struct ClassicContext {
328 elem: Trusted<HTMLScriptElement>,
330 kind: ExternalScriptKind,
332 character_encoding: &'static Encoding,
335 data: Vec<u8>,
337 metadata: Option<Metadata>,
339 url: ServoUrl,
341 status: Result<(), NetworkError>,
343 fetch_options: ScriptFetchOptions,
345 resource_timing: ResourceFetchTiming,
347}
348
349impl FetchResponseListener for ClassicContext {
350 fn process_request_body(&mut self, _: RequestId) {}
352
353 fn process_request_eof(&mut self, _: RequestId) {}
355
356 fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
357 self.metadata = metadata.ok().map(|meta| match meta {
358 FetchMetadata::Unfiltered(m) => m,
359 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
360 });
361
362 let status = self
363 .metadata
364 .as_ref()
365 .map(|m| m.status.clone())
366 .unwrap_or_else(HttpStatus::new_error);
367
368 self.status = {
369 if status.is_error() {
370 Err(NetworkError::Internal(
371 "No http status code received".to_owned(),
372 ))
373 } else if status.is_success() {
374 Ok(())
375 } else {
376 Err(NetworkError::Internal(format!(
377 "HTTP error code {}",
378 status.code()
379 )))
380 }
381 };
382 }
383
384 fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec<u8>) {
385 if self.status.is_ok() {
386 self.data.append(&mut chunk);
387 }
388 }
389
390 #[allow(unsafe_code)]
393 fn process_response_eof(
394 &mut self,
395 _: RequestId,
396 response: Result<ResourceFetchTiming, NetworkError>,
397 ) {
398 let (source_text, final_url) = match (response.as_ref(), self.status.as_ref()) {
399 (Err(err), _) | (_, Err(err)) => {
400 finish_fetching_a_classic_script(
402 &self.elem.root(),
403 self.kind,
404 self.url.clone(),
405 Err(NoTrace(err.clone())),
406 CanGc::note(),
407 );
408 return;
409 },
410 (Ok(_), Ok(_)) => {
411 let metadata = self.metadata.take().unwrap();
412
413 let encoding = metadata
415 .charset
416 .and_then(|encoding| Encoding::for_label(encoding.as_bytes()))
417 .unwrap_or(self.character_encoding);
418
419 let (source_text, _, _) = encoding.decode(&self.data);
421 (source_text, metadata.final_url)
422 },
423 };
424
425 let elem = self.elem.root();
426 let global = elem.global();
427 let _ar = enter_realm(&*global);
429
430 let load = ScriptOrigin::external(
461 Rc::new(DOMString::from(source_text)),
462 final_url.clone(),
463 self.fetch_options.clone(),
464 ScriptType::Classic,
465 elem.parser_document.global().unminified_js_dir(),
466 );
467 finish_fetching_a_classic_script(
468 &elem,
469 self.kind,
470 self.url.clone(),
471 Ok(load),
472 CanGc::note(),
473 );
474 }
476
477 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
478 &mut self.resource_timing
479 }
480
481 fn resource_timing(&self) -> &ResourceFetchTiming {
482 &self.resource_timing
483 }
484
485 fn submit_resource_timing(&mut self) {
486 network_listener::submit_timing(self, CanGc::note())
487 }
488
489 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
490 let global = &self.resource_timing_global();
491 let elem = self.elem.root();
492 let source_position = elem
493 .upcast::<Element>()
494 .compute_source_position(elem.line_number as u32);
495 global.report_csp_violations(violations, Some(elem.upcast()), Some(source_position));
496 }
497}
498
499impl ResourceTimingListener for ClassicContext {
500 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
501 let initiator_type = InitiatorType::LocalName(
502 self.elem
503 .root()
504 .upcast::<Element>()
505 .local_name()
506 .to_string(),
507 );
508 (initiator_type, self.url.clone())
509 }
510
511 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
512 self.elem.root().owner_document().global()
513 }
514}
515
516impl PreInvoke for ClassicContext {}
517
518#[allow(clippy::too_many_arguments)]
521pub(crate) fn script_fetch_request(
522 webview_id: WebViewId,
523 url: ServoUrl,
524 cors_setting: Option<CorsSettings>,
525 origin: ImmutableOrigin,
526 pipeline_id: PipelineId,
527 options: ScriptFetchOptions,
528 insecure_requests_policy: InsecureRequestsPolicy,
529 has_trustworthy_ancestor_origin: bool,
530 policy_container: PolicyContainer,
531) -> RequestBuilder {
532 create_a_potential_cors_request(
535 Some(webview_id),
536 url,
537 Destination::Script,
538 cors_setting,
539 None,
540 options.referrer,
541 insecure_requests_policy,
542 has_trustworthy_ancestor_origin,
543 policy_container,
544 )
545 .origin(origin)
546 .pipeline_id(Some(pipeline_id))
547 .parser_metadata(options.parser_metadata)
548 .integrity_metadata(options.integrity_metadata.clone())
549 .referrer_policy(options.referrer_policy)
550 .cryptographic_nonce_metadata(options.cryptographic_nonce)
551}
552
553fn fetch_a_classic_script(
555 script: &HTMLScriptElement,
556 kind: ExternalScriptKind,
557 url: ServoUrl,
558 cors_setting: Option<CorsSettings>,
559 options: ScriptFetchOptions,
560 character_encoding: &'static Encoding,
561) {
562 let doc = script.owner_document();
564 let global = script.global();
565 let request = script_fetch_request(
566 doc.webview_id(),
567 url.clone(),
568 cors_setting,
569 doc.origin().immutable().clone(),
570 global.pipeline_id(),
571 options.clone(),
572 doc.insecure_requests_policy(),
573 doc.has_trustworthy_ancestor_origin(),
574 global.policy_container(),
575 );
576 let request = doc.prepare_request(request);
577
578 let context = ClassicContext {
581 elem: Trusted::new(script),
582 kind,
583 character_encoding,
584 data: vec![],
585 metadata: None,
586 url: url.clone(),
587 status: Ok(()),
588 fetch_options: options,
589 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
590 };
591 doc.fetch(LoadType::Script(url), request, context);
592}
593
594impl HTMLScriptElement {
595 pub(crate) fn set_initial_script_text(&self) {
597 *self.script_text.borrow_mut() = self.text();
598 }
599
600 fn prepare_the_script_text(&self, can_gc: CanGc) -> Fallible<()> {
602 if self.script_text.borrow().clone() != self.text() {
606 *self.script_text.borrow_mut() = TrustedScript::get_trusted_script_compliant_string(
607 &self.owner_global(),
608 self.Text(),
609 "HTMLScriptElement text",
610 can_gc,
611 )?;
612 }
613
614 Ok(())
615 }
616
617 pub(crate) fn prepare(&self, introduction_type_override: Option<&'static CStr>, can_gc: CanGc) {
619 self.introduction_type_override
620 .set(introduction_type_override);
621
622 if self.already_started.get() {
624 return;
625 }
626
627 let was_parser_inserted = self.parser_inserted.get();
632 self.parser_inserted.set(false);
633
634 let element = self.upcast::<Element>();
637 let asynch = element.has_attribute(&local_name!("async"));
638 if was_parser_inserted && !asynch {
640 self.non_blocking.set(true);
641 }
642
643 if self.prepare_the_script_text(can_gc).is_err() {
646 return;
647 }
648 let text = self.script_text.borrow().clone();
650 if text.is_empty() && !element.has_attribute(&local_name!("src")) {
652 return;
653 }
654
655 if !self.upcast::<Node>().is_connected() {
657 return;
658 }
659
660 let script_type = if let Some(ty) = self.get_script_type() {
661 ty
663 } else {
664 return;
666 };
667
668 if was_parser_inserted {
672 self.parser_inserted.set(true);
673 self.non_blocking.set(false);
674 }
675
676 self.already_started.set(true);
678
679 let doc = self.owner_document();
681 self.preparation_time_document.set(Some(&doc));
682
683 if self.parser_inserted.get() && *self.parser_document != *doc {
687 return;
688 }
689
690 if !doc.is_scripting_enabled() {
692 return;
693 }
694
695 if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
697 return;
698 }
699
700 let global = &doc.global();
701
702 if !element.has_attribute(&local_name!("src")) &&
704 global
705 .get_csp_list()
706 .should_elements_inline_type_behavior_be_blocked(
707 global,
708 element,
709 InlineCheckType::Script,
710 &text,
711 )
712 {
713 warn!("Blocking inline script due to CSP");
714 return;
715 }
716
717 if script_type == ScriptType::Classic {
719 let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
720 let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
721 if let (Some(ref for_attribute), Some(ref event_attribute)) =
722 (for_attribute, event_attribute)
723 {
724 let for_value = for_attribute.value().to_ascii_lowercase();
725 let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
726 if for_value != "window" {
727 return;
728 }
729
730 let event_value = event_attribute.value().to_ascii_lowercase();
731 let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
732 if event_value != "onload" && event_value != "onload()" {
733 return;
734 }
735 }
736 }
737
738 let encoding = element
743 .get_attribute(&ns!(), &local_name!("charset"))
744 .and_then(|charset| Encoding::for_label(charset.value().as_bytes()))
745 .unwrap_or_else(|| doc.encoding());
746
747 let cors_setting = cors_setting_for_element(element);
749
750 let module_credentials_mode = match script_type {
752 ScriptType::Classic => CredentialsMode::CredentialsSameOrigin,
753 ScriptType::Module | ScriptType::ImportMap => reflect_cross_origin_attribute(element)
754 .map_or(
755 CredentialsMode::CredentialsSameOrigin,
756 |attr| match &*attr {
757 "use-credentials" => CredentialsMode::Include,
758 "anonymous" => CredentialsMode::CredentialsSameOrigin,
759 _ => CredentialsMode::CredentialsSameOrigin,
760 },
761 ),
762 };
763
764 let cryptographic_nonce = self.upcast::<Element>().nonce_value();
766
767 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
770 let integrity_val = im_attribute.as_ref().map(|a| a.value());
771 let integrity_metadata = match integrity_val {
772 Some(ref value) => &***value,
773 None => "",
774 };
775
776 let referrer_policy = referrer_policy_for_element(self.upcast::<Element>());
778
779 let parser_metadata = if self.parser_inserted.get() {
784 ParserMetadata::ParserInserted
785 } else {
786 ParserMetadata::NotParserInserted
787 };
788
789 let options = ScriptFetchOptions {
791 cryptographic_nonce,
792 integrity_metadata: integrity_metadata.to_owned(),
793 parser_metadata,
794 referrer: self.global().get_referrer(),
795 referrer_policy,
796 credentials_mode: module_credentials_mode,
797 };
798
799 let base_url = doc.base_url();
804 if let Some(src) = element.get_attribute(&ns!(), &local_name!("src")) {
805 if script_type == ScriptType::ImportMap {
809 self.queue_error_event();
812 return;
813 }
814
815 let src = src.value();
817
818 if src.is_empty() {
820 self.queue_error_event();
821 return;
822 }
823
824 let url = match base_url.join(&src) {
829 Ok(url) => url,
830 Err(_) => {
831 warn!("error parsing URL for script {}", &**src);
832 self.queue_error_event();
833 return;
834 },
835 };
836
837 match script_type {
844 ScriptType::Classic => {
845 let kind = if element.has_attribute(&local_name!("defer")) &&
846 was_parser_inserted &&
847 !asynch
848 {
849 ExternalScriptKind::Deferred
851 } else if was_parser_inserted && !asynch {
852 ExternalScriptKind::ParsingBlocking
854 } else if !asynch && !self.non_blocking.get() {
855 ExternalScriptKind::AsapInOrder
857 } else {
858 ExternalScriptKind::Asap
860 };
861
862 fetch_a_classic_script(self, kind, url, cors_setting, options, encoding);
864
865 match kind {
867 ExternalScriptKind::Deferred => doc.add_deferred_script(self),
868 ExternalScriptKind::ParsingBlocking => {
869 doc.set_pending_parsing_blocking_script(self, None)
870 },
871 ExternalScriptKind::AsapInOrder => doc.push_asap_in_order_script(self),
872 ExternalScriptKind::Asap => doc.add_asap_script(self),
873 }
874 },
875 ScriptType::Module => {
876 fetch_external_module_script(
878 ModuleOwner::Window(Trusted::new(self)),
879 url.clone(),
880 Destination::Script,
881 options,
882 can_gc,
883 );
884
885 if !asynch && was_parser_inserted {
886 doc.add_deferred_script(self);
888 } else if !asynch && !self.non_blocking.get() {
889 doc.push_asap_in_order_script(self);
891 } else {
892 doc.add_asap_script(self);
894 };
895 },
896 ScriptType::ImportMap => (),
897 }
898 } else {
899 assert!(!text.is_empty());
902
903 let text_rc = Rc::new(text);
904
905 match script_type {
907 ScriptType::Classic => {
908 let result = Ok(ScriptOrigin::internal(
909 text_rc,
910 base_url,
911 options,
912 script_type,
913 self.global().unminified_js_dir(),
914 Err(Error::NotFound),
915 ));
916
917 if was_parser_inserted &&
918 doc.get_current_parser()
919 .is_some_and(|parser| parser.script_nesting_level() <= 1) &&
920 doc.get_script_blocking_stylesheets_count() > 0
921 {
922 doc.set_pending_parsing_blocking_script(self, Some(result));
924 } else {
925 self.execute(result, can_gc);
927 }
928 },
929 ScriptType::Module => {
930 if !asynch && was_parser_inserted {
934 doc.add_deferred_script(self);
935 } else if !asynch && !self.non_blocking.get() {
936 doc.push_asap_in_order_script(self);
937 } else {
938 doc.add_asap_script(self);
939 };
940
941 fetch_inline_module_script(
942 ModuleOwner::Window(Trusted::new(self)),
943 text_rc,
944 base_url.clone(),
945 self.id,
946 options,
947 can_gc,
948 );
949 },
950 ScriptType::ImportMap => {
951 let import_map_result = parse_an_import_map_string(
954 ModuleOwner::Window(Trusted::new(self)),
955 Rc::clone(&text_rc),
956 base_url.clone(),
957 can_gc,
958 );
959 let result = Ok(ScriptOrigin::internal(
960 text_rc,
961 base_url,
962 options,
963 script_type,
964 self.global().unminified_js_dir(),
965 import_map_result,
966 ));
967
968 self.execute(result, can_gc);
970 },
971 }
972 }
973 }
974
975 fn substitute_with_local_script(&self, script: &mut ScriptOrigin) {
976 if self
977 .parser_document
978 .window()
979 .local_script_source()
980 .is_none() ||
981 !script.external
982 {
983 return;
984 }
985 let mut path = PathBuf::from(
986 self.parser_document
987 .window()
988 .local_script_source()
989 .clone()
990 .unwrap(),
991 );
992 path = path.join(&script.url[url::Position::BeforeHost..]);
993 debug!("Attempting to read script stored at: {:?}", path);
994 match read_to_string(path.clone()) {
995 Ok(local_script) => {
996 debug!("Found script stored at: {:?}", path);
997 script.code = SourceCode::Text(Rc::new(DOMString::from(local_script)));
998 },
999 Err(why) => warn!("Could not restore script from file {:?}", why),
1000 }
1001 }
1002
1003 pub(crate) fn execute(&self, result: ScriptResult, can_gc: CanGc) {
1005 let doc = self.owner_document();
1007
1008 if *doc != *self.preparation_time_document.get().unwrap() {
1010 return;
1011 }
1012
1013 let mut script = match result {
1015 Err(e) => {
1017 warn!("error loading script {:?}", e);
1018 self.dispatch_error_event(can_gc);
1019 return;
1020 },
1021
1022 Ok(script) => script,
1023 };
1024
1025 if script.type_ == ScriptType::Classic {
1026 unminify_js(&mut script);
1027 self.substitute_with_local_script(&mut script);
1028 }
1029
1030 let neutralized_doc = if script.external || script.type_ == ScriptType::Module {
1034 debug!("loading external script, url = {}", script.url);
1035 let doc = self.owner_document();
1036 doc.incr_ignore_destructive_writes_counter();
1037 Some(doc)
1038 } else {
1039 None
1040 };
1041
1042 let document = self.owner_document();
1044 let old_script = document.GetCurrentScript();
1045 let introduction_type =
1046 self.introduction_type_override
1047 .get()
1048 .unwrap_or(if script.external {
1049 IntroductionType::SRC_SCRIPT
1050 } else {
1051 IntroductionType::INLINE_SCRIPT
1052 });
1053
1054 match script.type_ {
1055 ScriptType::Classic => {
1056 if self.upcast::<Node>().is_in_a_shadow_tree() {
1057 document.set_current_script(None)
1058 } else {
1059 document.set_current_script(Some(self))
1060 }
1061 self.run_a_classic_script(&script, can_gc, Some(introduction_type));
1062 document.set_current_script(old_script.as_deref());
1063 },
1064 ScriptType::Module => {
1065 document.set_current_script(None);
1066 self.run_a_module_script(&script, false, can_gc);
1067 },
1068 ScriptType::ImportMap => {
1069 register_import_map(&self.owner_global(), script.import_map, can_gc);
1071 },
1072 }
1073
1074 if let Some(doc) = neutralized_doc {
1077 doc.decr_ignore_destructive_writes_counter();
1078 }
1079
1080 if script.external {
1082 self.dispatch_load_event(can_gc);
1083 }
1084 }
1085
1086 pub(crate) fn run_a_classic_script(
1088 &self,
1089 script: &ScriptOrigin,
1090 can_gc: CanGc,
1091 introduction_type: Option<&'static CStr>,
1092 ) {
1093 let document = self.owner_document();
1096 if !document.is_fully_active() || !document.is_scripting_enabled() {
1097 return;
1098 }
1099
1100 let window = self.owner_window();
1102 let line_number = if script.external {
1103 1
1104 } else {
1105 self.line_number as u32
1106 };
1107 rooted!(in(*GlobalScope::get_cx()) let mut rval = UndefinedValue());
1108 _ = window
1109 .as_global_scope()
1110 .evaluate_script_on_global_with_result(
1111 &script.code,
1112 script.url.as_str(),
1113 rval.handle_mut(),
1114 line_number,
1115 script.fetch_options.clone(),
1116 script.url.clone(),
1117 can_gc,
1118 introduction_type,
1119 );
1120 }
1121
1122 #[allow(unsafe_code)]
1123 pub(crate) fn run_a_module_script(
1125 &self,
1126 script: &ScriptOrigin,
1127 _rethrow_errors: bool,
1128 can_gc: CanGc,
1129 ) {
1130 let document = self.owner_document();
1133 if !document.is_fully_active() || !document.is_scripting_enabled() {
1134 return;
1135 }
1136
1137 let window = self.owner_window();
1139 let global = window.as_global_scope();
1140 let _aes = AutoEntryScript::new(global);
1141
1142 let tree = if script.external {
1143 global.get_module_map().borrow().get(&script.url).cloned()
1144 } else {
1145 global
1146 .get_inline_module_map()
1147 .borrow()
1148 .get(&self.id.clone())
1149 .cloned()
1150 };
1151
1152 if let Some(module_tree) = tree {
1153 {
1155 let module_error = module_tree.get_rethrow_error().borrow();
1156 let network_error = module_tree.get_network_error().borrow();
1157 if module_error.is_some() && network_error.is_none() {
1158 module_tree.report_error(global, can_gc);
1159 return;
1160 }
1161 }
1162
1163 let record = module_tree
1164 .get_record()
1165 .borrow()
1166 .as_ref()
1167 .map(|record| record.handle());
1168
1169 if let Some(record) = record {
1170 rooted!(in(*GlobalScope::get_cx()) let mut rval = UndefinedValue());
1171 let evaluated =
1172 module_tree.execute_module(global, record, rval.handle_mut().into(), can_gc);
1173
1174 if let Err(exception) = evaluated {
1175 module_tree.set_rethrow_error(exception);
1176 module_tree.report_error(global, can_gc);
1177 }
1178 }
1179 }
1180 }
1181
1182 pub(crate) fn queue_error_event(&self) {
1183 self.owner_global()
1184 .task_manager()
1185 .dom_manipulation_task_source()
1186 .queue_simple_event(self.upcast(), atom!("error"));
1187 }
1188
1189 pub(crate) fn dispatch_load_event(&self, can_gc: CanGc) {
1190 self.dispatch_event(
1191 atom!("load"),
1192 EventBubbles::DoesNotBubble,
1193 EventCancelable::NotCancelable,
1194 can_gc,
1195 );
1196 }
1197
1198 pub(crate) fn dispatch_error_event(&self, can_gc: CanGc) {
1199 self.dispatch_event(
1200 atom!("error"),
1201 EventBubbles::DoesNotBubble,
1202 EventCancelable::NotCancelable,
1203 can_gc,
1204 );
1205 }
1206
1207 pub(crate) fn get_script_type(&self) -> Option<ScriptType> {
1209 let element = self.upcast::<Element>();
1210
1211 let type_attr = element.get_attribute(&ns!(), &local_name!("type"));
1212 let language_attr = element.get_attribute(&ns!(), &local_name!("language"));
1213
1214 match (
1215 type_attr.as_ref().map(|t| t.value()),
1216 language_attr.as_ref().map(|l| l.value()),
1217 ) {
1218 (Some(ref ty), _) if ty.is_empty() => {
1219 debug!("script type empty, inferring js");
1220 Some(ScriptType::Classic)
1221 },
1222 (None, Some(ref lang)) if lang.is_empty() => {
1223 debug!("script type empty, inferring js");
1224 Some(ScriptType::Classic)
1225 },
1226 (None, None) => {
1227 debug!("script type empty, inferring js");
1228 Some(ScriptType::Classic)
1229 },
1230 (None, Some(ref lang)) => {
1231 debug!("script language={}", &***lang);
1232 let language = format!("text/{}", &***lang);
1233
1234 if SCRIPT_JS_MIMES.contains(&language.to_ascii_lowercase().as_str()) {
1235 Some(ScriptType::Classic)
1236 } else {
1237 None
1238 }
1239 },
1240 (Some(ref ty), _) => {
1241 debug!("script type={}", &***ty);
1242
1243 if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "module" {
1244 return Some(ScriptType::Module);
1245 }
1246
1247 if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "importmap" {
1248 return Some(ScriptType::ImportMap);
1249 }
1250
1251 if SCRIPT_JS_MIMES
1252 .contains(&ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS))
1253 {
1254 Some(ScriptType::Classic)
1255 } else {
1256 None
1257 }
1258 },
1259 }
1260 }
1261
1262 pub(crate) fn set_parser_inserted(&self, parser_inserted: bool) {
1263 self.parser_inserted.set(parser_inserted);
1264 }
1265
1266 pub(crate) fn get_parser_inserted(&self) -> bool {
1267 self.parser_inserted.get()
1268 }
1269
1270 pub(crate) fn set_already_started(&self, already_started: bool) {
1271 self.already_started.set(already_started);
1272 }
1273
1274 pub(crate) fn get_non_blocking(&self) -> bool {
1275 self.non_blocking.get()
1276 }
1277
1278 fn dispatch_event(
1279 &self,
1280 type_: Atom,
1281 bubbles: EventBubbles,
1282 cancelable: EventCancelable,
1283 can_gc: CanGc,
1284 ) -> bool {
1285 let window = self.owner_window();
1286 let event = Event::new(window.upcast(), type_, bubbles, cancelable, can_gc);
1287 event.fire(self.upcast(), can_gc)
1288 }
1289
1290 fn text(&self) -> DOMString {
1291 match self.Text() {
1292 TrustedScriptOrString::String(value) => value,
1293 TrustedScriptOrString::TrustedScript(trusted_script) => {
1294 DOMString::from(trusted_script.to_string())
1295 },
1296 }
1297 }
1298}
1299
1300impl VirtualMethods for HTMLScriptElement {
1301 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1302 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1303 }
1304
1305 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1306 self.super_type()
1307 .unwrap()
1308 .attribute_mutated(attr, mutation, can_gc);
1309 if *attr.local_name() == local_name!("src") {
1310 if let AttributeMutation::Set(_) = mutation {
1311 if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
1312 self.prepare(Some(IntroductionType::INJECTED_SCRIPT), can_gc);
1313 }
1314 }
1315 }
1316 }
1317
1318 fn children_changed(&self, mutation: &ChildrenMutation) {
1320 if let Some(s) = self.super_type() {
1321 s.children_changed(mutation);
1322 }
1323
1324 if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1325 let script = DomRoot::from_ref(self);
1326 self.owner_document().add_delayed_task(
1330 task!(ScriptPrepare: |script: DomRoot<HTMLScriptElement>| {
1331 script.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
1332 }),
1333 );
1334 }
1335 }
1336
1337 fn post_connection_steps(&self) {
1339 if let Some(s) = self.super_type() {
1340 s.post_connection_steps();
1341 }
1342
1343 if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1344 self.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
1345 }
1346 }
1347
1348 fn cloning_steps(
1349 &self,
1350 copy: &Node,
1351 maybe_doc: Option<&Document>,
1352 clone_children: CloneChildrenFlag,
1353 can_gc: CanGc,
1354 ) {
1355 if let Some(s) = self.super_type() {
1356 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
1357 }
1358
1359 if self.already_started.get() {
1361 copy.downcast::<HTMLScriptElement>()
1362 .unwrap()
1363 .set_already_started(true);
1364 }
1365 }
1366}
1367
1368impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement {
1369 fn Src(&self) -> TrustedScriptURLOrUSVString {
1371 let element = self.upcast::<Element>();
1372 element.get_trusted_type_url_attribute(&local_name!("src"))
1373 }
1374
1375 fn SetSrc(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> {
1377 let element = self.upcast::<Element>();
1378 let local_name = &local_name!("src");
1379 let value = TrustedScriptURL::get_trusted_script_url_compliant_string(
1380 &element.owner_global(),
1381 value,
1382 "HTMLScriptElement",
1383 local_name,
1384 can_gc,
1385 )?;
1386 element.set_attribute(
1387 local_name,
1388 AttrValue::String(value.as_ref().to_owned()),
1389 can_gc,
1390 );
1391 Ok(())
1392 }
1393
1394 make_getter!(Type, "type");
1396 make_setter!(SetType, "type");
1398
1399 make_getter!(Charset, "charset");
1401 make_setter!(SetCharset, "charset");
1403
1404 fn Async(&self) -> bool {
1406 self.non_blocking.get() ||
1407 self.upcast::<Element>()
1408 .has_attribute(&local_name!("async"))
1409 }
1410
1411 fn SetAsync(&self, value: bool, can_gc: CanGc) {
1413 self.non_blocking.set(false);
1414 self.upcast::<Element>()
1415 .set_bool_attribute(&local_name!("async"), value, can_gc);
1416 }
1417
1418 make_bool_getter!(Defer, "defer");
1420 make_bool_setter!(SetDefer, "defer");
1422
1423 make_bool_getter!(NoModule, "nomodule");
1425 make_bool_setter!(SetNoModule, "nomodule");
1427
1428 make_getter!(Integrity, "integrity");
1430 make_setter!(SetIntegrity, "integrity");
1432
1433 make_getter!(Event, "event");
1435 make_setter!(SetEvent, "event");
1437
1438 make_getter!(HtmlFor, "for");
1440 make_setter!(SetHtmlFor, "for");
1442
1443 fn GetCrossOrigin(&self) -> Option<DOMString> {
1445 reflect_cross_origin_attribute(self.upcast::<Element>())
1446 }
1447
1448 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1450 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1451 }
1452
1453 fn ReferrerPolicy(&self) -> DOMString {
1455 reflect_referrer_policy_attribute(self.upcast::<Element>())
1456 }
1457
1458 make_setter!(SetReferrerPolicy, "referrerpolicy");
1460
1461 fn InnerText(&self) -> TrustedScriptOrString {
1463 TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text())
1465 }
1466
1467 fn SetInnerText(&self, input: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1469 let value = TrustedScript::get_trusted_script_compliant_string(
1472 &self.owner_global(),
1473 input,
1474 "HTMLScriptElement innerText",
1475 can_gc,
1476 )?;
1477 *self.script_text.borrow_mut() = value.clone();
1478 self.upcast::<HTMLElement>().set_inner_text(value, can_gc);
1480 Ok(())
1481 }
1482
1483 fn Text(&self) -> TrustedScriptOrString {
1485 TrustedScriptOrString::String(self.upcast::<Node>().child_text_content())
1486 }
1487
1488 fn SetText(&self, value: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1490 let value = TrustedScript::get_trusted_script_compliant_string(
1493 &self.owner_global(),
1494 value,
1495 "HTMLScriptElement text",
1496 can_gc,
1497 )?;
1498 *self.script_text.borrow_mut() = value.clone();
1500 Node::string_replace_all(value, self.upcast::<Node>(), can_gc);
1502 Ok(())
1503 }
1504
1505 fn GetTextContent(&self) -> Option<TrustedScriptOrString> {
1507 Some(TrustedScriptOrString::String(
1509 self.upcast::<Node>().GetTextContent()?,
1510 ))
1511 }
1512
1513 fn SetTextContent(&self, value: Option<TrustedScriptOrString>, can_gc: CanGc) -> Fallible<()> {
1515 let value = TrustedScript::get_trusted_script_compliant_string(
1518 &self.owner_global(),
1519 value.unwrap_or(TrustedScriptOrString::String(DOMString::from(""))),
1520 "HTMLScriptElement textContent",
1521 can_gc,
1522 )?;
1523 *self.script_text.borrow_mut() = value.clone();
1525 self.upcast::<Node>()
1527 .set_text_content_for_element(Some(value), can_gc);
1528 Ok(())
1529 }
1530
1531 fn Supports(_window: &Window, type_: DOMString) -> bool {
1533 matches!(type_.str(), "classic" | "module" | "importmap")
1536 }
1537}
1538
1539#[derive(Clone, Copy)]
1540enum ExternalScriptKind {
1541 Deferred,
1542 ParsingBlocking,
1543 AsapInOrder,
1544 Asap,
1545}