aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2020-01-06 10:31:55 -0500
committerGitHub <noreply@github.com>2020-01-06 10:31:55 -0500
commit5c7a4db5f4e374dee287d403d90c7bc0e07ac9d0 (patch)
tree76b8911572eae14be6d3064ee101f7983c6e4796 /components/script/dom
parent3b442186366aebc987b1dd73a183ce92172e0bed (diff)
parent508bfbd0da53b0badf76e07c6286d6bd4e29900e (diff)
downloadservo-5c7a4db5f4e374dee287d403d90c7bc0e07ac9d0.tar.gz
servo-5c7a4db5f4e374dee287d403d90c7bc0e07ac9d0.zip
Auto merge of #23545 - CYBAI:support-module-script, r=jdm,manishearth
Support type=module script element This is still WIP but hope can be reviewed first to see if I'm on the right track. Thanks! πŸ™‡β€β™‚οΈ - [x] Support external module script - [x] Support internal module script - [x] Compile cyclic modules --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #23370 (GitHub issue number if applicable) - [x] There are tests for these changes <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23545) <!-- Reviewable:end -->
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/trace.rs9
-rw-r--r--components/script/dom/globalscope.rs30
-rw-r--r--components/script/dom/htmlscriptelement.rs270
-rw-r--r--components/script/dom/promise.rs2
-rw-r--r--components/script/dom/webidls/HTMLScriptElement.webidl2
5 files changed, 247 insertions, 66 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 217e66837bb..d2e1876b9d8 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -311,6 +311,15 @@ unsafe impl<T: JSTraceable> JSTraceable for VecDeque<T> {
}
}
+unsafe impl<T: JSTraceable + Eq + Hash> JSTraceable for indexmap::IndexSet<T> {
+ #[inline]
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ for e in self.iter() {
+ e.trace(trc);
+ }
+ }
+}
+
unsafe impl<A, B, C, D> JSTraceable for (A, B, C, D)
where
A: JSTraceable,
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index f6937427eda..04f8d948a24 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -24,6 +24,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
+use crate::dom::htmlscriptelement::ScriptId;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
@@ -32,6 +33,7 @@ use crate::dom::window::Window;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::dom::workletglobalscope::WorkletGlobalScope;
use crate::microtask::{Microtask, MicrotaskQueue};
+use crate::script_module::ModuleTree;
use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort};
use crate::script_thread::{MainThreadScriptChan, ScriptThread};
use crate::task::TaskCanceller;
@@ -119,6 +121,14 @@ pub struct GlobalScope {
/// Timers used by the Console API.
console_timers: DomRefCell<HashMap<DOMString, u64>>,
+ /// module map is used when importing JavaScript modules
+ /// https://html.spec.whatwg.org/multipage/#concept-settings-object-module-map
+ #[ignore_malloc_size_of = "mozjs"]
+ module_map: DomRefCell<HashMap<ServoUrl, Rc<ModuleTree>>>,
+
+ #[ignore_malloc_size_of = "mozjs"]
+ inline_module_map: DomRefCell<HashMap<ScriptId, Rc<ModuleTree>>>,
+
/// For providing instructions to an optional devtools server.
#[ignore_malloc_size_of = "channels are hard"]
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
@@ -391,6 +401,8 @@ impl GlobalScope {
pipeline_id,
devtools_wants_updates: Default::default(),
console_timers: DomRefCell::new(Default::default()),
+ module_map: DomRefCell::new(Default::default()),
+ inline_module_map: DomRefCell::new(Default::default()),
devtools_chan,
mem_profiler_chan,
time_profiler_chan,
@@ -1357,6 +1369,24 @@ impl GlobalScope {
&self.consumed_rejections
}
+ pub fn set_module_map(&self, url: ServoUrl, module: ModuleTree) {
+ self.module_map.borrow_mut().insert(url, Rc::new(module));
+ }
+
+ pub fn get_module_map(&self) -> &DomRefCell<HashMap<ServoUrl, Rc<ModuleTree>>> {
+ &self.module_map
+ }
+
+ pub fn set_inline_module_map(&self, script_id: ScriptId, module: ModuleTree) {
+ self.inline_module_map
+ .borrow_mut()
+ .insert(script_id, Rc::new(module));
+ }
+
+ pub fn get_inline_module_map(&self) -> &DomRefCell<HashMap<ScriptId, Rc<ModuleTree>>> {
+ &self.inline_module_map
+ }
+
#[allow(unsafe_code)]
pub fn get_cx(&self) -> SafeJSContext {
unsafe { SafeJSContext::from_ptr(Runtime::get()) }
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index fdeca285e30..3a55590c81d 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -12,6 +12,7 @@ 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::document::Document;
use crate::dom::element::{
@@ -27,6 +28,8 @@ 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::script_module::fetch_inline_module_script;
+use crate::script_module::{fetch_external_module_script, ModuleOwner};
use content_security_policy as csp;
use dom_struct::dom_struct;
use encoding_rs::Encoding;
@@ -35,7 +38,7 @@ use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
use msg::constellation_msg::PipelineId;
-use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder};
+use net_traits::request::{CorsSettings, CredentialsMode, Destination, Referrer, RequestBuilder};
use net_traits::ReferrerPolicy;
use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
@@ -51,6 +54,10 @@ use std::sync::{Arc, Mutex};
use style::str::{StaticStringVec, HTML_SPACE_CHARACTERS};
use uuid::Uuid;
+/// 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,
@@ -71,6 +78,10 @@ pub struct HTMLScriptElement {
/// Track line line_number
line_number: u64,
+
+ /// Unique id for each script element
+ #[ignore_malloc_size_of = "Defined in uuid"]
+ id: ScriptId,
}
impl HTMLScriptElement {
@@ -81,6 +92,7 @@ impl HTMLScriptElement {
creator: ElementCreator,
) -> HTMLScriptElement {
HTMLScriptElement {
+ 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()),
@@ -105,11 +117,15 @@ impl HTMLScriptElement {
HTMLScriptElementBinding::Wrap,
)
}
+
+ 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",
@@ -143,7 +159,7 @@ pub struct ScriptOrigin {
}
impl ScriptOrigin {
- fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
+ pub fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
ScriptOrigin {
text: text,
url: url,
@@ -152,7 +168,7 @@ impl ScriptOrigin {
}
}
- fn external(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
+ pub fn external(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
ScriptOrigin {
text: text,
url: url,
@@ -160,6 +176,10 @@ impl ScriptOrigin {
type_,
}
}
+
+ pub fn text(&self) -> DOMString {
+ self.text.clone()
+ }
}
pub type ScriptResult = Result<ScriptOrigin, NetworkError>;
@@ -427,7 +447,10 @@ impl HTMLScriptElement {
return;
}
- // TODO: Step 12: nomodule content attribute
+ // Step 12
+ if element.has_attribute(&local_name!("nomodule")) && script_type == ScriptType::Classic {
+ return;
+ }
// Step 13.
if !element.has_attribute(&local_name!("src")) &&
@@ -441,23 +464,25 @@ impl HTMLScriptElement {
}
// Step 14.
- 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;
- }
+ 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;
- }
- },
- (_, _) => (),
+ 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 15.
@@ -469,7 +494,18 @@ impl HTMLScriptElement {
// Step 16.
let cors_setting = cors_setting_for_element(element);
- // TODO: Step 17: Module script credentials mode.
+ // Step 17.
+ let credentials_mode = match script_type {
+ ScriptType::Classic => None,
+ ScriptType::Module => Some(reflect_cross_origin_attribute(element).map_or(
+ CredentialsMode::CredentialsSameOrigin,
+ |attr| match &*attr {
+ "use-credentials" => CredentialsMode::Include,
+ "anonymous" => CredentialsMode::CredentialsSameOrigin,
+ _ => CredentialsMode::CredentialsSameOrigin,
+ },
+ )),
+ };
// TODO: Step 18: Nonce.
@@ -514,6 +550,7 @@ impl HTMLScriptElement {
},
};
+ // Step 24.6.
match script_type {
ScriptType::Classic => {
// Preparation for step 26.
@@ -555,50 +592,69 @@ impl HTMLScriptElement {
}
},
ScriptType::Module => {
- warn!(
- "{} is a module script. It should be fixed after #23545 landed.",
- url.clone()
+ fetch_external_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ url.clone(),
+ Destination::Script,
+ integrity_metadata.to_owned(),
+ credentials_mode.unwrap(),
);
- self.global().issue_page_warning(&format!(
- "Module scripts are not supported; {} will not be executed.",
- url.clone()
- ));
+
+ if !r#async && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !r#async && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
},
}
} else {
// Step 25.
assert!(!text.is_empty());
- // Step 25-1.
+ // Step 25-1. & 25-2.
let result = Ok(ScriptOrigin::internal(
text.clone(),
base_url.clone(),
script_type.clone(),
));
- // TODO: Step 25-2.
- if let ScriptType::Module = script_type {
- warn!(
- "{} is a module script. It should be fixed after #23545 landed.",
- base_url.clone()
- );
- self.global().issue_page_warning(
- "Module scripts are not supported; ignoring inline module script.",
- );
- return;
- }
+ // Step 25-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 26.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
+ doc.set_pending_parsing_blocking_script(self, Some(result));
+ } else {
+ // Step 26.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 !r#async && was_parser_inserted {
+ doc.add_deferred_script(self);
+ } else if !r#async && !self.non_blocking.get() {
+ doc.push_asap_in_order_script(self);
+ } else {
+ doc.add_asap_script(self);
+ };
- // Step 26.
- 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 26.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
- doc.set_pending_parsing_blocking_script(self, Some(result));
- } else {
- // Step 26.i: otherwise.
- self.execute(result);
+ fetch_inline_module_script(
+ ModuleOwner::Window(Trusted::new(self)),
+ text.clone(),
+ base_url.clone(),
+ self.id.clone(),
+ credentials_mode.unwrap(),
+ );
+ },
}
}
}
@@ -656,7 +712,7 @@ impl HTMLScriptElement {
}
/// <https://html.spec.whatwg.org/multipage/#execute-the-script-block>
- pub fn execute(&self, result: Result<ScriptOrigin, NetworkError>) {
+ pub fn execute(&self, result: ScriptResult) {
// Step 1.
let doc = document_from_node(self);
if self.parser_inserted.get() && &*doc != &*self.parser_document {
@@ -674,10 +730,12 @@ impl HTMLScriptElement {
Ok(script) => script,
};
- self.unminify_js(&mut script);
+ if script.type_ == ScriptType::Classic {
+ self.unminify_js(&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();
@@ -690,21 +748,24 @@ 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);
-
- // Step 6.
- document.set_current_script(old_script.as_deref());
+ match script.type_ {
+ ScriptType::Classic => {
+ document.set_current_script(Some(self));
+ self.run_a_classic_script(&script);
+ document.set_current_script(old_script.as_deref());
+ },
+ ScriptType::Module => {
+ assert!(old_script.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();
}
@@ -736,6 +797,72 @@ impl HTMLScriptElement {
);
}
+ #[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);
+
+ if script.external {
+ let module_map = global.get_module_map().borrow();
+
+ if let Some(module_tree) = module_map.get(&script.url) {
+ // Step 6.
+ {
+ let module_error = module_tree.get_error().borrow();
+ if module_error.is_some() {
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+
+ let module_record = module_tree.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let evaluated = module_tree.execute_module(global, record.handle());
+
+ if let Err(exception) = evaluated {
+ module_tree.set_error(Some(exception.clone()));
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+ }
+ } else {
+ let inline_module_map = global.get_inline_module_map().borrow();
+
+ if let Some(module_tree) = inline_module_map.get(&self.id.clone()) {
+ // Step 6.
+ {
+ let module_error = module_tree.get_error().borrow();
+ if module_error.is_some() {
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+
+ let module_record = module_tree.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let evaluated = module_tree.execute_module(global, record.handle());
+
+ if let Err(exception) = evaluated {
+ module_tree.set_error(Some(exception.clone()));
+ module_tree.report_error(&global);
+ return;
+ }
+ }
+ }
+ }
+ }
+
pub fn queue_error_event(&self) {
let window = window_from_node(self);
window
@@ -818,10 +945,18 @@ impl HTMLScriptElement {
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);
}
+ pub fn get_non_blocking(&self) -> bool {
+ self.non_blocking.get()
+ }
+
fn dispatch_event(
&self,
type_: Atom,
@@ -930,6 +1065,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
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
index d2e209b98dd..785c1be8939 100644
--- a/components/script/dom/promise.rs
+++ b/components/script/dom/promise.rs
@@ -225,7 +225,7 @@ impl Promise {
}
#[allow(unsafe_code)]
- fn promise_obj(&self) -> HandleObject {
+ pub fn promise_obj(&self) -> HandleObject {
let obj = self.reflector().get_jsobject();
unsafe {
assert!(IsPromiseObject(obj));
diff --git a/components/script/dom/webidls/HTMLScriptElement.webidl b/components/script/dom/webidls/HTMLScriptElement.webidl
index f7126b7901b..13b69865fa5 100644
--- a/components/script/dom/webidls/HTMLScriptElement.webidl
+++ b/components/script/dom/webidls/HTMLScriptElement.webidl
@@ -12,6 +12,8 @@ interface HTMLScriptElement : HTMLElement {
[CEReactions]
attribute DOMString type;
[CEReactions]
+ attribute boolean noModule;
+ [CEReactions]
attribute DOMString charset;
[CEReactions]
attribute boolean async;