1use std::borrow::Cow;
6use std::cell::Cell;
7use std::ffi::CStr;
8use std::fs::read_to_string;
9use std::path::PathBuf;
10use std::rc::Rc;
11
12use base::id::WebViewId;
13use dom_struct::dom_struct;
14use encoding_rs::Encoding;
15use html5ever::{LocalName, Prefix, local_name, ns};
16use js::context::JSContext;
17use js::jsval::UndefinedValue;
18use js::rust::{HandleObject, Stencil};
19use net_traits::http_status::HttpStatus;
20use net_traits::request::{
21 CorsSettings, CredentialsMode, Destination, ParserMetadata, RequestBuilder, RequestId,
22};
23use net_traits::{FetchMetadata, Metadata, NetworkError, ResourceFetchTiming};
24use servo_url::ServoUrl;
25use style::attr::AttrValue;
26use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
27use stylo_atoms::Atom;
28use uuid::Uuid;
29
30use crate::document_loader::LoadType;
31use crate::dom::attr::Attr;
32use crate::dom::bindings::cell::DomRefCell;
33use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
35use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
36use crate::dom::bindings::codegen::UnionTypes::{
37 TrustedScriptOrString, TrustedScriptURLOrUSVString,
38};
39use crate::dom::bindings::error::{Error, Fallible};
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::refcounted::Trusted;
42use crate::dom::bindings::reflector::DomGlobal;
43use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
44use crate::dom::bindings::settings_stack::AutoEntryScript;
45use crate::dom::bindings::str::DOMString;
46use crate::dom::csp::{CspReporting, GlobalCspReporting, InlineCheckType, Violation};
47use crate::dom::document::Document;
48use crate::dom::element::{
49 AttributeMutation, Element, ElementCreator, cors_setting_for_element,
50 referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
51 set_cross_origin_attribute,
52};
53use crate::dom::event::{Event, EventBubbles, EventCancelable};
54use crate::dom::global_scope_script_execution::{ClassicScript, ErrorReporting, RethrowErrors};
55use crate::dom::globalscope::GlobalScope;
56use crate::dom::html::htmlelement::HTMLElement;
57use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeTraits};
58use crate::dom::performance::performanceresourcetiming::InitiatorType;
59use crate::dom::trustedscript::TrustedScript;
60use crate::dom::trustedscripturl::TrustedScriptURL;
61use crate::dom::virtualmethods::VirtualMethods;
62use crate::dom::window::Window;
63use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
64use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
65use crate::script_module::{
66 ImportMap, ModuleOwner, ModuleTree, ScriptFetchOptions, fetch_an_external_module_script,
67 fetch_inline_module_script, parse_an_import_map_string, register_import_map,
68};
69use crate::script_runtime::{CanGc, IntroductionType};
70
71#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq, MallocSizeOf)]
73pub(crate) struct ScriptId(#[no_trace] Uuid);
74
75#[dom_struct]
76pub(crate) struct HTMLScriptElement {
77 htmlelement: HTMLElement,
78
79 already_started: Cell<bool>,
81
82 parser_inserted: Cell<bool>,
84
85 non_blocking: Cell<bool>,
89
90 parser_document: Dom<Document>,
93
94 preparation_time_document: MutNullableDom<Document>,
97
98 line_number: u64,
100
101 #[ignore_malloc_size_of = "Defined in uuid"]
103 id: ScriptId,
104
105 script_text: DomRefCell<DOMString>,
107
108 from_an_external_file: Cell<bool>,
110
111 #[no_trace]
114 introduction_type_override: Cell<Option<&'static CStr>>,
115}
116
117impl HTMLScriptElement {
118 fn new_inherited(
119 local_name: LocalName,
120 prefix: Option<Prefix>,
121 document: &Document,
122 creator: ElementCreator,
123 ) -> HTMLScriptElement {
124 HTMLScriptElement {
125 id: ScriptId(Uuid::new_v4()),
126 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
127 already_started: Cell::new(false),
128 parser_inserted: Cell::new(creator.is_parser_created()),
129 non_blocking: Cell::new(!creator.is_parser_created()),
130 parser_document: Dom::from_ref(document),
131 preparation_time_document: MutNullableDom::new(None),
132 line_number: creator.return_line_number(),
133 script_text: DomRefCell::new(DOMString::new()),
134 from_an_external_file: Cell::new(false),
135 introduction_type_override: Cell::new(None),
136 }
137 }
138
139 pub(crate) fn new(
140 local_name: LocalName,
141 prefix: Option<Prefix>,
142 document: &Document,
143 proto: Option<HandleObject>,
144 creator: ElementCreator,
145 can_gc: CanGc,
146 ) -> DomRoot<HTMLScriptElement> {
147 Node::reflect_node_with_proto(
148 Box::new(HTMLScriptElement::new_inherited(
149 local_name, prefix, document, creator,
150 )),
151 document,
152 proto,
153 can_gc,
154 )
155 }
156
157 pub(crate) fn get_script_id(&self) -> ScriptId {
158 self.id
159 }
160}
161
162pub(crate) static SCRIPT_JS_MIMES: StaticStringVec = &[
165 "application/ecmascript",
166 "application/javascript",
167 "application/x-ecmascript",
168 "application/x-javascript",
169 "text/ecmascript",
170 "text/javascript",
171 "text/javascript1.0",
172 "text/javascript1.1",
173 "text/javascript1.2",
174 "text/javascript1.3",
175 "text/javascript1.4",
176 "text/javascript1.5",
177 "text/jscript",
178 "text/livescript",
179 "text/x-ecmascript",
180 "text/x-javascript",
181];
182
183#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
184pub(crate) enum ScriptType {
185 Classic,
186 Module,
187 ImportMap,
188}
189
190#[derive(JSTraceable, MallocSizeOf)]
191pub(crate) struct CompiledSourceCode {
192 #[ignore_malloc_size_of = "SM handles JS values"]
193 pub(crate) source_code: Stencil,
194 #[conditional_malloc_size_of = "Rc is hard"]
195 pub(crate) original_text: Rc<DOMString>,
196}
197
198#[derive(JSTraceable, MallocSizeOf)]
199pub(crate) enum SourceCode {
200 Text(#[conditional_malloc_size_of] Rc<DOMString>),
201 Compiled(CompiledSourceCode),
202}
203
204#[derive(JSTraceable, MallocSizeOf)]
205pub(crate) struct ScriptOrigin {
206 pub code: SourceCode,
207 #[no_trace]
208 pub url: ServoUrl,
209 external: bool,
210 pub fetch_options: ScriptFetchOptions,
211 type_: ScriptType,
212 unminified_dir: Option<String>,
213 import_map: Fallible<ImportMap>,
214}
215
216impl ScriptOrigin {
217 pub(crate) fn internal(
218 text: Rc<DOMString>,
219 url: ServoUrl,
220 fetch_options: ScriptFetchOptions,
221 type_: ScriptType,
222 unminified_dir: Option<String>,
223 import_map: Fallible<ImportMap>,
224 ) -> ScriptOrigin {
225 ScriptOrigin {
226 code: SourceCode::Text(text),
227 url,
228 external: false,
229 fetch_options,
230 type_,
231 unminified_dir,
232 import_map,
233 }
234 }
235
236 pub(crate) fn external(
237 text: Rc<DOMString>,
238 url: ServoUrl,
239 fetch_options: ScriptFetchOptions,
240 type_: ScriptType,
241 unminified_dir: Option<String>,
242 ) -> ScriptOrigin {
243 ScriptOrigin {
244 code: SourceCode::Text(text),
245 url,
246 external: true,
247 fetch_options,
248 type_,
249 unminified_dir,
250 import_map: Err(Error::NotFound(None)),
251 }
252 }
253
254 pub(crate) fn text(&self) -> Rc<DOMString> {
255 match &self.code {
256 SourceCode::Text(text) => Rc::clone(text),
257 SourceCode::Compiled(compiled_script) => Rc::clone(&compiled_script.original_text),
258 }
259 }
260}
261
262fn finish_fetching_a_classic_script(
264 elem: &HTMLScriptElement,
265 script_kind: ExternalScriptKind,
266 url: ServoUrl,
267 load: ScriptResult,
268 can_gc: CanGc,
269) {
270 let document;
273
274 match script_kind {
275 ExternalScriptKind::Asap => {
276 document = elem.preparation_time_document.get().unwrap();
277 document.asap_script_loaded(elem, load, can_gc)
278 },
279 ExternalScriptKind::AsapInOrder => {
280 document = elem.preparation_time_document.get().unwrap();
281 document.asap_in_order_script_loaded(elem, load, can_gc)
282 },
283 ExternalScriptKind::Deferred => {
284 document = elem.parser_document.as_rooted();
285 document.deferred_script_loaded(elem, load, can_gc);
286 },
287 ExternalScriptKind::ParsingBlocking => {
288 document = elem.parser_document.as_rooted();
289 document.pending_parsing_blocking_script_loaded(elem, load, can_gc);
290 },
291 }
292
293 document.finish_load(LoadType::Script(url), can_gc);
294}
295
296pub(crate) type ScriptResult = Result<Script, ()>;
297
298#[derive(JSTraceable, MallocSizeOf)]
300#[expect(clippy::large_enum_variant)]
301pub(crate) enum Script {
302 Classic(ClassicScript),
303 Module(#[conditional_malloc_size_of] Rc<ModuleTree>),
304 ImportMap(ScriptOrigin),
305}
306
307struct ClassicContext {
309 elem: Trusted<HTMLScriptElement>,
311 kind: ExternalScriptKind,
313 character_encoding: &'static Encoding,
316 data: Vec<u8>,
318 metadata: Option<Metadata>,
320 url: ServoUrl,
322 status: Result<(), NetworkError>,
324 fetch_options: ScriptFetchOptions,
326 response_was_cors_cross_origin: bool,
328}
329
330impl FetchResponseListener for ClassicContext {
331 fn process_request_body(&mut self, _: RequestId) {}
333
334 fn process_request_eof(&mut self, _: RequestId) {}
336
337 fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
338 self.metadata = metadata.ok().map(|meta| {
339 self.response_was_cors_cross_origin = meta.is_cors_cross_origin();
340 match meta {
341 FetchMetadata::Unfiltered(m) => m,
342 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
343 }
344 });
345
346 let status = self
347 .metadata
348 .as_ref()
349 .map(|m| m.status.clone())
350 .unwrap_or_else(HttpStatus::new_error);
351
352 self.status = {
353 if status.is_error() {
354 Err(NetworkError::ResourceLoadError(
355 "No http status code received".to_owned(),
356 ))
357 } else if status.is_success() {
358 Ok(())
359 } else {
360 Err(NetworkError::ResourceLoadError(format!(
361 "HTTP error code {}",
362 status.code()
363 )))
364 }
365 };
366 }
367
368 fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec<u8>) {
369 if self.status.is_ok() {
370 self.data.append(&mut chunk);
371 }
372 }
373
374 fn process_response_eof(
377 mut self,
378 _: RequestId,
379 response: Result<(), NetworkError>,
380 timing: ResourceFetchTiming,
381 ) {
382 match (response.as_ref(), self.status.as_ref()) {
383 (Err(error), _) | (_, Err(error)) => {
384 error!("Fetching classic script failed {:?}", error);
385 finish_fetching_a_classic_script(
387 &self.elem.root(),
388 self.kind,
389 self.url.clone(),
390 Err(()),
391 CanGc::note(),
392 );
393
394 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
396 return;
397 },
398 _ => {},
399 };
400
401 let metadata = self.metadata.take().unwrap();
402 let final_url = metadata.final_url;
403
404 let encoding = metadata
407 .charset
408 .and_then(|encoding| Encoding::for_label(encoding.as_bytes()))
409 .unwrap_or(self.character_encoding);
410
411 let (mut source_text, _, _) = encoding.decode(&self.data);
413
414 let elem = self.elem.root();
415 let global = elem.global();
416
417 if let Some(window) = global.downcast::<Window>() {
418 substitute_with_local_script(window, &mut source_text, final_url.clone());
419 }
420
421 let muted_errors = self.response_was_cors_cross_origin;
423
424 let script = global.create_a_classic_script(
427 source_text,
428 final_url.clone(),
429 self.fetch_options.clone(),
430 ErrorReporting::from(muted_errors),
431 Some(IntroductionType::SRC_SCRIPT),
432 1,
433 true,
434 );
435
436 let load = Script::Classic(script);
467 finish_fetching_a_classic_script(
468 &elem,
469 self.kind,
470 self.url.clone(),
471 Ok(load),
472 CanGc::note(),
473 );
474 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
477 }
478
479 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
480 let global = &self.resource_timing_global();
481 let elem = self.elem.root();
482 let source_position = elem
483 .upcast::<Element>()
484 .compute_source_position(elem.line_number as u32);
485 global.report_csp_violations(violations, Some(elem.upcast()), Some(source_position));
486 }
487}
488
489impl ResourceTimingListener for ClassicContext {
490 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
491 let initiator_type = InitiatorType::LocalName(
492 self.elem
493 .root()
494 .upcast::<Element>()
495 .local_name()
496 .to_string(),
497 );
498 (initiator_type, self.url.clone())
499 }
500
501 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
502 self.elem.root().owner_document().global()
503 }
504}
505
506#[allow(clippy::too_many_arguments)]
509pub(crate) fn script_fetch_request(
510 webview_id: WebViewId,
511 url: ServoUrl,
512 cors_setting: Option<CorsSettings>,
513 options: ScriptFetchOptions,
514) -> RequestBuilder {
515 create_a_potential_cors_request(
518 Some(webview_id),
519 url,
520 Destination::Script,
521 cors_setting,
522 None,
523 options.referrer,
524 )
525 .parser_metadata(options.parser_metadata)
526 .integrity_metadata(options.integrity_metadata.clone())
527 .referrer_policy(options.referrer_policy)
528 .cryptographic_nonce_metadata(options.cryptographic_nonce)
529}
530
531fn fetch_a_classic_script(
533 script: &HTMLScriptElement,
534 kind: ExternalScriptKind,
535 url: ServoUrl,
536 cors_setting: Option<CorsSettings>,
537 options: ScriptFetchOptions,
538 character_encoding: &'static Encoding,
539) {
540 let doc = script.owner_document();
542 let global = script.global();
543 let request =
544 script_fetch_request(doc.webview_id(), url.clone(), cors_setting, options.clone())
545 .with_global_scope(&global);
546
547 let context = ClassicContext {
550 elem: Trusted::new(script),
551 kind,
552 character_encoding,
553 data: vec![],
554 metadata: None,
555 url: url.clone(),
556 status: Ok(()),
557 fetch_options: options,
558 response_was_cors_cross_origin: false,
559 };
560 doc.fetch(LoadType::Script(url), request, context);
561}
562
563impl HTMLScriptElement {
564 pub(crate) fn set_initial_script_text(&self) {
566 *self.script_text.borrow_mut() = self.text();
567 }
568
569 fn prepare_the_script_text(&self, can_gc: CanGc) -> Fallible<()> {
571 if self.script_text.borrow().clone() != self.text() {
575 *self.script_text.borrow_mut() = TrustedScript::get_trusted_script_compliant_string(
576 &self.owner_global(),
577 self.Text(),
578 "HTMLScriptElement text",
579 can_gc,
580 )?;
581 }
582
583 Ok(())
584 }
585
586 pub(crate) fn prepare(&self, introduction_type_override: Option<&'static CStr>, can_gc: CanGc) {
588 self.introduction_type_override
589 .set(introduction_type_override);
590
591 if self.already_started.get() {
593 return;
594 }
595
596 let was_parser_inserted = self.parser_inserted.get();
601 self.parser_inserted.set(false);
602
603 let element = self.upcast::<Element>();
606 let asynch = element.has_attribute(&local_name!("async"));
607 if was_parser_inserted && !asynch {
609 self.non_blocking.set(true);
610 }
611
612 if self.prepare_the_script_text(can_gc).is_err() {
615 return;
616 }
617 let text = self.script_text.borrow().clone();
619 if text.is_empty() && !element.has_attribute(&local_name!("src")) {
621 return;
622 }
623
624 if !self.upcast::<Node>().is_connected() {
626 return;
627 }
628
629 let script_type = if let Some(ty) = self.get_script_type() {
630 ty
632 } else {
633 return;
635 };
636
637 if was_parser_inserted {
641 self.parser_inserted.set(true);
642 self.non_blocking.set(false);
643 }
644
645 self.already_started.set(true);
647
648 let doc = self.owner_document();
650 self.preparation_time_document.set(Some(&doc));
651
652 if self.parser_inserted.get() && *self.parser_document != *doc {
656 return;
657 }
658
659 if !doc.scripting_enabled() {
661 return;
662 }
663
664 if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
666 return;
667 }
668
669 let global = &doc.global();
670
671 if !element.has_attribute(&local_name!("src")) &&
673 global
674 .get_csp_list()
675 .should_elements_inline_type_behavior_be_blocked(
676 global,
677 element,
678 InlineCheckType::Script,
679 &text.str(),
680 self.line_number as u32,
681 )
682 {
683 warn!("Blocking inline script due to CSP");
684 return;
685 }
686
687 if script_type == ScriptType::Classic {
689 let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
690 let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
691 if let (Some(ref for_attribute), Some(ref event_attribute)) =
692 (for_attribute, event_attribute)
693 {
694 let for_value = for_attribute.value().to_ascii_lowercase();
695 let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
696 if for_value != "window" {
697 return;
698 }
699
700 let event_value = event_attribute.value().to_ascii_lowercase();
701 let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
702 if event_value != "onload" && event_value != "onload()" {
703 return;
704 }
705 }
706 }
707
708 let encoding = element
713 .get_attribute(&ns!(), &local_name!("charset"))
714 .and_then(|charset| Encoding::for_label(charset.value().as_bytes()))
715 .unwrap_or_else(|| doc.encoding());
716
717 let cors_setting = cors_setting_for_element(element);
719
720 let module_credentials_mode = match script_type {
722 ScriptType::Classic => CredentialsMode::CredentialsSameOrigin,
723 ScriptType::Module | ScriptType::ImportMap => reflect_cross_origin_attribute(element)
724 .map_or(
725 CredentialsMode::CredentialsSameOrigin,
726 |attr| match &*attr.str() {
727 "use-credentials" => CredentialsMode::Include,
728 "anonymous" => CredentialsMode::CredentialsSameOrigin,
729 _ => CredentialsMode::CredentialsSameOrigin,
730 },
731 ),
732 };
733
734 let cryptographic_nonce = self.upcast::<Element>().nonce_value();
736
737 let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
740 let integrity_val = im_attribute.as_ref().map(|a| a.value());
741 let integrity_metadata = match integrity_val {
742 Some(ref value) => &***value,
743 None => "",
744 };
745
746 let referrer_policy = referrer_policy_for_element(self.upcast::<Element>());
748
749 let parser_metadata = if self.parser_inserted.get() {
754 ParserMetadata::ParserInserted
755 } else {
756 ParserMetadata::NotParserInserted
757 };
758
759 let mut options = ScriptFetchOptions {
761 cryptographic_nonce,
762 integrity_metadata: integrity_metadata.to_owned(),
763 parser_metadata,
764 referrer: self.global().get_referrer(),
765 referrer_policy,
766 credentials_mode: module_credentials_mode,
767 };
768
769 let base_url = doc.base_url();
774 if let Some(src) = element.get_attribute(&ns!(), &local_name!("src")) {
775 if script_type == ScriptType::ImportMap {
779 self.queue_error_event();
782 return;
783 }
784
785 let src = src.value();
787
788 if src.is_empty() {
790 self.queue_error_event();
791 return;
792 }
793
794 self.from_an_external_file.set(true);
796
797 let url = match base_url.join(&src) {
799 Ok(url) => url,
800 Err(_) => {
801 warn!("error parsing URL for script {}", &**src);
802 self.queue_error_event();
803 return;
804 },
805 };
806
807 match script_type {
814 ScriptType::Classic => {
815 let kind = if element.has_attribute(&local_name!("defer")) &&
816 was_parser_inserted &&
817 !asynch
818 {
819 ExternalScriptKind::Deferred
821 } else if was_parser_inserted && !asynch {
822 ExternalScriptKind::ParsingBlocking
824 } else if !asynch && !self.non_blocking.get() {
825 ExternalScriptKind::AsapInOrder
827 } else {
828 ExternalScriptKind::Asap
830 };
831
832 fetch_a_classic_script(self, kind, url, cors_setting, options, encoding);
834
835 match kind {
837 ExternalScriptKind::Deferred => doc.add_deferred_script(self),
838 ExternalScriptKind::ParsingBlocking => {
839 doc.set_pending_parsing_blocking_script(self, None)
840 },
841 ExternalScriptKind::AsapInOrder => doc.push_asap_in_order_script(self),
842 ExternalScriptKind::Asap => doc.add_asap_script(self),
843 }
844 },
845 ScriptType::Module => {
846 if integrity_val.is_none() {
849 options.integrity_metadata = global
850 .import_map()
851 .resolve_a_module_integrity_metadata(&url);
852 }
853
854 fetch_an_external_module_script(
856 url.clone(),
857 ModuleOwner::Window(Trusted::new(self)),
858 options,
859 can_gc,
860 );
861
862 if !asynch && was_parser_inserted {
863 doc.add_deferred_script(self);
865 } else if !asynch && !self.non_blocking.get() {
866 doc.push_asap_in_order_script(self);
868 } else {
869 doc.add_asap_script(self);
871 };
872 },
873 ScriptType::ImportMap => (),
874 }
875 } else {
876 assert!(!text.is_empty());
879
880 let text_rc = Rc::new(text.clone());
881
882 match script_type {
884 ScriptType::Classic => {
885 let script = self.global().create_a_classic_script(
888 std::borrow::Cow::Borrowed(&text.str()),
889 base_url,
890 options,
891 ErrorReporting::Unmuted,
892 introduction_type_override.or(Some(IntroductionType::INLINE_SCRIPT)),
893 self.line_number as u32,
894 false,
895 );
896 let result = Ok(Script::Classic(script));
897
898 if was_parser_inserted &&
899 doc.get_current_parser()
900 .is_some_and(|parser| parser.script_nesting_level() <= 1) &&
901 doc.get_script_blocking_stylesheets_count() > 0
902 {
903 doc.set_pending_parsing_blocking_script(self, Some(result));
905 } else {
906 self.execute(result, can_gc);
908 }
909 },
910 ScriptType::Module => {
911 if !asynch && was_parser_inserted {
915 doc.add_deferred_script(self);
916 } else if !asynch && !self.non_blocking.get() {
917 doc.push_asap_in_order_script(self);
918 } else {
919 doc.add_asap_script(self);
920 };
921
922 fetch_inline_module_script(
923 ModuleOwner::Window(Trusted::new(self)),
924 text_rc,
925 base_url.clone(),
926 options,
927 self.line_number as u32,
928 can_gc,
929 );
930 },
931 ScriptType::ImportMap => {
932 let import_map_result = parse_an_import_map_string(
935 ModuleOwner::Window(Trusted::new(self)),
936 Rc::clone(&text_rc),
937 base_url.clone(),
938 can_gc,
939 );
940 let script = Script::ImportMap(ScriptOrigin::internal(
941 text_rc,
942 base_url,
943 options,
944 script_type,
945 self.global().unminified_js_dir(),
946 import_map_result,
947 ));
948
949 self.execute(Ok(script), can_gc);
951 },
952 }
953 }
954 }
955
956 pub(crate) fn execute(&self, result: ScriptResult, can_gc: CanGc) {
958 let doc = self.owner_document();
960
961 if *doc != *self.preparation_time_document.get().unwrap() {
963 return;
964 }
965
966 let script = match result {
968 Err(_) => {
970 self.dispatch_error_event(can_gc);
971 return;
972 },
973
974 Ok(script) => script,
975 };
976
977 let neutralized_doc =
981 if self.from_an_external_file.get() || matches!(script, Script::Module(_)) {
982 let doc = self.owner_document();
983 doc.incr_ignore_destructive_writes_counter();
984 Some(doc)
985 } else {
986 None
987 };
988
989 let document = self.owner_document();
990
991 match script {
992 Script::Classic(script) => {
993 let old_script = document.GetCurrentScript();
995
996 if self.upcast::<Node>().is_in_a_shadow_tree() {
999 document.set_current_script(None)
1000 } else {
1001 document.set_current_script(Some(self))
1002 }
1003
1004 _ = self.owner_window().as_global_scope().run_a_classic_script(
1006 script,
1007 RethrowErrors::No,
1008 can_gc,
1009 );
1010
1011 document.set_current_script(old_script.as_deref());
1013 },
1014 Script::Module(module_tree) => {
1015 document.set_current_script(None);
1017
1018 self.run_a_module_script(module_tree, false, can_gc);
1020 },
1021 Script::ImportMap(script) => {
1022 register_import_map(&self.owner_global(), script.import_map, can_gc);
1024 },
1025 }
1026
1027 if let Some(doc) = neutralized_doc {
1030 doc.decr_ignore_destructive_writes_counter();
1031 }
1032
1033 if self.from_an_external_file.get() {
1035 self.dispatch_load_event(can_gc);
1036 }
1037 }
1038
1039 pub(crate) fn run_a_module_script(
1041 &self,
1042 module_tree: Rc<ModuleTree>,
1043 _rethrow_errors: bool,
1044 can_gc: CanGc,
1045 ) {
1046 let document = self.owner_document();
1049 if !document.is_fully_active() || !document.scripting_enabled() {
1050 return;
1051 }
1052
1053 let window = self.owner_window();
1055 let global = window.as_global_scope();
1056 let _aes = AutoEntryScript::new(global);
1057
1058 {
1060 let module_error = module_tree.get_rethrow_error().borrow();
1061 if module_error.is_some() {
1062 module_tree.report_error(global, can_gc);
1063 return;
1064 }
1065 }
1066
1067 let record = module_tree.get_record().map(|record| record.handle());
1068
1069 if let Some(record) = record {
1070 rooted!(in(*GlobalScope::get_cx()) let mut rval = UndefinedValue());
1071 let evaluated = module_tree.execute_module(global, record, rval.handle_mut(), can_gc);
1072
1073 if let Err(exception) = evaluated {
1074 module_tree.set_rethrow_error(exception);
1075 module_tree.report_error(global, can_gc);
1076 }
1077 }
1078 }
1079
1080 pub(crate) fn queue_error_event(&self) {
1081 self.owner_global()
1082 .task_manager()
1083 .dom_manipulation_task_source()
1084 .queue_simple_event(self.upcast(), atom!("error"));
1085 }
1086
1087 pub(crate) fn dispatch_load_event(&self, can_gc: CanGc) {
1088 self.dispatch_event(
1089 atom!("load"),
1090 EventBubbles::DoesNotBubble,
1091 EventCancelable::NotCancelable,
1092 can_gc,
1093 );
1094 }
1095
1096 pub(crate) fn dispatch_error_event(&self, can_gc: CanGc) {
1097 self.dispatch_event(
1098 atom!("error"),
1099 EventBubbles::DoesNotBubble,
1100 EventCancelable::NotCancelable,
1101 can_gc,
1102 );
1103 }
1104
1105 pub(crate) fn get_script_type(&self) -> Option<ScriptType> {
1107 let element = self.upcast::<Element>();
1108
1109 let type_attr = element.get_attribute(&ns!(), &local_name!("type"));
1110 let language_attr = element.get_attribute(&ns!(), &local_name!("language"));
1111
1112 match (
1113 type_attr.as_ref().map(|t| t.value()),
1114 language_attr.as_ref().map(|l| l.value()),
1115 ) {
1116 (Some(ref ty), _) if ty.is_empty() => {
1117 debug!("script type empty, inferring js");
1118 Some(ScriptType::Classic)
1119 },
1120 (None, Some(ref lang)) if lang.is_empty() => {
1121 debug!("script type empty, inferring js");
1122 Some(ScriptType::Classic)
1123 },
1124 (None, None) => {
1125 debug!("script type empty, inferring js");
1126 Some(ScriptType::Classic)
1127 },
1128 (None, Some(ref lang)) => {
1129 debug!("script language={}", &***lang);
1130 let language = format!("text/{}", &***lang);
1131
1132 if SCRIPT_JS_MIMES.contains(&language.to_ascii_lowercase().as_str()) {
1133 Some(ScriptType::Classic)
1134 } else {
1135 None
1136 }
1137 },
1138 (Some(ref ty), _) => {
1139 debug!("script type={}", &***ty);
1140
1141 if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "module" {
1142 return Some(ScriptType::Module);
1143 }
1144
1145 if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "importmap" {
1146 return Some(ScriptType::ImportMap);
1147 }
1148
1149 if SCRIPT_JS_MIMES
1150 .contains(&ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS))
1151 {
1152 Some(ScriptType::Classic)
1153 } else {
1154 None
1155 }
1156 },
1157 }
1158 }
1159
1160 pub(crate) fn set_parser_inserted(&self, parser_inserted: bool) {
1161 self.parser_inserted.set(parser_inserted);
1162 }
1163
1164 pub(crate) fn get_parser_inserted(&self) -> bool {
1165 self.parser_inserted.get()
1166 }
1167
1168 pub(crate) fn set_already_started(&self, already_started: bool) {
1169 self.already_started.set(already_started);
1170 }
1171
1172 pub(crate) fn get_non_blocking(&self) -> bool {
1173 self.non_blocking.get()
1174 }
1175
1176 fn dispatch_event(
1177 &self,
1178 type_: Atom,
1179 bubbles: EventBubbles,
1180 cancelable: EventCancelable,
1181 can_gc: CanGc,
1182 ) -> bool {
1183 let window = self.owner_window();
1184 let event = Event::new(window.upcast(), type_, bubbles, cancelable, can_gc);
1185 event.fire(self.upcast(), can_gc)
1186 }
1187
1188 fn text(&self) -> DOMString {
1189 match self.Text() {
1190 TrustedScriptOrString::String(value) => value,
1191 TrustedScriptOrString::TrustedScript(trusted_script) => {
1192 DOMString::from(trusted_script.to_string())
1193 },
1194 }
1195 }
1196}
1197
1198impl VirtualMethods for HTMLScriptElement {
1199 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1200 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1201 }
1202
1203 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1204 self.super_type()
1205 .unwrap()
1206 .attribute_mutated(attr, mutation, can_gc);
1207 if *attr.local_name() == local_name!("src") {
1208 if let AttributeMutation::Set(..) = mutation {
1209 if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
1210 self.prepare(Some(IntroductionType::INJECTED_SCRIPT), can_gc);
1211 }
1212 }
1213 }
1214 }
1215
1216 fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
1218 if let Some(s) = self.super_type() {
1219 s.children_changed(mutation, can_gc);
1220 }
1221
1222 if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1223 let script = DomRoot::from_ref(self);
1224 self.owner_document().add_delayed_task(
1228 task!(ScriptPrepare: |script: DomRoot<HTMLScriptElement>| {
1229 script.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
1230 }),
1231 );
1232 }
1233 }
1234
1235 fn post_connection_steps(&self, cx: &mut JSContext) {
1237 if let Some(s) = self.super_type() {
1238 s.post_connection_steps(cx);
1239 }
1240
1241 if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1242 self.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::from_cx(cx));
1243 }
1244 }
1245
1246 fn cloning_steps(
1247 &self,
1248 copy: &Node,
1249 maybe_doc: Option<&Document>,
1250 clone_children: CloneChildrenFlag,
1251 can_gc: CanGc,
1252 ) {
1253 if let Some(s) = self.super_type() {
1254 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
1255 }
1256
1257 if self.already_started.get() {
1259 copy.downcast::<HTMLScriptElement>()
1260 .unwrap()
1261 .set_already_started(true);
1262 }
1263 }
1264}
1265
1266impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement {
1267 fn Src(&self) -> TrustedScriptURLOrUSVString {
1269 let element = self.upcast::<Element>();
1270 element.get_trusted_type_url_attribute(&local_name!("src"))
1271 }
1272
1273 fn SetSrc(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> {
1275 let element = self.upcast::<Element>();
1276 let local_name = &local_name!("src");
1277 let value = TrustedScriptURL::get_trusted_script_url_compliant_string(
1278 &element.owner_global(),
1279 value,
1280 "HTMLScriptElement",
1281 local_name,
1282 can_gc,
1283 )?;
1284 element.set_attribute(
1285 local_name,
1286 AttrValue::String(value.str().to_owned()),
1287 can_gc,
1288 );
1289 Ok(())
1290 }
1291
1292 make_getter!(Type, "type");
1294 make_setter!(SetType, "type");
1296
1297 make_getter!(Charset, "charset");
1299 make_setter!(SetCharset, "charset");
1301
1302 fn Async(&self) -> bool {
1304 self.non_blocking.get() ||
1305 self.upcast::<Element>()
1306 .has_attribute(&local_name!("async"))
1307 }
1308
1309 fn SetAsync(&self, value: bool, can_gc: CanGc) {
1311 self.non_blocking.set(false);
1312 self.upcast::<Element>()
1313 .set_bool_attribute(&local_name!("async"), value, can_gc);
1314 }
1315
1316 make_bool_getter!(Defer, "defer");
1318 make_bool_setter!(SetDefer, "defer");
1320
1321 make_bool_getter!(NoModule, "nomodule");
1323 make_bool_setter!(SetNoModule, "nomodule");
1325
1326 make_getter!(Integrity, "integrity");
1328 make_setter!(SetIntegrity, "integrity");
1330
1331 make_getter!(Event, "event");
1333 make_setter!(SetEvent, "event");
1335
1336 make_getter!(HtmlFor, "for");
1338 make_setter!(SetHtmlFor, "for");
1340
1341 fn GetCrossOrigin(&self) -> Option<DOMString> {
1343 reflect_cross_origin_attribute(self.upcast::<Element>())
1344 }
1345
1346 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1348 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1349 }
1350
1351 fn ReferrerPolicy(&self) -> DOMString {
1353 reflect_referrer_policy_attribute(self.upcast::<Element>())
1354 }
1355
1356 make_setter!(SetReferrerPolicy, "referrerpolicy");
1358
1359 fn InnerText(&self) -> TrustedScriptOrString {
1361 TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text())
1363 }
1364
1365 fn SetInnerText(&self, input: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1367 let value = TrustedScript::get_trusted_script_compliant_string(
1370 &self.owner_global(),
1371 input,
1372 "HTMLScriptElement innerText",
1373 can_gc,
1374 )?;
1375 *self.script_text.borrow_mut() = value.clone();
1376 self.upcast::<HTMLElement>().set_inner_text(value, can_gc);
1378 Ok(())
1379 }
1380
1381 fn Text(&self) -> TrustedScriptOrString {
1383 TrustedScriptOrString::String(self.upcast::<Node>().child_text_content())
1384 }
1385
1386 fn SetText(&self, value: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1388 let value = TrustedScript::get_trusted_script_compliant_string(
1391 &self.owner_global(),
1392 value,
1393 "HTMLScriptElement text",
1394 can_gc,
1395 )?;
1396 *self.script_text.borrow_mut() = value.clone();
1398 Node::string_replace_all(value, self.upcast::<Node>(), can_gc);
1400 Ok(())
1401 }
1402
1403 fn GetTextContent(&self) -> Option<TrustedScriptOrString> {
1405 Some(TrustedScriptOrString::String(
1407 self.upcast::<Node>().GetTextContent()?,
1408 ))
1409 }
1410
1411 fn SetTextContent(&self, value: Option<TrustedScriptOrString>, can_gc: CanGc) -> Fallible<()> {
1413 let value = TrustedScript::get_trusted_script_compliant_string(
1416 &self.owner_global(),
1417 value.unwrap_or(TrustedScriptOrString::String(DOMString::from(""))),
1418 "HTMLScriptElement textContent",
1419 can_gc,
1420 )?;
1421 *self.script_text.borrow_mut() = value.clone();
1423 self.upcast::<Node>()
1425 .set_text_content_for_element(Some(value), can_gc);
1426 Ok(())
1427 }
1428
1429 fn Supports(_window: &Window, type_: DOMString) -> bool {
1431 matches!(&*type_.str(), "classic" | "module" | "importmap")
1434 }
1435}
1436
1437pub(crate) fn substitute_with_local_script(
1438 window: &Window,
1439 script: &mut Cow<'_, str>,
1440 url: ServoUrl,
1441) {
1442 if window.local_script_source().is_none() {
1443 return;
1444 }
1445 let mut path = PathBuf::from(window.local_script_source().clone().unwrap());
1446 path = path.join(&url[url::Position::BeforeHost..]);
1447 debug!("Attempting to read script stored at: {:?}", path);
1448 match read_to_string(path.clone()) {
1449 Ok(local_script) => {
1450 debug!("Found script stored at: {:?}", path);
1451 *script = Cow::Owned(local_script);
1452 },
1453 Err(why) => warn!("Could not restore script from file {:?}", why),
1454 }
1455}
1456
1457#[derive(Clone, Copy)]
1458enum ExternalScriptKind {
1459 Deferred,
1460 ParsingBlocking,
1461 AsapInOrder,
1462 Asap,
1463}