diff options
author | Gregory Terzian <gterzian@users.noreply.github.com> | 2020-04-25 16:01:28 +0800 |
---|---|---|
committer | Gregory Terzian <gterzian@users.noreply.github.com> | 2020-05-21 13:21:21 +0800 |
commit | 89eb7c2aa2a949ac851ea33382af3c54efdcbc17 (patch) | |
tree | 00604f6a90e1e6a230892a4d2b191c0c92c9e0b5 /components/script/dom/serviceworkercontainer.rs | |
parent | 7e74f733016f2d384791fddcf0c5071cbce3e7cb (diff) | |
download | servo-89eb7c2aa2a949ac851ea33382af3c54efdcbc17.tar.gz servo-89eb7c2aa2a949ac851ea33382af3c54efdcbc17.zip |
serviceworker: make job queue unique per origin
Diffstat (limited to 'components/script/dom/serviceworkercontainer.rs')
-rw-r--r-- | components/script/dom/serviceworkercontainer.rs | 194 |
1 files changed, 164 insertions, 30 deletions
diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs index 8145729d857..b65dcd59b7d 100644 --- a/components/script/dom/serviceworkercontainer.rs +++ b/components/script/dom/serviceworkercontainer.rs @@ -5,6 +5,7 @@ use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions; use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::ServiceWorkerContainerMethods; use crate::dom::bindings::error::Error; +use crate::dom::bindings::refcounted::TrustedPromise; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::USVString; @@ -13,10 +14,17 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::serviceworker::ServiceWorker; +use crate::dom::serviceworkerregistration::ServiceWorkerRegistration; +use crate::realms::enter_realm; use crate::realms::InRealm; -use crate::script_thread::ScriptThread; -use crate::serviceworkerjob::{Job, JobType}; +use crate::task::TaskCanceller; +use crate::task_source::dom_manipulation::DOMManipulationTaskSource; +use crate::task_source::TaskSource; +use crate::task_source::TaskSourceName; use dom_struct::dom_struct; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use script_traits::{Job, JobError, JobResult, JobResultValue, JobType, ScriptMsg}; use std::default::Default; use std::rc::Rc; @@ -50,28 +58,50 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { self.client.get_controller() } - #[allow(unrooted_must_root)] // Job is unrooted - /// https://w3c.github.io/ServiceWorker/#navigator-service-worker-register and - A - /// https://w3c.github.io/ServiceWorker/#start-register-algorithm - B + /// https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-register - A + /// and https://w3c.github.io/ServiceWorker/#start-register - B fn Register( &self, script_url: USVString, options: &RegistrationOptions, comp: InRealm, ) -> Rc<Promise> { + // A: Step 2. + let global = self.client.global(); + // A: Step 1 - let promise = Promise::new_in_current_realm(&*self.global(), comp); + let promise = Promise::new_in_current_realm(&*global, comp); let USVString(ref script_url) = script_url; - let api_base_url = self.global().api_base_url(); - // A: Step 3-5 + + // A: Step 3 + let api_base_url = global.api_base_url(); let script_url = match api_base_url.join(script_url) { Ok(url) => url, Err(_) => { + // B: Step 1 promise.reject_error(Error::Type("Invalid script URL".to_owned())); return promise; }, }; - // B: Step 2 + + // A: Step 4-5 + let scope = match options.scope { + Some(ref scope) => { + let &USVString(ref inner_scope) = scope; + match api_base_url.join(inner_scope) { + Ok(url) => url, + Err(_) => { + promise.reject_error(Error::Type("Invalid scope URL".to_owned())); + return promise; + }, + } + }, + None => script_url.join("./").unwrap(), + }; + + // A: Step 6 -> invoke B. + + // B: Step 3 match script_url.scheme() { "https" | "http" => {}, _ => { @@ -79,7 +109,7 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { return promise; }, } - // B: Step 3 + // B: Step 4 if script_url.path().to_ascii_lowercase().contains("%2f") || script_url.path().to_ascii_lowercase().contains("%5c") { @@ -88,20 +118,7 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { )); return promise; } - // B: Step 4-5 - let scope = match options.scope { - Some(ref scope) => { - let &USVString(ref inner_scope) = scope; - match api_base_url.join(inner_scope) { - Ok(url) => url, - Err(_) => { - promise.reject_error(Error::Type("Invalid scope URL".to_owned())); - return promise; - }, - } - }, - None => script_url.join("./").unwrap(), - }; + // B: Step 6 match scope.scheme() { "https" | "http" => {}, @@ -120,17 +137,134 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { return promise; } - // B: Step 8 + // Setup the callback for reject/resolve of the promise, + // from steps running "in-parallel" from here in the serviceworker manager. + let (task_source, task_canceller) = ( + global.dom_manipulation_task_source(), + global.task_canceller(TaskSourceName::DOMManipulation), + ); + + let mut handler = RegisterJobResultHandler { + trusted_promise: Some(TrustedPromise::new(promise.clone())), + task_source, + task_canceller, + }; + + let (job_result_sender, job_result_receiver) = ipc::channel().expect("ipc channel failure"); + + ROUTER.add_route( + job_result_receiver.to_opaque(), + Box::new(move |message| { + let msg = message.to(); + match msg { + Ok(msg) => handler.handle(msg), + Err(err) => warn!("Error receiving a JobResult: {:?}", err), + } + }), + ); + + let scope_things = + ServiceWorkerRegistration::create_scope_things(&*global, script_url.clone()); + + // B: Step 8 - 13 let job = Job::create_job( JobType::Register, scope, script_url, - promise.clone(), - options.type_, - &*self.client, + job_result_sender, + self.client.creation_url(), + Some(scope_things), ); - // Job is unrooted here, do not do anything other than immediately scheduling - ScriptThread::schedule_job(job); + + // B: Step 14: schedule job. + let _ = global + .script_to_constellation_chan() + .send(ScriptMsg::ScheduleJob(job)); + + // A: Step 7 promise } } + +/// Callback for resolve/reject job promise for Register. +/// <https://w3c.github.io/ServiceWorker/#register> +struct RegisterJobResultHandler { + trusted_promise: Option<TrustedPromise>, + task_source: DOMManipulationTaskSource, + task_canceller: TaskCanceller, +} + +impl RegisterJobResultHandler { + /// <https://w3c.github.io/ServiceWorker/#reject-job-promise> + /// <https://w3c.github.io/ServiceWorker/#resolve-job-promise> + /// Handle a result to either resolve or reject the register job promise. + pub fn handle(&mut self, result: JobResult) { + match result { + JobResult::RejectPromise(error) => { + let promise = self + .trusted_promise + .take() + .expect("No promise to resolve for SW Register job."); + + // Step 1 + let _ = self.task_source.queue_with_canceller( + task!(reject_promise_with_security_error: move || { + let promise = promise.root(); + let _ac = enter_realm(&*promise.global()); + match error { + JobError::TypeError => { + promise.reject_error(Error::Type("Failed to register a ServiceWorker".to_string())); + }, + JobError::SecurityError => { + promise.reject_error(Error::Security); + }, + } + + }), + &self.task_canceller, + ); + + // TODO: step 2, handle equivalent jobs. + }, + JobResult::ResolvePromise(job, value) => { + let promise = self + .trusted_promise + .take() + .expect("No promise to resolve for SW Register job."); + + // Step 1 + let _ = self.task_source.queue_with_canceller( + task!(resolve_promise: move || { + let promise = promise.root(); + let global = promise.global(); + let _ac = enter_realm(&*global); + + // Step 1.1 + let JobResultValue::Registration { + id, + installing_worker, + waiting_worker, + active_worker, + } = value; + + // Step 1.2 (Job type is "register"). + let registration = global.get_serviceworker_registration( + &job.script_url, + &job.scope_url, + id, + installing_worker, + waiting_worker, + active_worker, + ); + + // Step 1.4 + promise.resolve_native(&*registration); + }), + &self.task_canceller, + ); + + // TODO: step 2, handle equivalent jobs. + }, + } + } +} |