aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/htmlscriptelement.rs
diff options
context:
space:
mode:
authoryvt <i@yvt.jp>2021-07-10 17:24:27 +0900
committeryvt <i@yvt.jp>2021-07-10 17:55:42 +0900
commit01a7de50ab1843d85295f9dccad7f4c099e7208c (patch)
treeee53fb6e8889deb7b880ee969e6c662e6128d210 /components/script/dom/htmlscriptelement.rs
parentff8d2cdbbfc7a9dc7f38b7dd47cb350fde39388f (diff)
parent94b613fbdaa2b98f2179fc0bbda13c64e6fa0d38 (diff)
downloadservo-01a7de50ab1843d85295f9dccad7f4c099e7208c.tar.gz
servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.zip
Merge remote-tracking branch 'upstream/master' into feat-cow-infra
`tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html` was reverted to the upstream version.
Diffstat (limited to 'components/script/dom/htmlscriptelement.rs')
-rw-r--r--components/script/dom/htmlscriptelement.rs1259
1 files changed, 934 insertions, 325 deletions
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index d6679c5168c..d27fee98fb5 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -1,100 +1,222 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use document_loader::LoadType;
-use dom::attr::Attr;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
-use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
-use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
-use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
-use dom::bindings::inheritance::Castable;
-use dom::bindings::js::{JS, Root};
-use dom::bindings::js::RootedReference;
-use dom::bindings::refcounted::Trusted;
-use dom::bindings::reflector::DomObject;
-use dom::bindings::str::DOMString;
-use dom::document::Document;
-use dom::element::{AttributeMutation, Element, ElementCreator};
-use dom::element::{cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute};
-use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
-use dom::globalscope::GlobalScope;
-use dom::htmlelement::HTMLElement;
-use dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
-use dom::node::{document_from_node, window_from_node};
-use dom::virtualmethods::VirtualMethods;
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::document_loader::LoadType;
+use crate::dom::attr::Attr;
+use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
+use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::refcounted::Trusted;
+use crate::dom::bindings::reflector::DomObject;
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::settings_stack::AutoEntryScript;
+use crate::dom::bindings::str::{DOMString, USVString};
+use crate::dom::bindings::trace::RootedTraceableBox;
+use crate::dom::document::Document;
+use crate::dom::element::{
+ cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
+ reflect_referrer_policy_attribute, set_cross_origin_attribute,
+};
+use crate::dom::element::{AttributeMutation, Element, ElementCreator};
+use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::htmlelement::HTMLElement;
+use crate::dom::node::{document_from_node, window_from_node};
+use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node};
+use crate::dom::performanceresourcetiming::InitiatorType;
+use crate::dom::virtualmethods::VirtualMethods;
+use crate::fetch::create_a_potential_cors_request;
+use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
+use crate::realms::enter_realm;
+use crate::script_module::fetch_inline_module_script;
+use crate::script_module::{fetch_external_module_script, ModuleOwner, ScriptFetchOptions};
+use crate::task::TaskCanceller;
+use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
+use crate::task_source::TaskSource;
+use crate::task_source::TaskSourceName;
+use content_security_policy as csp;
+use core::ffi::c_void;
use dom_struct::dom_struct;
-use encoding::label::encoding_from_whatwg_label;
-use encoding::types::{DecoderTrap, EncodingRef};
-use html5ever_atoms::LocalName;
+use encoding_rs::Encoding;
+use html5ever::{LocalName, Prefix};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
+use js::jsapi::{
+ CanCompileOffThread, CompileOffThread1, FinishOffThreadScript, Heap, JSScript, OffThreadToken,
+};
use js::jsval::UndefinedValue;
-use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
-use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType};
-use network_listener::{NetworkListener, PreInvoke};
+use js::rust::{transform_str_to_source_text, CompileOptionsWrapper};
+use msg::constellation_msg::PipelineId;
+use net_traits::request::{
+ CorsSettings, CredentialsMode, Destination, ParserMetadata, RequestBuilder,
+};
+use net_traits::{
+ FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming,
+ ResourceTimingType,
+};
use servo_atoms::Atom;
-use servo_config::opts;
+use servo_config::pref;
+use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;
-use std::ascii::AsciiExt;
use std::cell::Cell;
-use std::fs::File;
-use std::io::{Read, Write};
+use std::fs::{create_dir_all, read_to_string, File};
+use std::io::{Read, Seek, Write};
+use std::mem::replace;
use std::path::PathBuf;
-use std::process::{Command, Stdio};
+use std::process::Command;
+use std::rc::Rc;
use std::sync::{Arc, Mutex};
-use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
+use style::str::{StaticStringVec, HTML_SPACE_CHARACTERS};
use uuid::Uuid;
+pub struct OffThreadCompilationContext {
+ script_element: Trusted<HTMLScriptElement>,
+ script_kind: ExternalScriptKind,
+ final_url: ServoUrl,
+ url: ServoUrl,
+ task_source: DOMManipulationTaskSource,
+ canceller: TaskCanceller,
+ script_text: String,
+ fetch_options: ScriptFetchOptions,
+}
+
+/// A wrapper to mark OffThreadToken as Send,
+/// which should be safe according to
+/// mozjs/js/public/OffThreadScriptCompilation.h
+struct OffThreadCompilationToken(*mut OffThreadToken);
+
+#[allow(unsafe_code)]
+unsafe impl Send for OffThreadCompilationToken {}
+
+#[allow(unsafe_code)]
+unsafe extern "C" fn off_thread_compilation_callback(
+ token: *mut OffThreadToken,
+ callback_data: *mut c_void,
+) {
+ let mut context = Box::from_raw(callback_data as *mut OffThreadCompilationContext);
+ let token = OffThreadCompilationToken(token);
+
+ let url = context.url.clone();
+ let final_url = context.final_url.clone();
+ let script_element = context.script_element.clone();
+ let script_kind = context.script_kind.clone();
+ let script = replace(&mut context.script_text, String::new());
+ let fetch_options = context.fetch_options.clone();
+
+ // Continue with <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
+ let _ = context.task_source.queue_with_canceller(
+ task!(off_thread_compile_continue: move || {
+ let elem = script_element.root();
+ let global = elem.global();
+ let cx = global.get_cx();
+ let _ar = enter_realm(&*global);
+
+ rooted!(in(*cx)
+ let compiled_script = FinishOffThreadScript(*cx, token.0)
+ );
+
+ let load = if compiled_script.get().is_null() {
+ Err(NetworkError::Internal(
+ "Off-thread compilation failed.".into(),
+ ))
+ } else {
+ let script_text = DOMString::from(script);
+ let heap = Heap::default();
+ let source_code = RootedTraceableBox::new(heap);
+ source_code.set(compiled_script.get());
+ let code = SourceCode::Compiled(CompiledSourceCode {
+ source_code: source_code,
+ original_text: Rc::new(script_text),
+ });
+
+ Ok(ScriptOrigin {
+ code,
+ url: final_url,
+ external: true,
+ fetch_options: fetch_options,
+ type_: ScriptType::Classic,
+ })
+ };
+
+ finish_fetching_a_classic_script(&*elem, script_kind, url, load);
+ }),
+ &context.canceller,
+ );
+}
+
+/// An unique id for script element.
+#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
+pub struct ScriptId(Uuid);
+
#[dom_struct]
pub struct HTMLScriptElement {
htmlelement: HTMLElement,
- /// https://html.spec.whatwg.org/multipage/#already-started
+ /// <https://html.spec.whatwg.org/multipage/#already-started>
already_started: Cell<bool>,
- /// https://html.spec.whatwg.org/multipage/#parser-inserted
+ /// <https://html.spec.whatwg.org/multipage/#parser-inserted>
parser_inserted: Cell<bool>,
- /// https://html.spec.whatwg.org/multipage/#non-blocking
+ /// <https://html.spec.whatwg.org/multipage/#non-blocking>
///
/// (currently unused)
non_blocking: Cell<bool>,
/// Document of the parser that created this element
- parser_document: JS<Document>,
+ parser_document: Dom<Document>,
/// Track line line_number
line_number: u64,
+
+ /// Unique id for each script element
+ #[ignore_malloc_size_of = "Defined in uuid"]
+ id: ScriptId,
}
impl HTMLScriptElement {
- fn new_inherited(local_name: LocalName, prefix: Option<DOMString>, document: &Document,
- creator: ElementCreator) -> HTMLScriptElement {
+ fn new_inherited(
+ local_name: LocalName,
+ prefix: Option<Prefix>,
+ document: &Document,
+ creator: ElementCreator,
+ ) -> HTMLScriptElement {
HTMLScriptElement {
- htmlelement:
- HTMLElement::new_inherited(local_name, prefix, document),
+ id: ScriptId(Uuid::new_v4()),
+ htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
already_started: Cell::new(false),
parser_inserted: Cell::new(creator.is_parser_created()),
non_blocking: Cell::new(!creator.is_parser_created()),
- parser_document: JS::from_ref(document),
+ parser_document: Dom::from_ref(document),
line_number: creator.return_line_number(),
}
}
#[allow(unrooted_must_root)]
- pub fn new(local_name: LocalName, prefix: Option<DOMString>, document: &Document,
- creator: ElementCreator) -> Root<HTMLScriptElement> {
- Node::reflect_node(box HTMLScriptElement::new_inherited(local_name, prefix, document, creator),
- document,
- HTMLScriptElementBinding::Wrap)
+ pub fn new(
+ local_name: LocalName,
+ prefix: Option<Prefix>,
+ document: &Document,
+ creator: ElementCreator,
+ ) -> DomRoot<HTMLScriptElement> {
+ Node::reflect_node(
+ Box::new(HTMLScriptElement::new_inherited(
+ local_name, prefix, document, creator,
+ )),
+ document,
+ )
}
-}
+ pub fn get_script_id(&self) -> ScriptId {
+ self.id.clone()
+ }
+}
/// Supported script types as defined by
/// <https://html.spec.whatwg.org/multipage/#javascript-mime-type>.
-static SCRIPT_JS_MIMES: StaticStringVec = &[
+pub static SCRIPT_JS_MIMES: StaticStringVec = &[
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
@@ -113,42 +235,110 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[
"text/x-javascript",
];
-#[derive(HeapSizeOf, JSTraceable)]
-pub struct ClassicScript {
- text: DOMString,
+#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
+pub enum ScriptType {
+ Classic,
+ Module,
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct CompiledSourceCode {
+ #[ignore_malloc_size_of = "SM handles JS values"]
+ pub source_code: RootedTraceableBox<Heap<*mut JSScript>>,
+ #[ignore_malloc_size_of = "Rc is hard"]
+ pub original_text: Rc<DOMString>,
+}
+
+#[derive(JSTraceable)]
+pub enum SourceCode {
+ Text(Rc<DOMString>),
+ Compiled(CompiledSourceCode),
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct ScriptOrigin {
+ #[ignore_malloc_size_of = "Rc is hard"]
+ code: SourceCode,
url: ServoUrl,
external: bool,
+ fetch_options: ScriptFetchOptions,
+ type_: ScriptType,
}
-impl ClassicScript {
- fn internal(text: DOMString, url: ServoUrl) -> ClassicScript {
- ClassicScript {
- text: text,
+impl ScriptOrigin {
+ pub fn internal(
+ text: Rc<DOMString>,
+ url: ServoUrl,
+ fetch_options: ScriptFetchOptions,
+ type_: ScriptType,
+ ) -> ScriptOrigin {
+ ScriptOrigin {
+ code: SourceCode::Text(text),
url: url,
external: false,
+ fetch_options,
+ type_,
}
}
- fn external(text: DOMString, url: ServoUrl) -> ClassicScript {
- ClassicScript {
- text: text,
+ pub fn external(
+ text: Rc<DOMString>,
+ url: ServoUrl,
+ fetch_options: ScriptFetchOptions,
+ type_: ScriptType,
+ ) -> ScriptOrigin {
+ ScriptOrigin {
+ code: SourceCode::Text(text),
url: url,
external: true,
+ fetch_options,
+ type_,
+ }
+ }
+
+ pub fn text(&self) -> Rc<DOMString> {
+ match &self.code {
+ SourceCode::Text(text) => Rc::clone(&text),
+ SourceCode::Compiled(compiled_script) => Rc::clone(&compiled_script.original_text),
}
}
}
-pub type ScriptResult = Result<ClassicScript, NetworkError>;
+/// Final steps of <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
+fn finish_fetching_a_classic_script(
+ elem: &HTMLScriptElement,
+ script_kind: ExternalScriptKind,
+ url: ServoUrl,
+ load: ScriptResult,
+) {
+ // Step 11, Asynchronously complete this algorithm with script,
+ // which refers to step 26.6 "When the chosen algorithm asynchronously completes",
+ // of https://html.spec.whatwg.org/multipage/#prepare-a-script
+ let document = document_from_node(&*elem);
+
+ match script_kind {
+ ExternalScriptKind::Asap => document.asap_script_loaded(&elem, load),
+ ExternalScriptKind::AsapInOrder => document.asap_in_order_script_loaded(&elem, load),
+ ExternalScriptKind::Deferred => document.deferred_script_loaded(&elem, load),
+ ExternalScriptKind::ParsingBlocking => {
+ document.pending_parsing_blocking_script_loaded(&elem, load)
+ },
+ }
+
+ document.finish_load(LoadType::Script(url));
+}
+
+pub type ScriptResult = Result<ScriptOrigin, NetworkError>;
/// The context required for asynchronously loading an external script source.
-struct ScriptContext {
+struct ClassicContext {
/// The element that initiated the request.
elem: Trusted<HTMLScriptElement>,
/// The kind of external script.
kind: ExternalScriptKind,
/// The (fallback) character encoding argument to the "fetch a classic
/// script" algorithm.
- character_encoding: EncodingRef,
+ character_encoding: &'static Encoding,
/// The response body received to date.
data: Vec<u8>,
/// The response metadata received to date.
@@ -156,32 +346,42 @@ struct ScriptContext {
/// The initial URL requested.
url: ServoUrl,
/// Indicates whether the request failed, and why
- status: Result<(), NetworkError>
+ status: Result<(), NetworkError>,
+ /// The fetch options of the script
+ fetch_options: ScriptFetchOptions,
+ /// Timing object for this resource
+ resource_timing: ResourceFetchTiming,
}
-impl FetchResponseListener for ScriptContext {
+impl FetchResponseListener for ClassicContext {
fn process_request_body(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
fn process_request_eof(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
- fn process_response(&mut self,
- metadata: Result<FetchMetadata, NetworkError>) {
+ fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
self.metadata = metadata.ok().map(|meta| match meta {
FetchMetadata::Unfiltered(m) => m,
- FetchMetadata::Filtered { unsafe_, .. } => unsafe_
+ FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
});
- let status_code = self.metadata.as_ref().and_then(|m| {
- match m.status {
+ let status_code = self
+ .metadata
+ .as_ref()
+ .and_then(|m| match m.status {
Some((c, _)) => Some(c),
_ => None,
- }
- }).unwrap_or(0);
+ })
+ .unwrap_or(0);
self.status = match status_code {
- 0 => Err(NetworkError::Internal("No http status code received".to_owned())),
- 200...299 => Ok(()), // HTTP ok status codes
- _ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code)))
+ 0 => Err(NetworkError::Internal(
+ "No http status code received".to_owned(),
+ )),
+ 200..=299 => Ok(()), // HTTP ok status codes
+ _ => Err(NetworkError::Internal(format!(
+ "HTTP error code {}",
+ status_code
+ ))),
};
}
@@ -191,103 +391,194 @@ impl FetchResponseListener for ScriptContext {
}
}
- /// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
+ /// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
/// step 4-9
- fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
- // Step 5.
- let load = response.and(self.status.clone()).map(|_| {
- let metadata = self.metadata.take().unwrap();
+ #[allow(unsafe_code)]
+ fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) {
+ let (source_text, final_url) = match (response.as_ref(), self.status.as_ref()) {
+ (Err(err), _) | (_, Err(err)) => {
+ // Step 6, response is an error.
+ finish_fetching_a_classic_script(
+ &*self.elem.root(),
+ self.kind.clone(),
+ self.url.clone(),
+ Err(err.clone()),
+ );
+ return;
+ },
+ (Ok(_), Ok(_)) => {
+ let metadata = self.metadata.take().unwrap();
+
+ // Step 7.
+ let encoding = metadata
+ .charset
+ .and_then(|encoding| Encoding::for_label(encoding.as_bytes()))
+ .unwrap_or(self.character_encoding);
+
+ // Step 8.
+ let (source_text, _, _) = encoding.decode(&self.data);
+ (source_text, metadata.final_url)
+ },
+ };
- // Step 6.
- let encoding = metadata.charset
- .and_then(|encoding| encoding_from_whatwg_label(&encoding))
- .unwrap_or(self.character_encoding);
+ let elem = self.elem.root();
+ let global = elem.global();
+ let cx = global.get_cx();
+ let _ar = enter_realm(&*global);
+
+ let options = unsafe { CompileOptionsWrapper::new(*cx, final_url.as_str(), 1) };
+
+ let can_compile_off_thread = pref!(dom.script.asynch) &&
+ unsafe { CanCompileOffThread(*cx, options.ptr as *const _, source_text.len()) };
+
+ if can_compile_off_thread {
+ let source_string = source_text.to_string();
+
+ let context = Box::new(OffThreadCompilationContext {
+ script_element: self.elem.clone(),
+ script_kind: self.kind.clone(),
+ final_url,
+ url: self.url.clone(),
+ task_source: global.dom_manipulation_task_source(),
+ canceller: global.task_canceller(TaskSourceName::DOMManipulation),
+ script_text: source_string,
+ fetch_options: self.fetch_options.clone(),
+ });
+
+ unsafe {
+ assert!(!CompileOffThread1(
+ *cx,
+ options.ptr as *const _,
+ &mut transform_str_to_source_text(&context.script_text) as *mut _,
+ Some(off_thread_compilation_callback),
+ Box::into_raw(context) as *mut c_void,
+ )
+ .is_null());
+ }
+ } else {
+ let load = ScriptOrigin::external(
+ Rc::new(DOMString::from(source_text)),
+ final_url.clone(),
+ self.fetch_options.clone(),
+ ScriptType::Classic,
+ );
+ finish_fetching_a_classic_script(&*elem, self.kind.clone(), self.url.clone(), Ok(load));
+ }
+ }
- // Step 7.
- let source_text = encoding.decode(&self.data, DecoderTrap::Replace).unwrap();
- ClassicScript::external(DOMString::from(source_text), metadata.final_url)
- });
+ fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
+ &mut self.resource_timing
+ }
- // Step 9.
- // https://html.spec.whatwg.org/multipage/#prepare-a-script
- // Step 18.6 (When the chosen algorithm asynchronously completes).
- let elem = self.elem.root();
- let document = document_from_node(&*elem);
+ fn resource_timing(&self) -> &ResourceFetchTiming {
+ &self.resource_timing
+ }
- match self.kind {
- ExternalScriptKind::Asap => document.asap_script_loaded(&elem, load),
- ExternalScriptKind::AsapInOrder => document.asap_in_order_script_loaded(&elem, load),
- ExternalScriptKind::Deferred => document.deferred_script_loaded(&elem, load),
- ExternalScriptKind::ParsingBlocking => document.pending_parsing_blocking_script_loaded(&elem, load),
- }
+ fn submit_resource_timing(&mut self) {
+ network_listener::submit_timing(self)
+ }
+}
- document.finish_load(LoadType::Script(self.url.clone()));
+impl ResourceTimingListener for ClassicContext {
+ fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
+ let initiator_type = InitiatorType::LocalName(
+ self.elem
+ .root()
+ .upcast::<Element>()
+ .local_name()
+ .to_string(),
+ );
+ (initiator_type, self.url.clone())
+ }
+
+ fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
+ document_from_node(&*self.elem.root()).global()
}
}
-impl PreInvoke for ScriptContext {}
+impl PreInvoke for ClassicContext {}
+
+/// Steps 1-2 of <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
+// This function is also used to prefetch a script in `script::dom::servoparser::prefetch`.
+pub(crate) fn script_fetch_request(
+ url: ServoUrl,
+ cors_setting: Option<CorsSettings>,
+ origin: ImmutableOrigin,
+ pipeline_id: PipelineId,
+ options: ScriptFetchOptions,
+) -> RequestBuilder {
+ // We intentionally ignore options' credentials_mode member for classic scripts.
+ // The mode is initialized by create_a_potential_cors_request.
+ create_a_potential_cors_request(
+ url,
+ Destination::Script,
+ cors_setting,
+ None,
+ options.referrer,
+ )
+ .origin(origin)
+ .pipeline_id(Some(pipeline_id))
+ .parser_metadata(options.parser_metadata)
+ .integrity_metadata(options.integrity_metadata.clone())
+ .referrer_policy(options.referrer_policy)
+}
-/// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
-fn fetch_a_classic_script(script: &HTMLScriptElement,
- kind: ExternalScriptKind,
- url: ServoUrl,
- cors_setting: Option<CorsSettings>,
- integrity_metadata: String,
- character_encoding: EncodingRef) {
+/// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script>
+fn fetch_a_classic_script(
+ script: &HTMLScriptElement,
+ kind: ExternalScriptKind,
+ url: ServoUrl,
+ cors_setting: Option<CorsSettings>,
+ options: ScriptFetchOptions,
+ character_encoding: &'static Encoding,
+) {
let doc = document_from_node(script);
// Step 1, 2.
- let request = RequestInit {
- url: url.clone(),
- type_: RequestType::Script,
- destination: Destination::Script,
- // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
- // Step 1
- mode: match cors_setting {
- Some(_) => RequestMode::CorsMode,
- None => RequestMode::NoCors,
- },
- // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
- // Step 3-4
- credentials_mode: match cors_setting {
- Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
- _ => CredentialsMode::Include,
- },
- origin: doc.url(),
- pipeline_id: Some(script.global().pipeline_id()),
- referrer_url: Some(doc.url()),
- referrer_policy: doc.get_referrer_policy(),
- integrity_metadata: integrity_metadata,
- .. RequestInit::default()
- };
+ let request = script_fetch_request(
+ url.clone(),
+ cors_setting,
+ doc.origin().immutable().clone(),
+ script.global().pipeline_id(),
+ options.clone(),
+ );
// TODO: Step 3, Add custom steps to perform fetch
- let context = Arc::new(Mutex::new(ScriptContext {
+ let context = Arc::new(Mutex::new(ClassicContext {
elem: Trusted::new(script),
kind: kind,
character_encoding: character_encoding,
- data: vec!(),
+ data: vec![],
metadata: None,
url: url.clone(),
- status: Ok(())
+ status: Ok(()),
+ fetch_options: options,
+ resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
}));
let (action_sender, action_receiver) = ipc::channel().unwrap();
+ let (task_source, canceller) = doc
+ .window()
+ .task_manager()
+ .networking_task_source_with_canceller();
let listener = NetworkListener {
- context: context,
- task_source: doc.window().networking_task_source(),
- wrapper: Some(doc.window().get_runnable_wrapper())
+ context,
+ task_source,
+ canceller: Some(canceller),
};
- ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify_fetch(message.to().unwrap());
- });
+ ROUTER.add_route(
+ action_receiver.to_opaque(),
+ Box::new(move |message| {
+ listener.notify_fetch(message.to().unwrap());
+ }),
+ );
doc.fetch_async(LoadType::Script(url), request, action_sender);
}
impl HTMLScriptElement {
- /// https://html.spec.whatwg.org/multipage/#prepare-a-script
+ /// <https://html.spec.whatwg.org/multipage/#prepare-a-script>
pub fn prepare(&self) {
// Step 1.
if self.already_started.get() {
@@ -298,112 +589,160 @@ impl HTMLScriptElement {
let was_parser_inserted = self.parser_inserted.get();
self.parser_inserted.set(false);
- // Step 3.
+ // Step 4.
let element = self.upcast::<Element>();
- let async = element.has_attribute(&local_name!("async"));
+ let asynch = element.has_attribute(&local_name!("async"));
// Note: confusingly, this is done if the element does *not* have an "async" attribute.
- if was_parser_inserted && !async {
+ if was_parser_inserted && !asynch {
self.non_blocking.set(true);
}
- // Step 4.
+ // Step 5-6.
let text = self.Text();
if text.is_empty() && !element.has_attribute(&local_name!("src")) {
return;
}
- // Step 5.
- if !self.upcast::<Node>().is_in_doc() {
+ // Step 7.
+ if !self.upcast::<Node>().is_connected() {
return;
}
- // Step 6.
- if !self.is_javascript() {
+ let script_type = if let Some(ty) = self.get_script_type() {
+ ty
+ } else {
+ // Step 7.
return;
- }
+ };
- // Step 7.
+ // Step 8.
if was_parser_inserted {
self.parser_inserted.set(true);
self.non_blocking.set(false);
}
- // Step 8.
+ // Step 10.
self.already_started.set(true);
- // Step 9.
+ // Step 12.
let doc = document_from_node(self);
if self.parser_inserted.get() && &*self.parser_document != &*doc {
return;
}
- // Step 10.
+ // Step 13.
if !doc.is_scripting_enabled() {
return;
}
- // TODO(#4577): Step 11: CSP.
+ // Step 14
+ if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
+ return;
+ }
- // Step 12.
- let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
- let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
- match (for_attribute.r(), event_attribute.r()) {
- (Some(for_attribute), Some(event_attribute)) => {
- let for_value = for_attribute.value().to_ascii_lowercase();
- let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
- if for_value != "window" {
- return;
- }
+ // Step 15.
+ if !element.has_attribute(&local_name!("src")) &&
+ doc.should_elements_inline_type_behavior_be_blocked(
+ &element,
+ csp::InlineCheckType::Script,
+ &text,
+ ) == csp::CheckResult::Blocked
+ {
+ warn!("Blocking inline script due to CSP");
+ return;
+ }
- let event_value = event_attribute.value().to_ascii_lowercase();
- let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
- if event_value != "onload" && event_value != "onload()" {
- return;
- }
- },
- (_, _) => (),
+ // Step 16.
+ if script_type == ScriptType::Classic {
+ let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
+ let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
+ match (for_attribute, event_attribute) {
+ (Some(ref for_attribute), Some(ref event_attribute)) => {
+ let for_value = for_attribute.value().to_ascii_lowercase();
+ let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
+ if for_value != "window" {
+ return;
+ }
+
+ let event_value = event_attribute.value().to_ascii_lowercase();
+ let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
+ if event_value != "onload" && event_value != "onload()" {
+ return;
+ }
+ },
+ (_, _) => (),
+ }
}
- // Step 13.
- let encoding = element.get_attribute(&ns!(), &local_name!("charset"))
- .and_then(|charset| encoding_from_whatwg_label(&charset.value()))
- .unwrap_or_else(|| doc.encoding());
+ // Step 17.
+ let encoding = element
+ .get_attribute(&ns!(), &local_name!("charset"))
+ .and_then(|charset| Encoding::for_label(charset.value().as_bytes()))
+ .unwrap_or_else(|| doc.encoding());
- // Step 14.
+ // Step 18.
let cors_setting = cors_setting_for_element(element);
- // TODO: Step 15: Module script credentials mode.
+ // Step 19.
+ let module_credentials_mode = match script_type {
+ ScriptType::Classic => CredentialsMode::CredentialsSameOrigin,
+ ScriptType::Module => reflect_cross_origin_attribute(element).map_or(
+ CredentialsMode::CredentialsSameOrigin,
+ |attr| match &*attr {
+ "use-credentials" => CredentialsMode::Include,
+ "anonymous" => CredentialsMode::CredentialsSameOrigin,
+ _ => CredentialsMode::CredentialsSameOrigin,
+ },
+ ),
+ };
- // TODO: Step 16: Nonce.
+ // TODO: Step 20: Nonce.
- // Step 17: Integrity metadata.
+ // Step 21: Integrity metadata.
let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
- let integrity_val = im_attribute.r().map(|a| a.value());
+ let integrity_val = im_attribute.as_ref().map(|a| a.value());
let integrity_metadata = match integrity_val {
Some(ref value) => &***value,
None => "",
};
- // TODO: Step 18: parser state.
+ // TODO: Step 22: referrer policy
+
+ // Step 23
+ let parser_metadata = if self.parser_inserted.get() {
+ ParserMetadata::ParserInserted
+ } else {
+ ParserMetadata::NotParserInserted
+ };
+
+ // Step 24.
+ let options = ScriptFetchOptions {
+ cryptographic_nonce: "".into(),
+ integrity_metadata: integrity_metadata.to_owned(),
+ parser_metadata,
+ referrer: self.global().get_referrer(),
+ referrer_policy: referrer_policy_for_element(self.upcast::<Element>()),
+ credentials_mode: module_credentials_mode,
+ };
- // TODO: Step 19: environment settings object.
+ // TODO: Step 23: environment settings object.
let base_url = doc.base_url();
if let Some(src) = element.get_attribute(&ns!(), &local_name!("src")) {
- // Step 20.
+ // Step 26.
- // Step 20.1.
+ // Step 26.1.
let src = src.value();
- // Step 20.2.
+ // Step 26.2.
if src.is_empty() {
self.queue_error_event();
return;
}
- // Step 20.3: The "from an external file"" flag is stored in ClassicScript.
+ // Step 26.3: The "from an external file"" flag is stored in ScriptOrigin.
- // Step 20.4-20.5.
+ // Step 26.4-26.5.
let url = match base_url.join(&src) {
Ok(url) => url,
Err(_) => {
@@ -413,94 +752,233 @@ impl HTMLScriptElement {
},
};
- // Preparation for step 22.
- let kind = if element.has_attribute(&local_name!("defer")) && was_parser_inserted && !async {
- // Step 22.a: classic, has src, has defer, was parser-inserted, is not async.
- ExternalScriptKind::Deferred
- } else if was_parser_inserted && !async {
- // Step 22.b: classic, has src, was parser-inserted, is not async.
- ExternalScriptKind::ParsingBlocking
- } else if !async && !self.non_blocking.get() {
- // Step 22.c: classic, has src, is not async, is not non-blocking.
- ExternalScriptKind::AsapInOrder
- } else {
- // Step 22.d: classic, has src.
- ExternalScriptKind::Asap
- };
-
- // Step 20.6.
- fetch_a_classic_script(self, kind, url, cors_setting, integrity_metadata.to_owned(), encoding);
-
- // Step 22.
- match kind {
- ExternalScriptKind::Deferred => doc.add_deferred_script(self),
- ExternalScriptKind::ParsingBlocking => doc.set_pending_parsing_blocking_script(self, None),
- ExternalScriptKind::AsapInOrder => doc.push_asap_in_order_script(self),
- ExternalScriptKind::Asap => doc.add_asap_script(self),
+ // Step 26.6.
+ match script_type {
+ ScriptType::Classic => {
+ // Preparation for step 26.
+ let kind = if element.has_attribute(&local_name!("defer")) &&
+ was_parser_inserted &&
+ !asynch
+ {
+ // Step 26.a: classic, has src, has defer, was parser-inserted, is not async.
+ ExternalScriptKind::Deferred
+ } else if was_parser_inserted && !asynch {
+ // Step 26.c: classic, has src, was parser-inserted, is not async.
+ ExternalScriptKind::ParsingBlocking
+ } else if !asynch && !self.non_blocking.get() {
+ // Step 26.d: classic, has src, is not async, is not non-blocking.
+ ExternalScriptKind::AsapInOrder
+ } else {
+ // Step 26.f: classic, has src.
+ ExternalScriptKind::Asap
+ };
+
+ // Step 24.6.
+ fetch_a_classic_script(self, kind, url, cors_setting, options, encoding);
+
+ // Step 23.
+ match kind {
+ ExternalScriptKind::Deferred => doc.add_deferred_script(self),
+ ExternalScriptKind::ParsingBlocking => {
+ doc.set_pending_parsing_blocking_script(self, None)
+ },
+ ExternalScriptKind::AsapInOrder => doc.push_asap_in_order_script(self),
+ ExternalScriptKind::Asap => doc.add_asap_script(self),
+ }
+ },
+ ScriptType::Module => {
+ fetch_external_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ url.clone(),
+ Destination::Script,
+ options,
+ );
+
+ if !asynch && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !asynch && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
+ },
}
} else {
- // Step 21.
+ // Step 27.
assert!(!text.is_empty());
- let result = Ok(ClassicScript::internal(text, base_url));
-
- // Step 22.
- if was_parser_inserted &&
- doc.get_current_parser().map_or(false, |parser| parser.script_nesting_level() <= 1) &&
- doc.get_script_blocking_stylesheets_count() > 0 {
- // Step 22.e: classic, has no src, was parser-inserted, is blocked on stylesheet.
- doc.set_pending_parsing_blocking_script(self, Some(result));
- } else {
- // Step 22.f: otherwise.
- self.execute(result);
+
+ let text_rc = Rc::new(text);
+
+ // Step 27-1. & 27-2.
+ let result = Ok(ScriptOrigin::internal(
+ Rc::clone(&text_rc),
+ base_url.clone(),
+ options.clone(),
+ script_type.clone(),
+ ));
+
+ // Step 27-2.
+ match script_type {
+ ScriptType::Classic => {
+ if was_parser_inserted &&
+ doc.get_current_parser()
+ .map_or(false, |parser| parser.script_nesting_level() <= 1) &&
+ doc.get_script_blocking_stylesheets_count() > 0
+ {
+ // Step 27.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
+ doc.set_pending_parsing_blocking_script(self, Some(result));
+ } else {
+ // Step 27.i: otherwise.
+ self.execute(result);
+ }
+ },
+ ScriptType::Module => {
+ // We should add inline module script elements
+ // into those vectors in case that there's no
+ // descendants in the inline module script.
+ if !asynch && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !asynch && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
+
+ fetch_inline_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ text_rc,
+ base_url.clone(),
+ self.id.clone(),
+ options,
+ );
+ },
}
}
}
- fn unminify_js(&self, script: &mut ClassicScript) {
- if !opts::get().unminify_js {
+ fn unminify_js(&self, script: &mut ScriptOrigin) {
+ if !self.parser_document.window().unminify_js() {
return;
}
- match Command::new("js-beautify")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn() {
- Err(_) => {
- warn!("Failed to execute js-beautify. Will store unmodified script");
- },
- Ok(process) => {
- let mut script_content = String::from(script.text.clone());
- let _ = process.stdin.unwrap().write_all(script_content.as_bytes());
- script_content.clear();
- let _ = process.stdout.unwrap().read_to_string(&mut script_content);
+ // Write the minified code to a temporary file and pass its path as an argument
+ // to js-beautify to read from. Meanwhile, redirect the process' stdout into
+ // another temporary file and read that into a string. This avoids some hangs
+ // observed on macOS when using direct input/output pipes with very large
+ // unminified content.
+ let (input, output) = (tempfile::NamedTempFile::new(), tempfile::tempfile());
+ if let (Ok(mut input), Ok(mut output)) = (input, output) {
+ match &script.code {
+ SourceCode::Text(text) => {
+ input.write_all(text.as_bytes()).unwrap();
+ },
+ SourceCode::Compiled(compiled_source_code) => {
+ input
+ .write_all(compiled_source_code.original_text.as_bytes())
+ .unwrap();
+ },
+ }
+ match Command::new("js-beautify")
+ .arg(input.path())
+ .stdout(output.try_clone().unwrap())
+ .status()
+ {
+ Ok(status) if status.success() => {
+ let mut script_content = String::new();
+ output.seek(std::io::SeekFrom::Start(0)).unwrap();
+ output.read_to_string(&mut script_content).unwrap();
+ script.code = SourceCode::Text(Rc::new(DOMString::from(script_content)));
+ },
+ _ => {
+ warn!("Failed to execute js-beautify. Will store unmodified script");
+ },
+ }
+ } else {
+ warn!("Error creating input and output files for unminify");
+ }
- script.text = DOMString::from(script_content);
+ let path;
+ match window_from_node(self).unminified_js_dir() {
+ Some(unminified_js_dir) => path = PathBuf::from(unminified_js_dir),
+ None => {
+ warn!("Unminified script directory not found");
+ return;
},
}
-
- let path = PathBuf::from(window_from_node(self).unminified_js_dir().unwrap());
- let path = if script.external {
+ let (base, has_name) = match script.url.as_str().ends_with("/") {
+ true => (
+ path.join(&script.url[url::Position::BeforeHost..])
+ .as_path()
+ .to_owned(),
+ false,
+ ),
+ false => (
+ path.join(&script.url[url::Position::BeforeHost..])
+ .parent()
+ .unwrap()
+ .to_owned(),
+ true,
+ ),
+ };
+ match create_dir_all(base.clone()) {
+ Ok(()) => debug!("Created base dir: {:?}", base),
+ Err(e) => {
+ debug!("Failed to create base dir: {:?}, {:?}", base, e);
+ return;
+ },
+ }
+ let path = if script.external && has_name {
// External script.
- let path_parts = script.url.path_segments().unwrap();
- match path_parts.last() {
- Some(script_name) => path.join(script_name),
- None => path.join(Uuid::new_v4().to_string()),
- }
+ path.join(&script.url[url::Position::BeforeHost..])
} else {
- // Inline script.
- path.join(Uuid::new_v4().to_string())
+ // Inline script or url ends with '/'
+ base.join(Uuid::new_v4().to_string())
};
debug!("script will be stored in {:?}", path);
match File::create(&path) {
- Ok(mut file) => file.write_all(script.text.as_bytes()).unwrap(),
+ Ok(mut file) => match &script.code {
+ SourceCode::Text(text) => file.write_all(text.as_bytes()).unwrap(),
+ SourceCode::Compiled(compiled_source_code) => {
+ file.write_all(compiled_source_code.original_text.as_bytes())
+ .unwrap();
+ },
+ },
Err(why) => warn!("Could not store script {:?}", why),
}
}
- /// https://html.spec.whatwg.org/multipage/#execute-the-script-block
- pub fn execute(&self, result: Result<ClassicScript, NetworkError>) {
+ fn substitute_with_local_script(&self, script: &mut ScriptOrigin) {
+ if self
+ .parser_document
+ .window()
+ .local_script_source()
+ .is_none() ||
+ !script.external
+ {
+ return;
+ }
+ let mut path = PathBuf::from(
+ self.parser_document
+ .window()
+ .local_script_source()
+ .clone()
+ .unwrap(),
+ );
+ path = path.join(&script.url[url::Position::BeforeHost..]);
+ debug!("Attempting to read script stored at: {:?}", path);
+ match read_to_string(path.clone()) {
+ Ok(local_script) => {
+ debug!("Found script stored at: {:?}", path);
+ script.code = SourceCode::Text(Rc::new(DOMString::from(local_script)));
+ },
+ Err(why) => warn!("Could not restore script from file {:?}", why),
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#execute-the-script-block>
+ pub fn execute(&self, result: ScriptResult) {
// Step 1.
let doc = document_from_node(self);
if self.parser_inserted.get() && &*doc != &*self.parser_document {
@@ -513,15 +991,18 @@ impl HTMLScriptElement {
warn!("error loading script {:?}", e);
self.dispatch_error_event();
return;
- }
+ },
Ok(script) => script,
};
- self.unminify_js(&mut script);
+ if script.type_ == ScriptType::Classic {
+ self.unminify_js(&mut script);
+ self.substitute_with_local_script(&mut script);
+ }
// Step 3.
- let neutralized_doc = if script.external {
+ let neutralized_doc = if script.external || script.type_ == ScriptType::Module {
debug!("loading external script, url = {}", script.url);
let doc = document_from_node(self);
doc.incr_ignore_destructive_writes_counter();
@@ -534,28 +1015,35 @@ impl HTMLScriptElement {
let document = document_from_node(self);
let old_script = document.GetCurrentScript();
- // Step 5.a.1.
- document.set_current_script(Some(self));
-
- // Step 5.a.2.
- self.run_a_classic_script(&script);
+ match script.type_ {
+ ScriptType::Classic => document.set_current_script(Some(self)),
+ ScriptType::Module => document.set_current_script(None),
+ }
- // Step 6.
- document.set_current_script(old_script.r());
+ match script.type_ {
+ ScriptType::Classic => {
+ self.run_a_classic_script(&script);
+ document.set_current_script(old_script.as_deref());
+ },
+ ScriptType::Module => {
+ assert!(document.GetCurrentScript().is_none());
+ self.run_a_module_script(&script, false);
+ },
+ }
- // Step 7.
+ // Step 5.
if let Some(doc) = neutralized_doc {
doc.decr_ignore_destructive_writes_counter();
}
- // Step 8.
+ // Step 6.
if script.external {
self.dispatch_load_event();
}
}
// https://html.spec.whatwg.org/multipage/#run-a-classic-script
- pub fn run_a_classic_script(&self, script: &ClassicScript) {
+ pub fn run_a_classic_script(&self, script: &ScriptOrigin) {
// TODO use a settings object rather than this element's document/window
// Step 2
let document = document_from_node(self);
@@ -565,82 +1053,177 @@ impl HTMLScriptElement {
// Steps 4-10
let window = window_from_node(self);
- let line_number = if script.external { 1 } else { self.line_number as u32 };
- rooted!(in(window.get_cx()) let mut rval = UndefinedValue());
+ let line_number = if script.external {
+ 1
+ } else {
+ self.line_number as u32
+ };
+ rooted!(in(*window.get_cx()) let mut rval = UndefinedValue());
let global = window.upcast::<GlobalScope>();
global.evaluate_script_on_global_with_result(
- &script.text, script.url.as_str(), rval.handle_mut(), line_number);
+ &script.code,
+ script.url.as_str(),
+ rval.handle_mut(),
+ line_number,
+ script.fetch_options.clone(),
+ script.url.clone(),
+ );
+ }
+
+ #[allow(unsafe_code)]
+ /// https://html.spec.whatwg.org/multipage/#run-a-module-script
+ pub fn run_a_module_script(&self, script: &ScriptOrigin, _rethrow_errors: bool) {
+ // TODO use a settings object rather than this element's document/window
+ // Step 2
+ let document = document_from_node(self);
+ if !document.is_fully_active() || !document.is_scripting_enabled() {
+ return;
+ }
+
+ // Step 4
+ let window = window_from_node(self);
+ let global = window.upcast::<GlobalScope>();
+ let _aes = AutoEntryScript::new(&global);
+
+ let tree = if script.external {
+ global.get_module_map().borrow().get(&script.url).cloned()
+ } else {
+ global
+ .get_inline_module_map()
+ .borrow()
+ .get(&self.id.clone())
+ .cloned()
+ };
+
+ if let Some(module_tree) = tree {
+ // Step 6.
+ {
+ let module_error = module_tree.get_rethrow_error().borrow();
+ let network_error = module_tree.get_network_error().borrow();
+ if module_error.is_some() && network_error.is_none() {
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+
+ let record = module_tree
+ .get_record()
+ .borrow()
+ .as_ref()
+ .map(|record| record.handle());
+
+ if let Some(record) = record {
+ let evaluated = module_tree.execute_module(global, record);
+
+ if let Err(exception) = evaluated {
+ module_tree.set_rethrow_error(exception);
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+ }
}
pub fn queue_error_event(&self) {
let window = window_from_node(self);
- window.dom_manipulation_task_source().queue_simple_event(self.upcast(), atom!("error"), &window);
+ window
+ .task_manager()
+ .dom_manipulation_task_source()
+ .queue_simple_event(self.upcast(), atom!("error"), &window);
}
pub fn dispatch_load_event(&self) {
- self.dispatch_event(atom!("load"),
- EventBubbles::DoesNotBubble,
- EventCancelable::NotCancelable);
+ self.dispatch_event(
+ atom!("load"),
+ EventBubbles::DoesNotBubble,
+ EventCancelable::NotCancelable,
+ );
}
pub fn dispatch_error_event(&self) {
- self.dispatch_event(atom!("error"),
- EventBubbles::DoesNotBubble,
- EventCancelable::NotCancelable);
+ self.dispatch_event(
+ atom!("error"),
+ EventBubbles::DoesNotBubble,
+ EventCancelable::NotCancelable,
+ );
}
- pub fn is_javascript(&self) -> bool {
+ // https://html.spec.whatwg.org/multipage/#prepare-a-script Step 7.
+ pub fn get_script_type(&self) -> Option<ScriptType> {
let element = self.upcast::<Element>();
+
let type_attr = element.get_attribute(&ns!(), &local_name!("type"));
- let is_js = match type_attr.as_ref().map(|s| s.value()) {
- Some(ref s) if s.is_empty() => {
- // type attr exists, but empty means js
+ let language_attr = element.get_attribute(&ns!(), &local_name!("language"));
+
+ let script_type = match (
+ type_attr.as_ref().map(|t| t.value()),
+ language_attr.as_ref().map(|l| l.value()),
+ ) {
+ (Some(ref ty), _) if ty.is_empty() => {
debug!("script type empty, inferring js");
- true
+ Some(ScriptType::Classic)
},
- Some(s) => {
- debug!("script type={}", &**s);
- SCRIPT_JS_MIMES.contains(&s.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS))
+ (None, Some(ref lang)) if lang.is_empty() => {
+ debug!("script type empty, inferring js");
+ Some(ScriptType::Classic)
+ },
+ (None, None) => {
+ debug!("script type empty, inferring js");
+ Some(ScriptType::Classic)
+ },
+ (None, Some(ref lang)) => {
+ debug!("script language={}", &***lang);
+ let language = format!("text/{}", &***lang);
+
+ if SCRIPT_JS_MIMES.contains(&language.to_ascii_lowercase().as_str()) {
+ Some(ScriptType::Classic)
+ } else {
+ None
+ }
+ },
+ (Some(ref ty), _) => {
+ debug!("script type={}", &***ty);
+
+ if &***ty == String::from("module") {
+ return Some(ScriptType::Module);
+ }
+
+ if SCRIPT_JS_MIMES
+ .contains(&ty.to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS))
+ {
+ Some(ScriptType::Classic)
+ } else {
+ None
+ }
},
- None => {
- debug!("no script type");
- let language_attr = element.get_attribute(&ns!(), &local_name!("language"));
- let is_js = match language_attr.as_ref().map(|s| s.value()) {
- Some(ref s) if s.is_empty() => {
- debug!("script language empty, inferring js");
- true
- },
- Some(s) => {
- debug!("script language={}", &**s);
- let mut language = format!("text/{}", &**s);
- language.make_ascii_lowercase();
- SCRIPT_JS_MIMES.contains(&&*language)
- },
- None => {
- debug!("no script type or language, inferring js");
- true
- }
- };
- // https://github.com/rust-lang/rust/issues/21114
- is_js
- }
};
+
// https://github.com/rust-lang/rust/issues/21114
- is_js
+ script_type
}
pub fn set_parser_inserted(&self, parser_inserted: bool) {
self.parser_inserted.set(parser_inserted);
}
+ pub fn get_parser_inserted(&self) -> bool {
+ self.parser_inserted.get()
+ }
+
pub fn set_already_started(&self, already_started: bool) {
self.already_started.set(already_started);
}
- fn dispatch_event(&self,
- type_: Atom,
- bubbles: EventBubbles,
- cancelable: EventCancelable) -> EventStatus {
+ pub fn get_non_blocking(&self) -> bool {
+ self.non_blocking.get()
+ }
+
+ fn dispatch_event(
+ &self,
+ type_: Atom,
+ bubbles: EventBubbles,
+ cancelable: EventCancelable,
+ ) -> EventStatus {
let window = window_from_node(self);
let event = Event::new(window.upcast(), type_, bubbles, cancelable);
event.fire(self.upcast())
@@ -648,8 +1231,8 @@ impl HTMLScriptElement {
}
impl VirtualMethods for HTMLScriptElement {
- fn super_type(&self) -> Option<&VirtualMethods> {
- Some(self.upcast::<HTMLElement>() as &VirtualMethods)
+ fn super_type(&self) -> Option<&dyn VirtualMethods> {
+ Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
@@ -657,7 +1240,7 @@ impl VirtualMethods for HTMLScriptElement {
match *attr.local_name() {
local_name!("src") => {
if let AttributeMutation::Set(_) = mutation {
- if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
+ if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
@@ -670,30 +1253,39 @@ impl VirtualMethods for HTMLScriptElement {
if let Some(ref s) = self.super_type() {
s.children_changed(mutation);
}
- if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
+ if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
- if tree_in_doc && !self.parser_inserted.get() {
- self.prepare();
+ if context.tree_connected && !self.parser_inserted.get() {
+ let script = Trusted::new(self);
+ document_from_node(self).add_delayed_task(task!(ScriptDelayedInitialize: move || {
+ script.root().prepare();
+ }));
}
}
- fn cloning_steps(&self, copy: &Node, maybe_doc: Option<&Document>,
- clone_children: CloneChildrenFlag) {
+ fn cloning_steps(
+ &self,
+ copy: &Node,
+ maybe_doc: Option<&Document>,
+ clone_children: CloneChildrenFlag,
+ ) {
if let Some(ref s) = self.super_type() {
s.cloning_steps(copy, maybe_doc, clone_children);
}
// https://html.spec.whatwg.org/multipage/#already-started
if self.already_started.get() {
- copy.downcast::<HTMLScriptElement>().unwrap().set_already_started(true);
+ copy.downcast::<HTMLScriptElement>()
+ .unwrap()
+ .set_already_started(true);
}
}
}
@@ -701,8 +1293,9 @@ impl VirtualMethods for HTMLScriptElement {
impl HTMLScriptElementMethods for HTMLScriptElement {
// https://html.spec.whatwg.org/multipage/#dom-script-src
make_url_getter!(Src, "src");
+
// https://html.spec.whatwg.org/multipage/#dom-script-src
- make_setter!(SetSrc, "src");
+ make_url_setter!(SetSrc, "src");
// https://html.spec.whatwg.org/multipage/#dom-script-type
make_getter!(Type, "type");
@@ -716,13 +1309,16 @@ impl HTMLScriptElementMethods for HTMLScriptElement {
// https://html.spec.whatwg.org/multipage/#dom-script-async
fn Async(&self) -> bool {
- self.non_blocking.get() || self.upcast::<Element>().has_attribute(&local_name!("async"))
+ self.non_blocking.get() ||
+ self.upcast::<Element>()
+ .has_attribute(&local_name!("async"))
}
// https://html.spec.whatwg.org/multipage/#dom-script-async
fn SetAsync(&self, value: bool) {
self.non_blocking.set(false);
- self.upcast::<Element>().set_bool_attribute(&local_name!("async"), value);
+ self.upcast::<Element>()
+ .set_bool_attribute(&local_name!("async"), value);
}
// https://html.spec.whatwg.org/multipage/#dom-script-defer
@@ -730,6 +1326,11 @@ impl HTMLScriptElementMethods for HTMLScriptElement {
// https://html.spec.whatwg.org/multipage/#dom-script-defer
make_bool_setter!(SetDefer, "defer");
+ // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
+ make_bool_getter!(NoModule, "nomodule");
+ // https://html.spec.whatwg.org/multipage/#dom-script-nomodule
+ make_bool_setter!(SetNoModule, "nomodule");
+
// https://html.spec.whatwg.org/multipage/#dom-script-integrity
make_getter!(Integrity, "integrity");
// https://html.spec.whatwg.org/multipage/#dom-script-integrity
@@ -755,6 +1356,14 @@ impl HTMLScriptElementMethods for HTMLScriptElement {
set_cross_origin_attribute(self.upcast::<Element>(), value);
}
+ // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
+ fn ReferrerPolicy(&self) -> DOMString {
+ reflect_referrer_policy_attribute(self.upcast::<Element>())
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
+ make_setter!(SetReferrerPolicy, "referrerpolicy");
+
// https://html.spec.whatwg.org/multipage/#dom-script-text
fn Text(&self) -> DOMString {
self.upcast::<Node>().child_text_content()