aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/serviceworkercontainer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/serviceworkercontainer.rs')
-rw-r--r--components/script/dom/serviceworkercontainer.rs268
1 files changed, 209 insertions, 59 deletions
diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs
index ba2e327409f..b65dcd59b7d 100644
--- a/components/script/dom/serviceworkercontainer.rs
+++ b/components/script/dom/serviceworkercontainer.rs
@@ -1,30 +1,38 @@
/* 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 dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::{ServiceWorkerContainerMethods, Wrap};
-use dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions;
-use dom::bindings::error::Error;
-use dom::bindings::js::{JS, MutNullableJS, Root};
-use dom::bindings::reflector::{DomObject, reflect_dom_object};
-use dom::bindings::str::USVString;
-use dom::client::Client;
-use dom::eventtarget::EventTarget;
-use dom::globalscope::GlobalScope;
-use dom::promise::Promise;
-use dom::serviceworker::ServiceWorker;
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+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;
+use crate::dom::client::Client;
+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::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 script_thread::ScriptThread;
-use serviceworkerjob::{Job, JobType};
-use std::ascii::AsciiExt;
+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;
#[dom_struct]
pub struct ServiceWorkerContainer {
eventtarget: EventTarget,
- controller: MutNullableJS<ServiceWorker>,
- client: JS<Client>
+ controller: MutNullableDom<ServiceWorker>,
+ client: Dom<Client>,
}
impl ServiceWorkerContainer {
@@ -32,89 +40,231 @@ impl ServiceWorkerContainer {
ServiceWorkerContainer {
eventtarget: EventTarget::new_inherited(),
controller: Default::default(),
- client: JS::from_ref(client),
+ client: Dom::from_ref(client),
}
}
#[allow(unrooted_must_root)]
- pub fn new(global: &GlobalScope) -> Root<ServiceWorkerContainer> {
+ pub fn new(global: &GlobalScope) -> DomRoot<ServiceWorkerContainer> {
let client = Client::new(&global.as_window());
let container = ServiceWorkerContainer::new_inherited(&*client);
- reflect_dom_object(box container, global, Wrap)
+ reflect_dom_object(Box::new(container), global)
}
}
impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
// https://w3c.github.io/ServiceWorker/#service-worker-container-controller-attribute
- fn GetController(&self) -> Option<Root<ServiceWorker>> {
+ fn GetController(&self) -> Option<DomRoot<ServiceWorker>> {
self.client.get_controller()
}
- #[allow(unrooted_must_root)]
- // https://w3c.github.io/ServiceWorker/#service-worker-container-register-method and - A
- // https://w3c.github.io/ServiceWorker/#start-register-algorithm - B
- fn Register(&self,
- script_url: USVString,
- options: &RegistrationOptions) -> Rc<Promise> {
+ /// 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(&*self.global());
- let ctx = (&*self.global()).get_cx();
+ 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(_) => {
- promise.reject_error(ctx, Error::Type("Invalid script URL".to_owned()));
+ // B: Step 1
+ promise.reject_error(Error::Type("Invalid script URL".to_owned()));
return promise;
- }
+ },
};
- // B: Step 2
- match script_url.scheme() {
- "https" | "http" => {},
- _ => {
- promise.reject_error(ctx, Error::Type("Only secure origins are allowed".to_owned()));
- return promise;
- }
- }
- // B: Step 3
- if script_url.path().to_ascii_lowercase().contains("%2f") ||
- script_url.path().to_ascii_lowercase().contains("%5c") {
- promise.reject_error(ctx, Error::Type("Script URL contains forbidden characters".to_owned()));
- return promise;
- }
- // B: Step 4-5
+
+ // 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(ctx, Error::Type("Invalid scope URL".to_owned()));
+ promise.reject_error(Error::Type("Invalid scope URL".to_owned()));
return promise;
- }
+ },
}
},
- None => script_url.join("./").unwrap()
+ None => script_url.join("./").unwrap(),
};
+
+ // A: Step 6 -> invoke B.
+
+ // B: Step 3
+ match script_url.scheme() {
+ "https" | "http" => {},
+ _ => {
+ promise.reject_error(Error::Type("Only secure origins are allowed".to_owned()));
+ return promise;
+ },
+ }
+ // B: Step 4
+ if script_url.path().to_ascii_lowercase().contains("%2f") ||
+ script_url.path().to_ascii_lowercase().contains("%5c")
+ {
+ promise.reject_error(Error::Type(
+ "Script URL contains forbidden characters".to_owned(),
+ ));
+ return promise;
+ }
+
// B: Step 6
match scope.scheme() {
"https" | "http" => {},
_ => {
- promise.reject_error(ctx, Error::Type("Only secure origins are allowed".to_owned()));
+ promise.reject_error(Error::Type("Only secure origins are allowed".to_owned()));
return promise;
- }
+ },
}
// B: Step 7
if scope.path().to_ascii_lowercase().contains("%2f") ||
- scope.path().to_ascii_lowercase().contains("%5c") {
- promise.reject_error(ctx, Error::Type("Scope URL contains forbidden characters".to_owned()));
+ scope.path().to_ascii_lowercase().contains("%5c")
+ {
+ promise.reject_error(Error::Type(
+ "Scope URL contains forbidden characters".to_owned(),
+ ));
return promise;
}
- // B: Step 8
- let job = Job::create_job(JobType::Register, scope, script_url, promise.clone(), &*self.client);
- ScriptThread::schedule_job(job, &*self.global());
+ // 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,
+ job_result_sender,
+ self.client.creation_url(),
+ Some(scope_things),
+ );
+
+ // 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.
+ },
+ }
+ }
+}