Scaffold module script

This commit is contained in:
CYBAI 2019-06-05 22:20:10 +09:00
parent 86575bba1b
commit f2007751dd
6 changed files with 1620 additions and 63 deletions

View file

@ -24,6 +24,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource; use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File; use crate::dom::file::File;
use crate::dom::htmlscriptelement::ScriptId;
use crate::dom::messageevent::MessageEvent; use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort; use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
@ -32,6 +33,7 @@ use crate::dom::window::Window;
use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::dom::workletglobalscope::WorkletGlobalScope; use crate::dom::workletglobalscope::WorkletGlobalScope;
use crate::microtask::{Microtask, MicrotaskQueue}; use crate::microtask::{Microtask, MicrotaskQueue};
use crate::script_module::ModuleTree;
use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort}; use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort};
use crate::script_thread::{MainThreadScriptChan, ScriptThread}; use crate::script_thread::{MainThreadScriptChan, ScriptThread};
use crate::task::TaskCanceller; use crate::task::TaskCanceller;
@ -119,6 +121,14 @@ pub struct GlobalScope {
/// Timers used by the Console API. /// Timers used by the Console API.
console_timers: DomRefCell<HashMap<DOMString, u64>>, 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. /// For providing instructions to an optional devtools server.
#[ignore_malloc_size_of = "channels are hard"] #[ignore_malloc_size_of = "channels are hard"]
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
@ -391,6 +401,8 @@ impl GlobalScope {
pipeline_id, pipeline_id,
devtools_wants_updates: Default::default(), devtools_wants_updates: Default::default(),
console_timers: DomRefCell::new(Default::default()), console_timers: DomRefCell::new(Default::default()),
module_map: DomRefCell::new(Default::default()),
inline_module_map: DomRefCell::new(Default::default()),
devtools_chan, devtools_chan,
mem_profiler_chan, mem_profiler_chan,
time_profiler_chan, time_profiler_chan,
@ -1357,6 +1369,24 @@ impl GlobalScope {
&self.consumed_rejections &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)] #[allow(unsafe_code)]
pub fn get_cx(&self) -> SafeJSContext { pub fn get_cx(&self) -> SafeJSContext {
unsafe { SafeJSContext::from_ptr(Runtime::get()) } unsafe { SafeJSContext::from_ptr(Runtime::get()) }

View file

@ -12,6 +12,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot}; 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::str::{DOMString, USVString};
use crate::dom::document::Document; use crate::dom::document::Document;
use crate::dom::element::{ use crate::dom::element::{
@ -27,6 +28,8 @@ use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::fetch::create_a_potential_CORS_request; use crate::fetch::create_a_potential_CORS_request;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; 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 content_security_policy as csp;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use encoding_rs::Encoding; use encoding_rs::Encoding;
@ -51,6 +54,10 @@ use std::sync::{Arc, Mutex};
use style::str::{StaticStringVec, HTML_SPACE_CHARACTERS}; use style::str::{StaticStringVec, HTML_SPACE_CHARACTERS};
use uuid::Uuid; use uuid::Uuid;
/// An unique id for script element.
#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
pub struct ScriptId(Uuid);
#[dom_struct] #[dom_struct]
pub struct HTMLScriptElement { pub struct HTMLScriptElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
@ -71,6 +78,10 @@ pub struct HTMLScriptElement {
/// Track line line_number /// Track line line_number
line_number: u64, line_number: u64,
/// Unique id for each script element
#[ignore_malloc_size_of = "Defined in uuid"]
id: ScriptId,
} }
impl HTMLScriptElement { impl HTMLScriptElement {
@ -81,6 +92,7 @@ impl HTMLScriptElement {
creator: ElementCreator, creator: ElementCreator,
) -> HTMLScriptElement { ) -> HTMLScriptElement {
HTMLScriptElement { HTMLScriptElement {
id: ScriptId(Uuid::new_v4()),
htmlelement: HTMLElement::new_inherited(local_name, prefix, document), htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
already_started: Cell::new(false), already_started: Cell::new(false),
parser_inserted: Cell::new(creator.is_parser_created()), parser_inserted: Cell::new(creator.is_parser_created()),
@ -105,11 +117,15 @@ impl HTMLScriptElement {
HTMLScriptElementBinding::Wrap, HTMLScriptElementBinding::Wrap,
) )
} }
pub fn get_script_id(&self) -> ScriptId {
self.id.clone()
}
} }
/// Supported script types as defined by /// Supported script types as defined by
/// <https://html.spec.whatwg.org/multipage/#javascript-mime-type>. /// <https://html.spec.whatwg.org/multipage/#javascript-mime-type>.
static SCRIPT_JS_MIMES: StaticStringVec = &[ pub static SCRIPT_JS_MIMES: StaticStringVec = &[
"application/ecmascript", "application/ecmascript",
"application/javascript", "application/javascript",
"application/x-ecmascript", "application/x-ecmascript",
@ -143,7 +159,7 @@ pub struct ScriptOrigin {
} }
impl ScriptOrigin { impl ScriptOrigin {
fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin { pub fn internal(text: DOMString, url: ServoUrl, type_: ScriptType) -> ScriptOrigin {
ScriptOrigin { ScriptOrigin {
text: text, text: text,
url: url, 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 { ScriptOrigin {
text: text, text: text,
url: url, url: url,
@ -160,6 +176,10 @@ impl ScriptOrigin {
type_, type_,
} }
} }
pub fn text(&self) -> DOMString {
self.text.clone()
}
} }
pub type ScriptResult = Result<ScriptOrigin, NetworkError>; pub type ScriptResult = Result<ScriptOrigin, NetworkError>;
@ -441,23 +461,25 @@ impl HTMLScriptElement {
} }
// Step 14. // Step 14.
let for_attribute = element.get_attribute(&ns!(), &local_name!("for")); if script_type == ScriptType::Classic {
let event_attribute = element.get_attribute(&ns!(), &local_name!("event")); let for_attribute = element.get_attribute(&ns!(), &local_name!("for"));
match (for_attribute, event_attribute) { let event_attribute = element.get_attribute(&ns!(), &local_name!("event"));
(Some(ref for_attribute), Some(ref event_attribute)) => { match (for_attribute, event_attribute) {
let for_value = for_attribute.value().to_ascii_lowercase(); (Some(ref for_attribute), Some(ref event_attribute)) => {
let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS); let for_value = for_attribute.value().to_ascii_lowercase();
if for_value != "window" { let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
return; if for_value != "window" {
} return;
}
let event_value = event_attribute.value().to_ascii_lowercase(); let event_value = event_attribute.value().to_ascii_lowercase();
let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS); let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
if event_value != "onload" && event_value != "onload()" { if event_value != "onload" && event_value != "onload()" {
return; return;
} }
}, },
(_, _) => (), (_, _) => (),
}
} }
// Step 15. // Step 15.
@ -514,6 +536,7 @@ impl HTMLScriptElement {
}, },
}; };
// Step 24.6.
match script_type { match script_type {
ScriptType::Classic => { ScriptType::Classic => {
// Preparation for step 26. // Preparation for step 26.
@ -555,50 +578,66 @@ impl HTMLScriptElement {
} }
}, },
ScriptType::Module => { ScriptType::Module => {
warn!( fetch_external_module_script(
"{} is a module script. It should be fixed after #23545 landed.", ModuleOwner::Window(Trusted::new(self)),
url.clone() url.clone(),
Destination::Script,
); );
self.global().issue_page_warning(&format!(
"Module scripts are not supported; {} will not be executed.", if !r#async && was_parser_inserted {
url.clone() 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 { } else {
// Step 25. // Step 25.
assert!(!text.is_empty()); assert!(!text.is_empty());
// Step 25-1. // Step 25-1. & 25-2.
let result = Ok(ScriptOrigin::internal( let result = Ok(ScriptOrigin::internal(
text.clone(), text.clone(),
base_url.clone(), base_url.clone(),
script_type.clone(), script_type.clone(),
)); ));
// TODO: Step 25-2. // Step 25-2.
if let ScriptType::Module = script_type { match script_type {
warn!( ScriptType::Classic => {
"{} is a module script. It should be fixed after #23545 landed.", if was_parser_inserted &&
base_url.clone() doc.get_current_parser()
); .map_or(false, |parser| parser.script_nesting_level() <= 1) &&
self.global().issue_page_warning( doc.get_script_blocking_stylesheets_count() > 0
"Module scripts are not supported; ignoring inline module script.", {
); // Step 26.h: classic, has no src, was parser-inserted, is blocked on stylesheet.
return; 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. fetch_inline_module_script(
if was_parser_inserted && ModuleOwner::Window(Trusted::new(self)),
doc.get_current_parser() text.clone(),
.map_or(false, |parser| parser.script_nesting_level() <= 1) && base_url.clone(),
doc.get_script_blocking_stylesheets_count() > 0 self.id.clone(),
{ );
// 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);
} }
} }
} }
@ -656,7 +695,7 @@ impl HTMLScriptElement {
} }
/// <https://html.spec.whatwg.org/multipage/#execute-the-script-block> /// <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. // Step 1.
let doc = document_from_node(self); let doc = document_from_node(self);
if self.parser_inserted.get() && &*doc != &*self.parser_document { if self.parser_inserted.get() && &*doc != &*self.parser_document {
@ -674,10 +713,12 @@ impl HTMLScriptElement {
Ok(script) => script, Ok(script) => script,
}; };
self.unminify_js(&mut script); if script.type_ == ScriptType::Classic {
self.unminify_js(&mut script);
}
// Step 3. // 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); debug!("loading external script, url = {}", script.url);
let doc = document_from_node(self); let doc = document_from_node(self);
doc.incr_ignore_destructive_writes_counter(); doc.incr_ignore_destructive_writes_counter();
@ -690,21 +731,24 @@ impl HTMLScriptElement {
let document = document_from_node(self); let document = document_from_node(self);
let old_script = document.GetCurrentScript(); let old_script = document.GetCurrentScript();
// Step 5.a.1. match script.type_ {
document.set_current_script(Some(self)); 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 5.a.2. // Step 5.
self.run_a_classic_script(&script);
// Step 6.
document.set_current_script(old_script.as_deref());
// Step 7.
if let Some(doc) = neutralized_doc { if let Some(doc) = neutralized_doc {
doc.decr_ignore_destructive_writes_counter(); doc.decr_ignore_destructive_writes_counter();
} }
// Step 8. // Step 6.
if script.external { if script.external {
self.dispatch_load_event(); self.dispatch_load_event();
} }
@ -736,6 +780,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) { pub fn queue_error_event(&self) {
let window = window_from_node(self); let window = window_from_node(self);
window window
@ -818,10 +928,18 @@ impl HTMLScriptElement {
self.parser_inserted.set(parser_inserted); 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) { pub fn set_already_started(&self, already_started: bool) {
self.already_started.set(already_started); self.already_started.set(already_started);
} }
pub fn get_non_blocking(&self) -> bool {
self.non_blocking.get()
}
fn dispatch_event( fn dispatch_event(
&self, &self,
type_: Atom, type_: Atom,

View file

@ -225,7 +225,7 @@ impl Promise {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn promise_obj(&self) -> HandleObject { pub fn promise_obj(&self) -> HandleObject {
let obj = self.reflector().get_jsobject(); let obj = self.reflector().get_jsobject();
unsafe { unsafe {
assert!(IsPromiseObject(obj)); assert!(IsPromiseObject(obj));

View file

@ -83,6 +83,8 @@ mod microtask;
#[warn(deprecated)] #[warn(deprecated)]
mod network_listener; mod network_listener;
#[warn(deprecated)] #[warn(deprecated)]
mod script_module;
#[warn(deprecated)]
pub mod script_runtime; pub mod script_runtime;
#[warn(deprecated)] #[warn(deprecated)]
#[allow(unsafe_code)] #[allow(unsafe_code)]

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,7 @@ use crate::dom::promise::Promise;
use crate::dom::promiserejectionevent::PromiseRejectionEvent; use crate::dom::promiserejectionevent::PromiseRejectionEvent;
use crate::dom::response::Response; use crate::dom::response::Response;
use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue}; use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue};
use crate::script_module::EnsureModuleHooksInitialized;
use crate::script_thread::trace_thread; use crate::script_thread::trace_thread;
use crate::task::TaskBox; use crate::task::TaskBox;
use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::networking::NetworkingTaskSource;
@ -498,6 +499,8 @@ unsafe fn new_rt_and_cx_with_parent(
SetJobQueue(cx, job_queue); SetJobQueue(cx, job_queue);
SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut()); SetPromiseRejectionTrackerCallback(cx, Some(promise_rejection_tracker), ptr::null_mut());
EnsureModuleHooksInitialized(runtime.rt());
set_gc_zeal_options(cx); set_gc_zeal_options(cx);
// Enable or disable the JITs. // Enable or disable the JITs.