script/dom/html/
htmlscriptelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4use 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 script_bindings::domstring::BytesView;
27use servo_url::{ImmutableOrigin, ServoUrl};
28use style::attr::AttrValue;
29use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
30use stylo_atoms::Atom;
31use uuid::Uuid;
32
33use crate::document_loader::LoadType;
34use crate::dom::attr::Attr;
35use crate::dom::bindings::cell::DomRefCell;
36use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
37use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
38use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
39use crate::dom::bindings::codegen::UnionTypes::{
40    TrustedScriptOrString, TrustedScriptURLOrUSVString,
41};
42use crate::dom::bindings::error::{Error, Fallible};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::reflector::DomGlobal;
46use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
47use crate::dom::bindings::settings_stack::AutoEntryScript;
48use crate::dom::bindings::str::DOMString;
49use crate::dom::bindings::trace::NoTrace;
50use crate::dom::csp::{CspReporting, GlobalCspReporting, InlineCheckType, Violation};
51use crate::dom::document::Document;
52use crate::dom::element::{
53    AttributeMutation, Element, ElementCreator, cors_setting_for_element,
54    referrer_policy_for_element, reflect_cross_origin_attribute, reflect_referrer_policy_attribute,
55    set_cross_origin_attribute,
56};
57use crate::dom::event::{Event, EventBubbles, EventCancelable};
58use crate::dom::globalscope::GlobalScope;
59use crate::dom::html::htmlelement::HTMLElement;
60use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeTraits};
61use crate::dom::performance::performanceresourcetiming::InitiatorType;
62use crate::dom::trustedscript::TrustedScript;
63use crate::dom::trustedscripturl::TrustedScriptURL;
64use crate::dom::virtualmethods::VirtualMethods;
65use crate::dom::window::Window;
66use crate::fetch::create_a_potential_cors_request;
67use crate::network_listener::{self, PreInvoke, ResourceTimingListener};
68use crate::realms::enter_realm;
69use crate::script_module::{
70    ImportMap, ModuleOwner, ScriptFetchOptions, fetch_external_module_script,
71    fetch_inline_module_script, parse_an_import_map_string, register_import_map,
72};
73use crate::script_runtime::{CanGc, IntroductionType};
74use crate::unminify::{ScriptSource, unminify_js};
75
76impl ScriptSource for ScriptOrigin {
77    fn unminified_dir(&self) -> Option<String> {
78        self.unminified_dir.clone()
79    }
80
81    fn extract_bytes(&self) -> BytesView<'_> {
82        match &self.code {
83            SourceCode::Text(text) => text.as_bytes(),
84            SourceCode::Compiled(compiled_source_code) => {
85                compiled_source_code.original_text.as_bytes()
86            },
87        }
88    }
89
90    fn rewrite_source(&mut self, source: Rc<DOMString>) {
91        self.code = SourceCode::Text(source);
92    }
93
94    fn url(&self) -> ServoUrl {
95        self.url.clone()
96    }
97
98    fn is_external(&self) -> bool {
99        self.external
100    }
101}
102
103/// An unique id for script element.
104#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
105pub(crate) struct ScriptId(#[no_trace] Uuid);
106
107#[dom_struct]
108pub(crate) struct HTMLScriptElement {
109    htmlelement: HTMLElement,
110
111    /// <https://html.spec.whatwg.org/multipage/#already-started>
112    already_started: Cell<bool>,
113
114    /// <https://html.spec.whatwg.org/multipage/#parser-inserted>
115    parser_inserted: Cell<bool>,
116
117    /// <https://html.spec.whatwg.org/multipage/#non-blocking>
118    ///
119    /// (currently unused)
120    non_blocking: Cell<bool>,
121
122    /// Document of the parser that created this element
123    /// <https://html.spec.whatwg.org/multipage/#parser-document>
124    parser_document: Dom<Document>,
125
126    /// Prevents scripts that move between documents during preparation from executing.
127    /// <https://html.spec.whatwg.org/multipage/#preparation-time-document>
128    preparation_time_document: MutNullableDom<Document>,
129
130    /// Track line line_number
131    line_number: u64,
132
133    /// Unique id for each script element
134    #[ignore_malloc_size_of = "Defined in uuid"]
135    id: ScriptId,
136
137    /// <https://w3c.github.io/trusted-types/dist/spec/#htmlscriptelement-script-text>
138    script_text: DomRefCell<DOMString>,
139
140    /// `introductionType` value to set in the `CompileOptionsWrapper`, overriding the usual
141    /// `srcScript` or `inlineScript` that this script would normally use.
142    #[no_trace]
143    introduction_type_override: Cell<Option<&'static CStr>>,
144}
145
146impl HTMLScriptElement {
147    fn new_inherited(
148        local_name: LocalName,
149        prefix: Option<Prefix>,
150        document: &Document,
151        creator: ElementCreator,
152    ) -> HTMLScriptElement {
153        HTMLScriptElement {
154            id: ScriptId(Uuid::new_v4()),
155            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
156            already_started: Cell::new(false),
157            parser_inserted: Cell::new(creator.is_parser_created()),
158            non_blocking: Cell::new(!creator.is_parser_created()),
159            parser_document: Dom::from_ref(document),
160            preparation_time_document: MutNullableDom::new(None),
161            line_number: creator.return_line_number(),
162            script_text: DomRefCell::new(DOMString::new()),
163            introduction_type_override: Cell::new(None),
164        }
165    }
166
167    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
168    pub(crate) fn new(
169        local_name: LocalName,
170        prefix: Option<Prefix>,
171        document: &Document,
172        proto: Option<HandleObject>,
173        creator: ElementCreator,
174        can_gc: CanGc,
175    ) -> DomRoot<HTMLScriptElement> {
176        Node::reflect_node_with_proto(
177            Box::new(HTMLScriptElement::new_inherited(
178                local_name, prefix, document, creator,
179            )),
180            document,
181            proto,
182            can_gc,
183        )
184    }
185
186    pub(crate) fn get_script_id(&self) -> ScriptId {
187        self.id
188    }
189}
190
191/// Supported script types as defined by
192/// <https://html.spec.whatwg.org/multipage/#javascript-mime-type>.
193pub(crate) static SCRIPT_JS_MIMES: StaticStringVec = &[
194    "application/ecmascript",
195    "application/javascript",
196    "application/x-ecmascript",
197    "application/x-javascript",
198    "text/ecmascript",
199    "text/javascript",
200    "text/javascript1.0",
201    "text/javascript1.1",
202    "text/javascript1.2",
203    "text/javascript1.3",
204    "text/javascript1.4",
205    "text/javascript1.5",
206    "text/jscript",
207    "text/livescript",
208    "text/x-ecmascript",
209    "text/x-javascript",
210];
211
212#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
213pub(crate) enum ScriptType {
214    Classic,
215    Module,
216    ImportMap,
217}
218
219#[derive(JSTraceable, MallocSizeOf)]
220pub(crate) struct CompiledSourceCode {
221    #[ignore_malloc_size_of = "SM handles JS values"]
222    pub(crate) source_code: Stencil,
223    #[conditional_malloc_size_of = "Rc is hard"]
224    pub(crate) original_text: Rc<DOMString>,
225}
226
227#[derive(JSTraceable, MallocSizeOf)]
228pub(crate) enum SourceCode {
229    Text(#[conditional_malloc_size_of] Rc<DOMString>),
230    Compiled(CompiledSourceCode),
231}
232
233#[derive(JSTraceable, MallocSizeOf)]
234pub(crate) struct ScriptOrigin {
235    pub code: SourceCode,
236    #[no_trace]
237    pub url: ServoUrl,
238    external: bool,
239    pub fetch_options: ScriptFetchOptions,
240    type_: ScriptType,
241    unminified_dir: Option<String>,
242    import_map: Fallible<ImportMap>,
243}
244
245impl ScriptOrigin {
246    pub(crate) fn internal(
247        text: Rc<DOMString>,
248        url: ServoUrl,
249        fetch_options: ScriptFetchOptions,
250        type_: ScriptType,
251        unminified_dir: Option<String>,
252        import_map: Fallible<ImportMap>,
253    ) -> ScriptOrigin {
254        ScriptOrigin {
255            code: SourceCode::Text(text),
256            url,
257            external: false,
258            fetch_options,
259            type_,
260            unminified_dir,
261            import_map,
262        }
263    }
264
265    pub(crate) fn external(
266        text: Rc<DOMString>,
267        url: ServoUrl,
268        fetch_options: ScriptFetchOptions,
269        type_: ScriptType,
270        unminified_dir: Option<String>,
271    ) -> ScriptOrigin {
272        ScriptOrigin {
273            code: SourceCode::Text(text),
274            url,
275            external: true,
276            fetch_options,
277            type_,
278            unminified_dir,
279            import_map: Err(Error::NotFound(None)),
280        }
281    }
282
283    pub(crate) fn text(&self) -> Rc<DOMString> {
284        match &self.code {
285            SourceCode::Text(text) => Rc::clone(text),
286            SourceCode::Compiled(compiled_script) => Rc::clone(&compiled_script.original_text),
287        }
288    }
289}
290
291/// Final steps of <https://html.spec.whatwg.org/multipage/#prepare-the-script-element>
292fn finish_fetching_a_classic_script(
293    elem: &HTMLScriptElement,
294    script_kind: ExternalScriptKind,
295    url: ServoUrl,
296    load: ScriptResult,
297    can_gc: CanGc,
298) {
299    // Step 33. The "steps to run when the result is ready" for each type of script in 33.2-33.5.
300    // of https://html.spec.whatwg.org/multipage/#prepare-the-script-element
301    let document;
302
303    match script_kind {
304        ExternalScriptKind::Asap => {
305            document = elem.preparation_time_document.get().unwrap();
306            document.asap_script_loaded(elem, load, can_gc)
307        },
308        ExternalScriptKind::AsapInOrder => {
309            document = elem.preparation_time_document.get().unwrap();
310            document.asap_in_order_script_loaded(elem, load, can_gc)
311        },
312        ExternalScriptKind::Deferred => {
313            document = elem.parser_document.as_rooted();
314            document.deferred_script_loaded(elem, load, can_gc);
315        },
316        ExternalScriptKind::ParsingBlocking => {
317            document = elem.parser_document.as_rooted();
318            document.pending_parsing_blocking_script_loaded(elem, load, can_gc);
319        },
320    }
321
322    document.finish_load(LoadType::Script(url), can_gc);
323}
324
325pub(crate) type ScriptResult = Result<ScriptOrigin, NoTrace<NetworkError>>;
326
327/// The context required for asynchronously loading an external script source.
328struct ClassicContext {
329    /// The element that initiated the request.
330    elem: Trusted<HTMLScriptElement>,
331    /// The kind of external script.
332    kind: ExternalScriptKind,
333    /// The (fallback) character encoding argument to the "fetch a classic
334    /// script" algorithm.
335    character_encoding: &'static Encoding,
336    /// The response body received to date.
337    data: Vec<u8>,
338    /// The response metadata received to date.
339    metadata: Option<Metadata>,
340    /// The initial URL requested.
341    url: ServoUrl,
342    /// Indicates whether the request failed, and why
343    status: Result<(), NetworkError>,
344    /// The fetch options of the script
345    fetch_options: ScriptFetchOptions,
346    /// Timing object for this resource
347    resource_timing: ResourceFetchTiming,
348}
349
350impl FetchResponseListener for ClassicContext {
351    // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
352    fn process_request_body(&mut self, _: RequestId) {}
353
354    // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
355    fn process_request_eof(&mut self, _: RequestId) {}
356
357    fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
358        self.metadata = metadata.ok().map(|meta| match meta {
359            FetchMetadata::Unfiltered(m) => m,
360            FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
361        });
362
363        let status = self
364            .metadata
365            .as_ref()
366            .map(|m| m.status.clone())
367            .unwrap_or_else(HttpStatus::new_error);
368
369        self.status = {
370            if status.is_error() {
371                Err(NetworkError::Internal(
372                    "No http status code received".to_owned(),
373                ))
374            } else if status.is_success() {
375                Ok(())
376            } else {
377                Err(NetworkError::Internal(format!(
378                    "HTTP error code {}",
379                    status.code()
380                )))
381            }
382        };
383    }
384
385    fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec<u8>) {
386        if self.status.is_ok() {
387            self.data.append(&mut chunk);
388        }
389    }
390
391    /// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
392    /// step 4-9
393    #[allow(unsafe_code)]
394    fn process_response_eof(
395        &mut self,
396        _: RequestId,
397        response: Result<ResourceFetchTiming, NetworkError>,
398    ) {
399        let (source_text, final_url) = match (response.as_ref(), self.status.as_ref()) {
400            (Err(err), _) | (_, Err(err)) => {
401                // Step 6, response is an error.
402                finish_fetching_a_classic_script(
403                    &self.elem.root(),
404                    self.kind,
405                    self.url.clone(),
406                    Err(NoTrace(err.clone())),
407                    CanGc::note(),
408                );
409                return;
410            },
411            (Ok(_), Ok(_)) => {
412                let metadata = self.metadata.take().unwrap();
413
414                // Step 7.
415                let encoding = metadata
416                    .charset
417                    .and_then(|encoding| Encoding::for_label(encoding.as_bytes()))
418                    .unwrap_or(self.character_encoding);
419
420                // Step 8.
421                let (source_text, _, _) = encoding.decode(&self.data);
422                (source_text, metadata.final_url)
423            },
424        };
425
426        let elem = self.elem.root();
427        let global = elem.global();
428        // let cx = GlobalScope::get_cx();
429        let _ar = enter_realm(&*global);
430
431        /*
432        let options = unsafe { CompileOptionsWrapper::new(*cx, final_url.as_str(), 1) };
433
434        let can_compile_off_thread = pref!(dom_script_asynch) &&
435            unsafe { CanCompileOffThread(*cx, options.ptr as *const _, source_text.len()) };
436
437        if can_compile_off_thread {
438            let source_string = source_text.to_string();
439
440            let context = Box::new(OffThreadCompilationContext {
441                script_element: self.elem.clone(),
442                script_kind: self.kind,
443                final_url,
444                url: self.url.clone(),
445                task_source: elem.owner_global().task_manager().dom_manipulation_task_source(),
446                script_text: source_string,
447                fetch_options: self.fetch_options.clone(),
448            });
449
450            unsafe {
451                assert!(!CompileToStencilOffThread1(
452                    *cx,
453                    options.ptr as *const _,
454                    &mut transform_str_to_source_text(&context.script_text) as *mut _,
455                    Some(off_thread_compilation_callback),
456                    Box::into_raw(context) as *mut c_void,
457                )
458                .is_null());
459            }
460        } else {*/
461        let load = ScriptOrigin::external(
462            Rc::new(DOMString::from(source_text)),
463            final_url.clone(),
464            self.fetch_options.clone(),
465            ScriptType::Classic,
466            elem.parser_document.global().unminified_js_dir(),
467        );
468        finish_fetching_a_classic_script(
469            &elem,
470            self.kind,
471            self.url.clone(),
472            Ok(load),
473            CanGc::note(),
474        );
475        // }
476    }
477
478    fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
479        &mut self.resource_timing
480    }
481
482    fn resource_timing(&self) -> &ResourceFetchTiming {
483        &self.resource_timing
484    }
485
486    fn submit_resource_timing(&mut self) {
487        network_listener::submit_timing(self, CanGc::note())
488    }
489
490    fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
491        let global = &self.resource_timing_global();
492        let elem = self.elem.root();
493        let source_position = elem
494            .upcast::<Element>()
495            .compute_source_position(elem.line_number as u32);
496        global.report_csp_violations(violations, Some(elem.upcast()), Some(source_position));
497    }
498}
499
500impl ResourceTimingListener for ClassicContext {
501    fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
502        let initiator_type = InitiatorType::LocalName(
503            self.elem
504                .root()
505                .upcast::<Element>()
506                .local_name()
507                .to_string(),
508        );
509        (initiator_type, self.url.clone())
510    }
511
512    fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
513        self.elem.root().owner_document().global()
514    }
515}
516
517impl PreInvoke for ClassicContext {}
518
519/// Steps 1-2 of <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
520// This function is also used to prefetch a script in `script::dom::servoparser::prefetch`.
521#[allow(clippy::too_many_arguments)]
522pub(crate) fn script_fetch_request(
523    webview_id: WebViewId,
524    url: ServoUrl,
525    cors_setting: Option<CorsSettings>,
526    origin: ImmutableOrigin,
527    pipeline_id: PipelineId,
528    options: ScriptFetchOptions,
529    insecure_requests_policy: InsecureRequestsPolicy,
530    has_trustworthy_ancestor_origin: bool,
531    policy_container: PolicyContainer,
532) -> RequestBuilder {
533    // We intentionally ignore options' credentials_mode member for classic scripts.
534    // The mode is initialized by create_a_potential_cors_request.
535    create_a_potential_cors_request(
536        Some(webview_id),
537        url,
538        Destination::Script,
539        cors_setting,
540        None,
541        options.referrer,
542        insecure_requests_policy,
543        has_trustworthy_ancestor_origin,
544        policy_container,
545    )
546    .origin(origin)
547    .pipeline_id(Some(pipeline_id))
548    .parser_metadata(options.parser_metadata)
549    .integrity_metadata(options.integrity_metadata.clone())
550    .referrer_policy(options.referrer_policy)
551    .cryptographic_nonce_metadata(options.cryptographic_nonce)
552}
553
554/// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
555fn fetch_a_classic_script(
556    script: &HTMLScriptElement,
557    kind: ExternalScriptKind,
558    url: ServoUrl,
559    cors_setting: Option<CorsSettings>,
560    options: ScriptFetchOptions,
561    character_encoding: &'static Encoding,
562) {
563    // Step 1, 2.
564    let doc = script.owner_document();
565    let global = script.global();
566    let request = script_fetch_request(
567        doc.webview_id(),
568        url.clone(),
569        cors_setting,
570        doc.origin().immutable().clone(),
571        global.pipeline_id(),
572        options.clone(),
573        doc.insecure_requests_policy(),
574        doc.has_trustworthy_ancestor_origin(),
575        global.policy_container(),
576    );
577    let request = doc.prepare_request(request);
578
579    // TODO: Step 3, Add custom steps to perform fetch
580
581    let context = ClassicContext {
582        elem: Trusted::new(script),
583        kind,
584        character_encoding,
585        data: vec![],
586        metadata: None,
587        url: url.clone(),
588        status: Ok(()),
589        fetch_options: options,
590        resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
591    };
592    doc.fetch(LoadType::Script(url), request, context);
593}
594
595impl HTMLScriptElement {
596    /// <https://w3c.github.io/trusted-types/dist/spec/#setting-slot-values-from-parser>
597    pub(crate) fn set_initial_script_text(&self) {
598        *self.script_text.borrow_mut() = self.text();
599    }
600
601    /// <https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-prepare-the-script-text>
602    fn prepare_the_script_text(&self, can_gc: CanGc) -> Fallible<()> {
603        // Step 1. If script’s script text value is not equal to its child text content,
604        // set script’s script text to the result of executing
605        // Get Trusted Type compliant string, with the following arguments:
606        if self.script_text.borrow().clone() != self.text() {
607            *self.script_text.borrow_mut() = TrustedScript::get_trusted_script_compliant_string(
608                &self.owner_global(),
609                self.Text(),
610                "HTMLScriptElement text",
611                can_gc,
612            )?;
613        }
614
615        Ok(())
616    }
617
618    /// <https://html.spec.whatwg.org/multipage/#prepare-the-script-element>
619    pub(crate) fn prepare(&self, introduction_type_override: Option<&'static CStr>, can_gc: CanGc) {
620        self.introduction_type_override
621            .set(introduction_type_override);
622
623        // Step 1. If el's already started is true, then return.
624        if self.already_started.get() {
625            return;
626        }
627
628        // Step 2. Let parser document be el's parser document.
629        // TODO
630
631        // Step 3. Set el's parser document to null.
632        let was_parser_inserted = self.parser_inserted.get();
633        self.parser_inserted.set(false);
634
635        // Step 4.
636        // If parser document is non-null and el does not have an async attribute, then set el's force async to true.
637        let element = self.upcast::<Element>();
638        let asynch = element.has_attribute(&local_name!("async"));
639        // Note: confusingly, this is done if the element does *not* have an "async" attribute.
640        if was_parser_inserted && !asynch {
641            self.non_blocking.set(true);
642        }
643
644        // Step 5. Execute the Prepare the script text algorithm on el.
645        // If that algorithm threw an error, then return.
646        if self.prepare_the_script_text(can_gc).is_err() {
647            return;
648        }
649        // Step 5a. Let source text be el’s script text value.
650        let text = self.script_text.borrow().clone();
651        // Step 6. If el has no src attribute, and source text is the empty string, then return.
652        if text.is_empty() && !element.has_attribute(&local_name!("src")) {
653            return;
654        }
655
656        // Step 7. If el is not connected, then return.
657        if !self.upcast::<Node>().is_connected() {
658            return;
659        }
660
661        let script_type = if let Some(ty) = self.get_script_type() {
662            // Step 9-11.
663            ty
664        } else {
665            // Step 12. Otherwise, return. (No script is executed, and el's type is left as null.)
666            return;
667        };
668
669        // Step 13.
670        // If parser document is non-null, then set el's parser document back to parser document and set el's force
671        // async to false.
672        if was_parser_inserted {
673            self.parser_inserted.set(true);
674            self.non_blocking.set(false);
675        }
676
677        // Step 14. Set el's already started to true.
678        self.already_started.set(true);
679
680        // Step 15. Set el's preparation-time document to its node document.
681        let doc = self.owner_document();
682        self.preparation_time_document.set(Some(&doc));
683
684        // Step 16.
685        // If parser document is non-null, and parser document is not equal to el's preparation-time document, then
686        // return.
687        if self.parser_inserted.get() && *self.parser_document != *doc {
688            return;
689        }
690
691        // Step 17. If scripting is disabled for el, then return.
692        if !doc.scripting_enabled() {
693            return;
694        }
695
696        // Step 18. If el has a nomodule content attribute and its type is "classic", then return.
697        if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
698            return;
699        }
700
701        let global = &doc.global();
702
703        // Step 19. CSP.
704        if !element.has_attribute(&local_name!("src")) &&
705            global
706                .get_csp_list()
707                .should_elements_inline_type_behavior_be_blocked(
708                    global,
709                    element,
710                    InlineCheckType::Script,
711                    &text.str(),
712                )
713        {
714            warn!("Blocking inline script due to CSP");
715            return;
716        }
717
718        // Step 20. If el has an event attribute and a for attribute, and el's type is "classic", then:
719        if script_type == ScriptType::Classic {
720            let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
721            let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
722            if let (Some(ref for_attribute), Some(ref event_attribute)) =
723                (for_attribute, event_attribute)
724            {
725                let for_value = for_attribute.value().to_ascii_lowercase();
726                let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
727                if for_value != "window" {
728                    return;
729                }
730
731                let event_value = event_attribute.value().to_ascii_lowercase();
732                let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
733                if event_value != "onload" && event_value != "onload()" {
734                    return;
735                }
736            }
737        }
738
739        // Step 21. If el has a charset attribute, then let encoding be the result of getting
740        // an encoding from the value of the charset attribute.
741        // If el does not have a charset attribute, or if getting an encoding failed,
742        // then let encoding be el's node document's the encoding.
743        let encoding = element
744            .get_attribute(&ns!(), &local_name!("charset"))
745            .and_then(|charset| Encoding::for_label(charset.value().as_bytes()))
746            .unwrap_or_else(|| doc.encoding());
747
748        // Step 22. CORS setting.
749        let cors_setting = cors_setting_for_element(element);
750
751        // Step 23. Module script credentials mode.
752        let module_credentials_mode = match script_type {
753            ScriptType::Classic => CredentialsMode::CredentialsSameOrigin,
754            ScriptType::Module | ScriptType::ImportMap => reflect_cross_origin_attribute(element)
755                .map_or(
756                    CredentialsMode::CredentialsSameOrigin,
757                    |attr| match &*attr.str() {
758                        "use-credentials" => CredentialsMode::Include,
759                        "anonymous" => CredentialsMode::CredentialsSameOrigin,
760                        _ => CredentialsMode::CredentialsSameOrigin,
761                    },
762                ),
763        };
764
765        // Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
766        let cryptographic_nonce = self.upcast::<Element>().nonce_value();
767
768        // Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
769        // Otherwise, let integrity metadata be the empty string.
770        let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
771        let integrity_val = im_attribute.as_ref().map(|a| a.value());
772        let integrity_metadata = match integrity_val {
773            Some(ref value) => &***value,
774            None => "",
775        };
776
777        // Step 26. Let referrer policy be the current state of el's referrerpolicy content attribute.
778        let referrer_policy = referrer_policy_for_element(self.upcast::<Element>());
779
780        // TODO: Step 27. Fetch priority.
781
782        // Step 28. Let parser metadata be "parser-inserted" if el is parser-inserted,
783        // and "not-parser-inserted" otherwise.
784        let parser_metadata = if self.parser_inserted.get() {
785            ParserMetadata::ParserInserted
786        } else {
787            ParserMetadata::NotParserInserted
788        };
789
790        // Step 29. Fetch options.
791        let options = ScriptFetchOptions {
792            cryptographic_nonce,
793            integrity_metadata: integrity_metadata.to_owned(),
794            parser_metadata,
795            referrer: self.global().get_referrer(),
796            referrer_policy,
797            credentials_mode: module_credentials_mode,
798        };
799
800        // Step 30. Let settings object be el's node document's relevant settings object.
801        // This is done by passing ModuleOwner in step 31.11 and step 32.2.
802        // What we actually need is global's import map eventually.
803
804        let base_url = doc.base_url();
805        if let Some(src) = element.get_attribute(&ns!(), &local_name!("src")) {
806            // Step 31. If el has a src content attribute, then:
807
808            // Step 31.1. If el's type is "importmap".
809            if script_type == ScriptType::ImportMap {
810                // then queue an element task on the DOM manipulation task source
811                // given el to fire an event named error at el, and return.
812                self.queue_error_event();
813                return;
814            }
815
816            // Step 31.2. Let src be the value of el's src attribute.
817            let src = src.value();
818
819            // Step 31.3. If src is the empty string.
820            if src.is_empty() {
821                self.queue_error_event();
822                return;
823            }
824
825            // Step 31.4. Set el's from an external file to true.
826            // The "from an external file"" flag is stored in ScriptOrigin.
827
828            // Step 31.5-31.6. Parse URL.
829            let url = match base_url.join(&src) {
830                Ok(url) => url,
831                Err(_) => {
832                    warn!("error parsing URL for script {}", &**src);
833                    self.queue_error_event();
834                    return;
835                },
836            };
837
838            // TODO:
839            // Step 31.7. If el is potentially render-blocking, then block rendering on el.
840            // Step 31.8. Set el's delaying the load event to true.
841            // Step 31.9. If el is currently render-blocking, then set options's render-blocking to true.
842
843            // Step 31.11. Switch on el's type:
844            match script_type {
845                ScriptType::Classic => {
846                    let kind = if element.has_attribute(&local_name!("defer")) &&
847                        was_parser_inserted &&
848                        !asynch
849                    {
850                        // Step 33.4: classic, has src, has defer, was parser-inserted, is not async.
851                        ExternalScriptKind::Deferred
852                    } else if was_parser_inserted && !asynch {
853                        // Step 33.5: classic, has src, was parser-inserted, is not async.
854                        ExternalScriptKind::ParsingBlocking
855                    } else if !asynch && !self.non_blocking.get() {
856                        // Step 33.3: classic, has src, is not async, is not non-blocking.
857                        ExternalScriptKind::AsapInOrder
858                    } else {
859                        // Step 33.2: classic, has src.
860                        ExternalScriptKind::Asap
861                    };
862
863                    // Step 31.11. Fetch a classic script.
864                    fetch_a_classic_script(self, kind, url, cors_setting, options, encoding);
865
866                    // Step 33.2/33.3/33.4/33.5, substeps 1-2. Add el to the corresponding script list.
867                    match kind {
868                        ExternalScriptKind::Deferred => doc.add_deferred_script(self),
869                        ExternalScriptKind::ParsingBlocking => {
870                            doc.set_pending_parsing_blocking_script(self, None)
871                        },
872                        ExternalScriptKind::AsapInOrder => doc.push_asap_in_order_script(self),
873                        ExternalScriptKind::Asap => doc.add_asap_script(self),
874                    }
875                },
876                ScriptType::Module => {
877                    // Step 31.11. Fetch an external module script graph.
878                    fetch_external_module_script(
879                        ModuleOwner::Window(Trusted::new(self)),
880                        url.clone(),
881                        Destination::Script,
882                        options,
883                        can_gc,
884                    );
885
886                    if !asynch && was_parser_inserted {
887                        // 33.4: module, not async, parser-inserted
888                        doc.add_deferred_script(self);
889                    } else if !asynch && !self.non_blocking.get() {
890                        // 33.3: module, not parser-inserted
891                        doc.push_asap_in_order_script(self);
892                    } else {
893                        // 33.2: module, async
894                        doc.add_asap_script(self);
895                    };
896                },
897                ScriptType::ImportMap => (),
898            }
899        } else {
900            // Step 32. If el does not have a src content attribute:
901
902            assert!(!text.is_empty());
903
904            let text_rc = Rc::new(text);
905
906            // Step 32.2: Switch on el's type:
907            match script_type {
908                ScriptType::Classic => {
909                    let result = Ok(ScriptOrigin::internal(
910                        text_rc,
911                        base_url,
912                        options,
913                        script_type,
914                        self.global().unminified_js_dir(),
915                        Err(Error::NotFound(None)),
916                    ));
917
918                    if was_parser_inserted &&
919                        doc.get_current_parser()
920                            .is_some_and(|parser| parser.script_nesting_level() <= 1) &&
921                        doc.get_script_blocking_stylesheets_count() > 0
922                    {
923                        // Step 34.2: classic, has no src, was parser-inserted, is blocked on stylesheet.
924                        doc.set_pending_parsing_blocking_script(self, Some(result));
925                    } else {
926                        // Step 34.3: otherwise.
927                        self.execute(result, can_gc);
928                    }
929                },
930                ScriptType::Module => {
931                    // We should add inline module script elements
932                    // into those vectors in case that there's no
933                    // descendants in the inline module script.
934                    if !asynch && was_parser_inserted {
935                        doc.add_deferred_script(self);
936                    } else if !asynch && !self.non_blocking.get() {
937                        doc.push_asap_in_order_script(self);
938                    } else {
939                        doc.add_asap_script(self);
940                    };
941
942                    fetch_inline_module_script(
943                        ModuleOwner::Window(Trusted::new(self)),
944                        text_rc,
945                        base_url.clone(),
946                        self.id,
947                        options,
948                        self.line_number,
949                        can_gc,
950                    );
951                },
952                ScriptType::ImportMap => {
953                    // Step 32.1 Let result be the result of creating an import map
954                    // parse result given source text and base URL.
955                    let import_map_result = parse_an_import_map_string(
956                        ModuleOwner::Window(Trusted::new(self)),
957                        Rc::clone(&text_rc),
958                        base_url.clone(),
959                        can_gc,
960                    );
961                    let result = Ok(ScriptOrigin::internal(
962                        text_rc,
963                        base_url,
964                        options,
965                        script_type,
966                        self.global().unminified_js_dir(),
967                        import_map_result,
968                    ));
969
970                    // Step 34.3
971                    self.execute(result, can_gc);
972                },
973            }
974        }
975    }
976
977    fn substitute_with_local_script(&self, script: &mut ScriptOrigin) {
978        if self
979            .parser_document
980            .window()
981            .local_script_source()
982            .is_none() ||
983            !script.external
984        {
985            return;
986        }
987        let mut path = PathBuf::from(
988            self.parser_document
989                .window()
990                .local_script_source()
991                .clone()
992                .unwrap(),
993        );
994        path = path.join(&script.url[url::Position::BeforeHost..]);
995        debug!("Attempting to read script stored at: {:?}", path);
996        match read_to_string(path.clone()) {
997            Ok(local_script) => {
998                debug!("Found script stored at: {:?}", path);
999                script.code = SourceCode::Text(Rc::new(DOMString::from(local_script)));
1000            },
1001            Err(why) => warn!("Could not restore script from file {:?}", why),
1002        }
1003    }
1004
1005    /// <https://html.spec.whatwg.org/multipage/#execute-the-script-element>
1006    pub(crate) fn execute(&self, result: ScriptResult, can_gc: CanGc) {
1007        // Step 1. Let document be el's node document.
1008        let doc = self.owner_document();
1009
1010        // Step 2. If el's preparation-time document is not equal to document, then return.
1011        if *doc != *self.preparation_time_document.get().unwrap() {
1012            return;
1013        }
1014
1015        // TODO: Step 3. Unblock rendering on el.
1016        let mut script = match result {
1017            // Step 4. If el's result is null, then fire an event named error at el, and return.
1018            Err(e) => {
1019                warn!("error loading script {:?}", e);
1020                self.dispatch_error_event(can_gc);
1021                return;
1022            },
1023
1024            Ok(script) => script,
1025        };
1026
1027        if script.type_ == ScriptType::Classic {
1028            unminify_js(&mut script);
1029            self.substitute_with_local_script(&mut script);
1030        }
1031
1032        // Step 5.
1033        // If el's from an external file is true, or el's type is "module", then increment document's
1034        // ignore-destructive-writes counter.
1035        let neutralized_doc = if script.external || script.type_ == ScriptType::Module {
1036            debug!("loading external script, url = {}", script.url);
1037            let doc = self.owner_document();
1038            doc.incr_ignore_destructive_writes_counter();
1039            Some(doc)
1040        } else {
1041            None
1042        };
1043
1044        // Step 6.
1045        let document = self.owner_document();
1046        let old_script = document.GetCurrentScript();
1047        let introduction_type =
1048            self.introduction_type_override
1049                .get()
1050                .unwrap_or(if script.external {
1051                    IntroductionType::SRC_SCRIPT
1052                } else {
1053                    IntroductionType::INLINE_SCRIPT
1054                });
1055
1056        match script.type_ {
1057            ScriptType::Classic => {
1058                if self.upcast::<Node>().is_in_a_shadow_tree() {
1059                    document.set_current_script(None)
1060                } else {
1061                    document.set_current_script(Some(self))
1062                }
1063                let line_number = if script.external {
1064                    1
1065                } else {
1066                    self.line_number as u32
1067                };
1068                self.owner_window().as_global_scope().run_a_classic_script(
1069                    &script,
1070                    line_number,
1071                    Some(introduction_type),
1072                    can_gc,
1073                );
1074                document.set_current_script(old_script.as_deref());
1075            },
1076            ScriptType::Module => {
1077                document.set_current_script(None);
1078                self.run_a_module_script(&script, false, can_gc);
1079            },
1080            ScriptType::ImportMap => {
1081                // Step 6.1 Register an import map given el's relevant global object and el's result.
1082                register_import_map(&self.owner_global(), script.import_map, can_gc);
1083            },
1084        }
1085
1086        // Step 7.
1087        // Decrement the ignore-destructive-writes counter of document, if it was incremented in the earlier step.
1088        if let Some(doc) = neutralized_doc {
1089            doc.decr_ignore_destructive_writes_counter();
1090        }
1091
1092        // Step 8. If el's from an external file is true, then fire an event named load at el.
1093        if script.external {
1094            self.dispatch_load_event(can_gc);
1095        }
1096    }
1097
1098    #[allow(unsafe_code)]
1099    /// <https://html.spec.whatwg.org/multipage/#run-a-module-script>
1100    pub(crate) fn run_a_module_script(
1101        &self,
1102        script: &ScriptOrigin,
1103        _rethrow_errors: bool,
1104        can_gc: CanGc,
1105    ) {
1106        // TODO use a settings object rather than this element's document/window
1107        // Step 2
1108        let document = self.owner_document();
1109        if !document.is_fully_active() || !document.scripting_enabled() {
1110            return;
1111        }
1112
1113        // Step 4
1114        let window = self.owner_window();
1115        let global = window.as_global_scope();
1116        let _aes = AutoEntryScript::new(global);
1117
1118        let tree = if script.external {
1119            global.get_module_map().borrow().get(&script.url).cloned()
1120        } else {
1121            global
1122                .get_inline_module_map()
1123                .borrow()
1124                .get(&self.id.clone())
1125                .cloned()
1126        };
1127
1128        if let Some(module_tree) = tree {
1129            // Step 6.
1130            {
1131                let module_error = module_tree.get_rethrow_error().borrow();
1132                let network_error = module_tree.get_network_error().borrow();
1133                if module_error.is_some() && network_error.is_none() {
1134                    module_tree.report_error(global, can_gc);
1135                    return;
1136                }
1137            }
1138
1139            let record = module_tree
1140                .get_record()
1141                .borrow()
1142                .as_ref()
1143                .map(|record| record.handle());
1144
1145            if let Some(record) = record {
1146                rooted!(in(*GlobalScope::get_cx()) let mut rval = UndefinedValue());
1147                let evaluated =
1148                    module_tree.execute_module(global, record, rval.handle_mut().into(), can_gc);
1149
1150                if let Err(exception) = evaluated {
1151                    module_tree.set_rethrow_error(exception);
1152                    module_tree.report_error(global, can_gc);
1153                }
1154            }
1155        }
1156    }
1157
1158    pub(crate) fn queue_error_event(&self) {
1159        self.owner_global()
1160            .task_manager()
1161            .dom_manipulation_task_source()
1162            .queue_simple_event(self.upcast(), atom!("error"));
1163    }
1164
1165    pub(crate) fn dispatch_load_event(&self, can_gc: CanGc) {
1166        self.dispatch_event(
1167            atom!("load"),
1168            EventBubbles::DoesNotBubble,
1169            EventCancelable::NotCancelable,
1170            can_gc,
1171        );
1172    }
1173
1174    pub(crate) fn dispatch_error_event(&self, can_gc: CanGc) {
1175        self.dispatch_event(
1176            atom!("error"),
1177            EventBubbles::DoesNotBubble,
1178            EventCancelable::NotCancelable,
1179            can_gc,
1180        );
1181    }
1182
1183    // https://html.spec.whatwg.org/multipage/#prepare-a-script Step 7.
1184    pub(crate) fn get_script_type(&self) -> Option<ScriptType> {
1185        let element = self.upcast::<Element>();
1186
1187        let type_attr = element.get_attribute(&ns!(), &local_name!("type"));
1188        let language_attr = element.get_attribute(&ns!(), &local_name!("language"));
1189
1190        match (
1191            type_attr.as_ref().map(|t| t.value()),
1192            language_attr.as_ref().map(|l| l.value()),
1193        ) {
1194            (Some(ref ty), _) if ty.is_empty() => {
1195                debug!("script type empty, inferring js");
1196                Some(ScriptType::Classic)
1197            },
1198            (None, Some(ref lang)) if lang.is_empty() => {
1199                debug!("script type empty, inferring js");
1200                Some(ScriptType::Classic)
1201            },
1202            (None, None) => {
1203                debug!("script type empty, inferring js");
1204                Some(ScriptType::Classic)
1205            },
1206            (None, Some(ref lang)) => {
1207                debug!("script language={}", &***lang);
1208                let language = format!("text/{}", &***lang);
1209
1210                if SCRIPT_JS_MIMES.contains(&language.to_ascii_lowercase().as_str()) {
1211                    Some(ScriptType::Classic)
1212                } else {
1213                    None
1214                }
1215            },
1216            (Some(ref ty), _) => {
1217                debug!("script type={}", &***ty);
1218
1219                if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "module" {
1220                    return Some(ScriptType::Module);
1221                }
1222
1223                if ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) == "importmap" {
1224                    return Some(ScriptType::ImportMap);
1225                }
1226
1227                if SCRIPT_JS_MIMES
1228                    .contains(&ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS))
1229                {
1230                    Some(ScriptType::Classic)
1231                } else {
1232                    None
1233                }
1234            },
1235        }
1236    }
1237
1238    pub(crate) fn set_parser_inserted(&self, parser_inserted: bool) {
1239        self.parser_inserted.set(parser_inserted);
1240    }
1241
1242    pub(crate) fn get_parser_inserted(&self) -> bool {
1243        self.parser_inserted.get()
1244    }
1245
1246    pub(crate) fn set_already_started(&self, already_started: bool) {
1247        self.already_started.set(already_started);
1248    }
1249
1250    pub(crate) fn get_non_blocking(&self) -> bool {
1251        self.non_blocking.get()
1252    }
1253
1254    fn dispatch_event(
1255        &self,
1256        type_: Atom,
1257        bubbles: EventBubbles,
1258        cancelable: EventCancelable,
1259        can_gc: CanGc,
1260    ) -> bool {
1261        let window = self.owner_window();
1262        let event = Event::new(window.upcast(), type_, bubbles, cancelable, can_gc);
1263        event.fire(self.upcast(), can_gc)
1264    }
1265
1266    fn text(&self) -> DOMString {
1267        match self.Text() {
1268            TrustedScriptOrString::String(value) => value,
1269            TrustedScriptOrString::TrustedScript(trusted_script) => {
1270                DOMString::from(trusted_script.to_string())
1271            },
1272        }
1273    }
1274}
1275
1276impl VirtualMethods for HTMLScriptElement {
1277    fn super_type(&self) -> Option<&dyn VirtualMethods> {
1278        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1279    }
1280
1281    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1282        self.super_type()
1283            .unwrap()
1284            .attribute_mutated(attr, mutation, can_gc);
1285        if *attr.local_name() == local_name!("src") {
1286            if let AttributeMutation::Set(_) = mutation {
1287                if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
1288                    self.prepare(Some(IntroductionType::INJECTED_SCRIPT), can_gc);
1289                }
1290            }
1291        }
1292    }
1293
1294    /// <https://html.spec.whatwg.org/multipage/#script-processing-model:the-script-element-26>
1295    fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
1296        if let Some(s) = self.super_type() {
1297            s.children_changed(mutation, can_gc);
1298        }
1299
1300        if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1301            let script = DomRoot::from_ref(self);
1302            // This method can be invoked while there are script/layout blockers present
1303            // as DOM mutations have not yet settled. We use a delayed task to avoid
1304            // running any scripts until the DOM tree is safe for interactions.
1305            self.owner_document().add_delayed_task(
1306                task!(ScriptPrepare: |script: DomRoot<HTMLScriptElement>| {
1307                    script.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
1308                }),
1309            );
1310        }
1311    }
1312
1313    /// <https://html.spec.whatwg.org/multipage/#script-processing-model:the-script-element-20>
1314    fn post_connection_steps(&self, can_gc: CanGc) {
1315        if let Some(s) = self.super_type() {
1316            s.post_connection_steps(can_gc);
1317        }
1318
1319        if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
1320            self.prepare(Some(IntroductionType::INJECTED_SCRIPT), can_gc);
1321        }
1322    }
1323
1324    fn cloning_steps(
1325        &self,
1326        copy: &Node,
1327        maybe_doc: Option<&Document>,
1328        clone_children: CloneChildrenFlag,
1329        can_gc: CanGc,
1330    ) {
1331        if let Some(s) = self.super_type() {
1332            s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
1333        }
1334
1335        // https://html.spec.whatwg.org/multipage/#already-started
1336        if self.already_started.get() {
1337            copy.downcast::<HTMLScriptElement>()
1338                .unwrap()
1339                .set_already_started(true);
1340        }
1341    }
1342}
1343
1344impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement {
1345    /// <https://html.spec.whatwg.org/multipage/#dom-script-src>
1346    fn Src(&self) -> TrustedScriptURLOrUSVString {
1347        let element = self.upcast::<Element>();
1348        element.get_trusted_type_url_attribute(&local_name!("src"))
1349    }
1350
1351    /// <https://w3c.github.io/trusted-types/dist/spec/#the-src-idl-attribute>
1352    fn SetSrc(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> {
1353        let element = self.upcast::<Element>();
1354        let local_name = &local_name!("src");
1355        let value = TrustedScriptURL::get_trusted_script_url_compliant_string(
1356            &element.owner_global(),
1357            value,
1358            "HTMLScriptElement",
1359            local_name,
1360            can_gc,
1361        )?;
1362        element.set_attribute(
1363            local_name,
1364            AttrValue::String(value.str().to_owned()),
1365            can_gc,
1366        );
1367        Ok(())
1368    }
1369
1370    // https://html.spec.whatwg.org/multipage/#dom-script-type
1371    make_getter!(Type, "type");
1372    // https://html.spec.whatwg.org/multipage/#dom-script-type
1373    make_setter!(SetType, "type");
1374
1375    // https://html.spec.whatwg.org/multipage/#dom-script-charset
1376    make_getter!(Charset, "charset");
1377    // https://html.spec.whatwg.org/multipage/#dom-script-charset
1378    make_setter!(SetCharset, "charset");
1379
1380    /// <https://html.spec.whatwg.org/multipage/#dom-script-async>
1381    fn Async(&self) -> bool {
1382        self.non_blocking.get() ||
1383            self.upcast::<Element>()
1384                .has_attribute(&local_name!("async"))
1385    }
1386
1387    /// <https://html.spec.whatwg.org/multipage/#dom-script-async>
1388    fn SetAsync(&self, value: bool, can_gc: CanGc) {
1389        self.non_blocking.set(false);
1390        self.upcast::<Element>()
1391            .set_bool_attribute(&local_name!("async"), value, can_gc);
1392    }
1393
1394    // https://html.spec.whatwg.org/multipage/#dom-script-defer
1395    make_bool_getter!(Defer, "defer");
1396    // https://html.spec.whatwg.org/multipage/#dom-script-defer
1397    make_bool_setter!(SetDefer, "defer");
1398
1399    // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
1400    make_bool_getter!(NoModule, "nomodule");
1401    // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
1402    make_bool_setter!(SetNoModule, "nomodule");
1403
1404    // https://html.spec.whatwg.org/multipage/#dom-script-integrity
1405    make_getter!(Integrity, "integrity");
1406    // https://html.spec.whatwg.org/multipage/#dom-script-integrity
1407    make_setter!(SetIntegrity, "integrity");
1408
1409    // https://html.spec.whatwg.org/multipage/#dom-script-event
1410    make_getter!(Event, "event");
1411    // https://html.spec.whatwg.org/multipage/#dom-script-event
1412    make_setter!(SetEvent, "event");
1413
1414    // https://html.spec.whatwg.org/multipage/#dom-script-htmlfor
1415    make_getter!(HtmlFor, "for");
1416    // https://html.spec.whatwg.org/multipage/#dom-script-htmlfor
1417    make_setter!(SetHtmlFor, "for");
1418
1419    /// <https://html.spec.whatwg.org/multipage/#dom-script-crossorigin>
1420    fn GetCrossOrigin(&self) -> Option<DOMString> {
1421        reflect_cross_origin_attribute(self.upcast::<Element>())
1422    }
1423
1424    /// <https://html.spec.whatwg.org/multipage/#dom-script-crossorigin>
1425    fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1426        set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1427    }
1428
1429    /// <https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy>
1430    fn ReferrerPolicy(&self) -> DOMString {
1431        reflect_referrer_policy_attribute(self.upcast::<Element>())
1432    }
1433
1434    // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
1435    make_setter!(SetReferrerPolicy, "referrerpolicy");
1436
1437    /// <https://w3c.github.io/trusted-types/dist/spec/#dom-htmlscriptelement-innertext>
1438    fn InnerText(&self) -> TrustedScriptOrString {
1439        // Step 1: Return the result of running get the text steps with this.
1440        TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text())
1441    }
1442
1443    /// <https://w3c.github.io/trusted-types/dist/spec/#the-innerText-idl-attribute>
1444    fn SetInnerText(&self, input: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1445        // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript,
1446        // this's relevant global object, the given value, HTMLScriptElement innerText, and script.
1447        let value = TrustedScript::get_trusted_script_compliant_string(
1448            &self.owner_global(),
1449            input,
1450            "HTMLScriptElement innerText",
1451            can_gc,
1452        )?;
1453        *self.script_text.borrow_mut() = value.clone();
1454        // Step 3: Run set the inner text steps with this and value.
1455        self.upcast::<HTMLElement>().set_inner_text(value, can_gc);
1456        Ok(())
1457    }
1458
1459    /// <https://html.spec.whatwg.org/multipage/#dom-script-text>
1460    fn Text(&self) -> TrustedScriptOrString {
1461        TrustedScriptOrString::String(self.upcast::<Node>().child_text_content())
1462    }
1463
1464    /// <https://w3c.github.io/trusted-types/dist/spec/#the-text-idl-attribute>
1465    fn SetText(&self, value: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> {
1466        // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript,
1467        // this's relevant global object, the given value, HTMLScriptElement text, and script.
1468        let value = TrustedScript::get_trusted_script_compliant_string(
1469            &self.owner_global(),
1470            value,
1471            "HTMLScriptElement text",
1472            can_gc,
1473        )?;
1474        // Step 2: Set this's script text value to the given value.
1475        *self.script_text.borrow_mut() = value.clone();
1476        // Step 3: String replace all with the given value within this.
1477        Node::string_replace_all(value, self.upcast::<Node>(), can_gc);
1478        Ok(())
1479    }
1480
1481    /// <https://w3c.github.io/trusted-types/dist/spec/#the-textContent-idl-attribute>
1482    fn GetTextContent(&self) -> Option<TrustedScriptOrString> {
1483        // Step 1: Return the result of running get text content with this.
1484        Some(TrustedScriptOrString::String(
1485            self.upcast::<Node>().GetTextContent()?,
1486        ))
1487    }
1488
1489    /// <https://w3c.github.io/trusted-types/dist/spec/#the-textContent-idl-attribute>
1490    fn SetTextContent(&self, value: Option<TrustedScriptOrString>, can_gc: CanGc) -> Fallible<()> {
1491        // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript,
1492        // this's relevant global object, the given value, HTMLScriptElement textContent, and script.
1493        let value = TrustedScript::get_trusted_script_compliant_string(
1494            &self.owner_global(),
1495            value.unwrap_or(TrustedScriptOrString::String(DOMString::from(""))),
1496            "HTMLScriptElement textContent",
1497            can_gc,
1498        )?;
1499        // Step 2: Set this's script text value to value.
1500        *self.script_text.borrow_mut() = value.clone();
1501        // Step 3: Run set text content with this and value.
1502        self.upcast::<Node>()
1503            .set_text_content_for_element(Some(value), can_gc);
1504        Ok(())
1505    }
1506
1507    /// <https://html.spec.whatwg.org/multipage/#dom-script-supports>
1508    fn Supports(_window: &Window, type_: DOMString) -> bool {
1509        // The type argument has to exactly match these values,
1510        // we do not perform an ASCII case-insensitive match.
1511        matches!(&*type_.str(), "classic" | "module" | "importmap")
1512    }
1513}
1514
1515#[derive(Clone, Copy)]
1516enum ExternalScriptKind {
1517    Deferred,
1518    ParsingBlocking,
1519    AsapInOrder,
1520    Asap,
1521}