/* 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 https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions; use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::{ ServiceWorkerContainerMethods, Wrap, }; use crate::dom::bindings::error::Error; 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::realms::InRealm; use crate::script_thread::ScriptThread; use crate::serviceworkerjob::{Job, JobType}; use dom_struct::dom_struct; use std::default::Default; use std::rc::Rc; #[dom_struct] pub struct ServiceWorkerContainer { eventtarget: EventTarget, controller: MutNullableDom, client: Dom, } impl ServiceWorkerContainer { fn new_inherited(client: &Client) -> ServiceWorkerContainer { ServiceWorkerContainer { eventtarget: EventTarget::new_inherited(), controller: Default::default(), client: Dom::from_ref(client), } } #[allow(unrooted_must_root)] pub fn new(global: &GlobalScope) -> DomRoot { let client = Client::new(&global.as_window()); let container = ServiceWorkerContainer::new_inherited(&*client); reflect_dom_object(Box::new(container), global, Wrap) } } impl ServiceWorkerContainerMethods for ServiceWorkerContainer { // https://w3c.github.io/ServiceWorker/#service-worker-container-controller-attribute fn GetController(&self) -> Option> { 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 fn Register( &self, script_url: USVString, options: &RegistrationOptions, comp: InRealm, ) -> Rc { // A: Step 1 let promise = Promise::new_in_current_realm(&*self.global(), comp); let USVString(ref script_url) = script_url; let api_base_url = self.global().api_base_url(); // A: Step 3-5 let script_url = match api_base_url.join(script_url) { Ok(url) => url, Err(_) => { promise.reject_error(Error::Type("Invalid script URL".to_owned())); return promise; }, }; // B: Step 2 match script_url.scheme() { "https" | "http" => {}, _ => { promise.reject_error(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(Error::Type( "Script URL contains forbidden characters".to_owned(), )); 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" => {}, _ => { 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(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(), options.type_, &*self.client, ); // Job is unrooted here, do not do anything other than immediately scheduling ScriptThread::schedule_job(job); promise } }