diff options
19 files changed, 254 insertions, 116 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 7bd1e324ca9..ba5a4353d12 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -996,6 +996,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got store registration scope message"); self.handle_register_serviceworker(scope_things, scope); } + FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => { + if let Some(ref mgr) = self.swmanager_chan { + let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url)); + } else { + warn!("Unable to forward DOMMessage for postMessage call"); + } + } } } diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index f9c4e98f2f9..21963eec172 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -12,11 +12,14 @@ use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_STRUCTURED_CLONE_VERSION}; use js::jsapi::{JS_ClearPendingException, JS_WriteStructuredClone}; use libc::size_t; use std::ptr; +use std::slice; /// A buffer for a structured clone. -pub struct StructuredCloneData { - data: *mut u64, - nbytes: size_t, +pub enum StructuredCloneData { + /// A non-serializable (default) variant + Struct(*mut u64, size_t), + /// A variant that can be serialized + Vector(Vec<u8>) } impl StructuredCloneData { @@ -39,26 +42,47 @@ impl StructuredCloneData { } return Err(Error::DataClone); } - Ok(StructuredCloneData { - data: data, - nbytes: nbytes, - }) + Ok(StructuredCloneData::Struct(data, nbytes)) + } + + /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing + pub fn move_to_arraybuffer(self) -> Vec<u8> { + match self { + StructuredCloneData::Struct(data, nbytes) => { + unsafe { + slice::from_raw_parts(data as *mut u8, nbytes).to_vec() + } + } + StructuredCloneData::Vector(msg) => msg + } } /// Reads a structured clone. /// /// Panics if `JS_ReadStructuredClone` fails. - pub fn read(self, global: GlobalRef, rval: MutableHandleValue) { + fn read_clone(global: GlobalRef, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) { unsafe { assert!(JS_ReadStructuredClone(global.get_cx(), - self.data, - self.nbytes, + data, + nbytes, JS_STRUCTURED_CLONE_VERSION, rval, ptr::null(), ptr::null_mut())); } } + + /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone. + pub fn read(self, global: GlobalRef, rval: MutableHandleValue) { + match self { + StructuredCloneData::Vector(mut vec_msg) => { + let nbytes = vec_msg.len(); + let data = vec_msg.as_mut_ptr() as *mut u64; + StructuredCloneData::read_clone(global, data, nbytes, rval); + } + StructuredCloneData::Struct(data, nbytes) => StructuredCloneData::read_clone(global, data, nbytes, rval) + } + } } unsafe impl Send for StructuredCloneData {} diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs index 2b79be43dc8..9c37d73bd9e 100644 --- a/components/script/dom/serviceworker.rs +++ b/components/script/dom/serviceworker.rs @@ -2,20 +2,23 @@ * 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::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg}; +use dom::abstractworker::SimpleWorkerErrorHandler; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap}; +use dom::bindings::error::{ErrorResult, Error}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::refcounted::Trusted; -use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::USVString; +use dom::bindings::structuredclone::StructuredCloneData; use dom::eventtarget::EventTarget; +use js::jsapi::{HandleValue, JSContext}; use script_thread::Runnable; +use script_traits::{ScriptMsg, DOMMessage}; use std::cell::Cell; -use std::sync::mpsc::Sender; use url::Url; pub type TrustedServiceWorkerAddress = Trusted<ServiceWorker>; @@ -24,28 +27,31 @@ pub type TrustedServiceWorkerAddress = Trusted<ServiceWorker>; pub struct ServiceWorker { eventtarget: EventTarget, script_url: DOMRefCell<String>, + scope_url: Url, state: Cell<ServiceWorkerState>, - #[ignore_heap_size_of = "Defined in std"] - sender: Option<Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>>, skip_waiting: Cell<bool> } impl ServiceWorker { fn new_inherited(script_url: &str, - skip_waiting: bool) -> ServiceWorker { + skip_waiting: bool, + scope_url: Url) -> ServiceWorker { ServiceWorker { eventtarget: EventTarget::new_inherited(), - sender: None, script_url: DOMRefCell::new(String::from(script_url)), state: Cell::new(ServiceWorkerState::Installing), + scope_url: scope_url, skip_waiting: Cell::new(skip_waiting) } } - pub fn new(global: GlobalRef, - script_url: &str, + pub fn install_serviceworker(global: GlobalRef, + script_url: Url, + scope_url: Url, skip_waiting: bool) -> Root<ServiceWorker> { - reflect_dom_object(box ServiceWorker::new_inherited(script_url, skip_waiting), global, Wrap) + reflect_dom_object(box ServiceWorker::new_inherited(script_url.as_str(), + skip_waiting, + scope_url), global, Wrap) } pub fn dispatch_simple_error(address: TrustedServiceWorkerAddress) { @@ -61,14 +67,6 @@ impl ServiceWorker { pub fn get_script_url(&self) -> Url { Url::parse(&self.script_url.borrow().clone()).unwrap() } - - pub fn install_serviceworker(global: GlobalRef, - script_url: Url, - skip_waiting: bool) -> Root<ServiceWorker> { - ServiceWorker::new(global, - script_url.as_str(), - skip_waiting) - } } impl ServiceWorkerMethods for ServiceWorker { @@ -82,6 +80,20 @@ impl ServiceWorkerMethods for ServiceWorker { USVString(self.script_url.borrow().clone()) } + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-postmessage + fn PostMessage(&self, cx: *mut JSContext, message: HandleValue) -> ErrorResult { + // Step 1 + if let ServiceWorkerState::Redundant = self.state.get() { + return Err(Error::InvalidState); + } + // Step 7 + let data = try!(StructuredCloneData::write(cx, message)); + let msg_vec = DOMMessage(data.move_to_arraybuffer()); + let _ = self.global().r().constellation_chan().send(ScriptMsg::ForwardDOMMessage(msg_vec, + self.scope_url.clone())); + Ok(()) + } + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container-onerror-attribute event_handler!(error, GetOnerror, SetOnerror); diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs index d6379c30183..73e19690e2e 100644 --- a/components/script/dom/serviceworkercontainer.rs +++ b/components/script/dom/serviceworkercontainer.rs @@ -95,10 +95,9 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer { return Err(Error::Type("Scope URL contains forbidden characters".to_owned())); } - let scope_str = scope.as_str().to_owned(); let worker_registration = ServiceWorkerRegistration::new(self.global().r(), script_url, - scope_str.clone(), + scope.clone(), self); ScriptThread::set_registration(scope, &*worker_registration, self.global().r().pipeline_id()); Ok(worker_registration) diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 16d5348b6e5..f473c6cadb2 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -5,7 +5,6 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; use dom::abstractworker::WorkerScriptMsg; -use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods; @@ -16,7 +15,6 @@ use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::eventtarget::EventTarget; use dom::messageevent::MessageEvent; -use dom::serviceworker::TrustedServiceWorkerAddress; use dom::workerglobalscope::WorkerGlobalScope; use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; use ipc_channel::router::ROUTER; @@ -26,13 +24,12 @@ use js::rust::Runtime; use msg::constellation_msg::PipelineId; use net_traits::{LoadContext, load_whole_resource, IpcSend, CustomResponseMediator}; use rand::random; -use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx}; +use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx, ScriptChan}; use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg}; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; use std::thread; use std::time::Duration; -use style::thread_state; -use style::thread_state::{IN_WORKER, SCRIPT}; +use style::thread_state::{self, IN_WORKER, SCRIPT}; use url::Url; use util::prefs::PREFS; use util::thread::spawn_named; @@ -48,7 +45,26 @@ pub enum ServiceWorkerScriptMsg { pub enum MixedMessage { FromServiceWorker(ServiceWorkerScriptMsg), FromDevtools(DevtoolScriptControlMsg), - FromTimeoutThread(()), + FromTimeoutThread(()) +} + +#[derive(JSTraceable, Clone)] +pub struct ServiceWorkerChan { + pub sender: Sender<ServiceWorkerScriptMsg> +} + +impl ScriptChan for ServiceWorkerChan { + fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { + self.sender + .send(ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(msg))) + .map_err(|_| ()) + } + + fn clone(&self) -> Box<ScriptChan + Send> { + box ServiceWorkerChan { + sender: self.sender.clone(), + } + } } #[dom_struct] @@ -61,12 +77,9 @@ pub struct ServiceWorkerGlobalScope { own_sender: Sender<ServiceWorkerScriptMsg>, #[ignore_heap_size_of = "Defined in std"] timer_event_port: Receiver<()>, - #[ignore_heap_size_of = "Trusted<T> has unclear ownership like JS<T>"] - worker: DOMRefCell<Option<TrustedServiceWorkerAddress>>, #[ignore_heap_size_of = "Defined in std"] swmanager_sender: IpcSender<ServiceWorkerMsg>, - #[ignore_heap_size_of = "Defined in std"] - scope_url: Url + scope_url: Url, } impl ServiceWorkerGlobalScope { @@ -93,7 +106,6 @@ impl ServiceWorkerGlobalScope { receiver: receiver, timer_event_port: timer_event_port, own_sender: own_sender, - worker: DOMRefCell::new(None), swmanager_sender: swmanager_sender, scope_url: scope_url } @@ -228,8 +240,7 @@ impl ServiceWorkerGlobalScope { CommonWorker(WorkerScriptMsg::DOMMessage(data)) => { let scope = self.upcast::<WorkerGlobalScope>(); let target = self.upcast(); - let _ac = JSAutoCompartment::new(scope.get_cx(), - scope.reflector().get_jsobject().get()); + let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get()); rooted!(in(scope.get_cx()) let mut message = UndefinedValue()); data.read(GlobalRef::Worker(scope), message.handle_mut()); MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle()); @@ -292,6 +303,12 @@ impl ServiceWorkerGlobalScope { pub fn process_event(&self, msg: CommonScriptMsg) { self.handle_script_event(ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(msg))); } + + pub fn script_chan(&self) -> Box<ScriptChan + Send> { + box ServiceWorkerChan { + sender: self.own_sender.clone() + } + } } #[allow(unsafe_code)] diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs index ccf238dde8a..d246dc4423b 100644 --- a/components/script/dom/serviceworkerregistration.rs +++ b/components/script/dom/serviceworkerregistration.rs @@ -25,21 +25,21 @@ pub struct ServiceWorkerRegistration { } impl ServiceWorkerRegistration { - fn new_inherited(active_sw: &ServiceWorker, scope: String) -> ServiceWorkerRegistration { + fn new_inherited(active_sw: &ServiceWorker, scope: Url) -> ServiceWorkerRegistration { ServiceWorkerRegistration { eventtarget: EventTarget::new_inherited(), active: Some(JS::from_ref(active_sw)), installing: None, waiting: None, - scope: scope, + scope: scope.as_str().to_owned(), } } #[allow(unrooted_must_root)] pub fn new(global: GlobalRef, script_url: Url, - scope: String, + scope: Url, container: &Controllable) -> Root<ServiceWorkerRegistration> { - let active_worker = ServiceWorker::install_serviceworker(global, script_url.clone(), true); + let active_worker = ServiceWorker::install_serviceworker(global, script_url.clone(), scope.clone(), true); active_worker.set_transition_state(ServiceWorkerState::Installed); container.set_controller(&*active_worker.clone()); reflect_dom_object(box ServiceWorkerRegistration::new_inherited(&*active_worker, scope), global, Wrap) diff --git a/components/script/dom/webidls/ServiceWorker.webidl b/components/script/dom/webidls/ServiceWorker.webidl index efea6603c3f..0d0855f4239 100644 --- a/components/script/dom/webidls/ServiceWorker.webidl +++ b/components/script/dom/webidls/ServiceWorker.webidl @@ -7,7 +7,7 @@ interface ServiceWorker : EventTarget { readonly attribute USVString scriptURL; readonly attribute ServiceWorkerState state; - //[Throws] void postMessage(any message/*, optional sequence<Transferable> transfer*/); + [Throws] void postMessage(any message/*, optional sequence<Transferable> transfer*/); // event attribute EventHandler onstatechange; diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 95e9bb13106..bb9a9c3f4ab 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -397,12 +397,14 @@ impl WorkerGlobalScope { } pub fn script_chan(&self) -> Box<ScriptChan + Send> { - let dedicated = - self.downcast::<DedicatedWorkerGlobalScope>(); + let dedicated = self.downcast::<DedicatedWorkerGlobalScope>(); + let service_worker = self.downcast::<ServiceWorkerGlobalScope>(); if let Some(dedicated) = dedicated { return dedicated.script_chan(); + } else if let Some(service_worker) = service_worker { + return service_worker.script_chan(); } else { - panic!("need to implement a sender for SharedWorker/ServiceWorker") + panic!("need to implement a sender for SharedWorker") } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2e9bbf617f3..4a19547138c 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1450,7 +1450,7 @@ impl ScriptThread { let scope_things = ServiceWorkerRegistration::create_scope_things(global_ref, script_url); let _ = self.constellation_chan.send(ConstellationMsg::RegisterServiceWorker(scope_things, scope)); } else { - warn!("Registration failed for {}", pipeline_id); + warn!("Registration failed for {}", scope); } } diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index 09f5288929a..3ce26a7693e 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -8,12 +8,14 @@ //! active_workers map use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; +use dom::abstractworker::WorkerScriptMsg; +use dom::bindings::structuredclone::StructuredCloneData; use dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg}; use dom::serviceworkerregistration::longest_prefix_match; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::{CustomResponseMediator, CoreResourceMsg}; -use script_traits::{ServiceWorkerMsg, ScopeThings, SWManagerMsg, SWManagerSenders}; +use script_traits::{ServiceWorkerMsg, ScopeThings, SWManagerMsg, SWManagerSenders, DOMMessage}; use std::collections::HashMap; use std::sync::mpsc::{channel, Sender, Receiver, RecvError}; use url::Url; @@ -29,7 +31,7 @@ pub struct ServiceWorkerManager { // map of registered service worker descriptors registered_workers: HashMap<Url, ScopeThings>, // map of active service worker descriptors - active_workers: HashMap<Url, ScopeThings>, + active_workers: HashMap<Url, Sender<ServiceWorkerScriptMsg>>, // own sender to send messages here own_sender: IpcSender<ServiceWorkerMsg>, // receiver to receive messages from constellation @@ -59,54 +61,50 @@ impl ServiceWorkerManager { let _ = sw_senders.resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan)); let _ = sw_senders.swmanager_sender.send(SWManagerMsg::OwnSender(own_sender.clone())); spawn_named("ServiceWorkerManager".to_owned(), move || { - ServiceWorkerManager::new(own_sender, from_constellation, resource_port).handle_message(); + ServiceWorkerManager::new(own_sender, + from_constellation, + resource_port).handle_message(); }); } - pub fn prepare_activation(&mut self, load_url: &Url) -> Option<Sender<ServiceWorkerScriptMsg>> { - let mut scope_url = None; + pub fn get_matching_scope(&self, load_url: &Url) -> Option<Url> { for scope in self.registered_workers.keys() { if longest_prefix_match(&scope, load_url) { - scope_url = Some(scope.clone()); - break; + return Some(scope.clone()); } } + None + } - if let Some(scope_url) = scope_url { - if self.active_workers.contains_key(&scope_url) { - // do not run the same worker if already active. - warn!("Service worker for {:?} already active", scope_url); - return None; - } - let scope_things = self.registered_workers.get(&scope_url); - if let Some(scope_things) = scope_things { - let (sender, receiver) = channel(); - let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); - if let Some(ref chan) = scope_things.devtools_chan { - let title = format!("ServiceWorker for {}", scope_things.script_url); - let page_info = DevtoolsPageInfo { - title: title, - url: scope_things.script_url.clone(), - }; - let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal((scope_things.pipeline_id, - Some(scope_things.worker_id)), - devtools_sender.clone(), - page_info)); + pub fn wakeup_serviceworker(&mut self, scope_url: Url) -> Option<Sender<ServiceWorkerScriptMsg>> { + let scope_things = self.registered_workers.get(&scope_url); + if let Some(scope_things) = scope_things { + let (sender, receiver) = channel(); + let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); + if let Some(ref chan) = scope_things.devtools_chan { + let title = format!("ServiceWorker for {}", scope_things.script_url); + let page_info = DevtoolsPageInfo { + title: title, + url: scope_things.script_url.clone(), }; - ServiceWorkerGlobalScope::run_serviceworker_scope(scope_things.clone(), - sender.clone(), - receiver, - devtools_receiver, - self.own_sender.clone(), - scope_url.clone()); - // We store the activated worker - self.active_workers.insert(scope_url, scope_things.clone()); - return Some(sender); - } else { - warn!("Unable to activate service worker"); - } + let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal((scope_things.pipeline_id, + Some(scope_things.worker_id)), + devtools_sender, + page_info)); + }; + ServiceWorkerGlobalScope::run_serviceworker_scope(scope_things.clone(), + sender.clone(), + receiver, + devtools_receiver, + self.own_sender.clone(), + scope_url.clone()); + // We store the activated worker + self.active_workers.insert(scope_url, sender.clone()); + return Some(sender); + } else { + warn!("Unable to activate service worker"); + None } - None } fn handle_message(&mut self) { @@ -125,6 +123,12 @@ impl ServiceWorkerManager { } } + fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) { + let DOMMessage(data) = msg; + let data = StructuredCloneData::Vector(data); + let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::DOMMessage(data))); + } + fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool { match msg { ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope) => { @@ -139,7 +143,19 @@ impl ServiceWorkerManager { if self.active_workers.contains_key(&scope) { let _ = self.active_workers.remove(&scope); } else { - warn!("ScopeThings for {:?} is not active", scope); + warn!("ServiceWorker for {:?} is not active", scope); + } + true + }, + ServiceWorkerMsg::ForwardDOMMessage(msg, scope_url) => { + if self.active_workers.contains_key(&scope_url) { + if let Some(ref sender) = self.active_workers.get(&scope_url) { + self.forward_message(msg, &sender); + } + } else { + if let Some(ref sender) = self.wakeup_serviceworker(scope_url) { + self.forward_message(msg, &sender); + } } true } @@ -147,12 +163,20 @@ impl ServiceWorkerManager { } } - #[inline] fn handle_message_from_resource(&mut self, mediator: CustomResponseMediator) -> bool { if serviceworker_enabled() { - let worker_sender = self.prepare_activation(&mediator.load_url); - if let Some(ref sender) = worker_sender { - let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator)); + if let Some(scope) = self.get_matching_scope(&mediator.load_url) { + if self.active_workers.contains_key(&scope) { + if let Some(sender) = self.active_workers.get(&scope) { + let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator)); + } + } else { + if let Some(sender) = self.wakeup_serviceworker(scope) { + let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator)); + } + } + } else { + let _ = mediator.response_chan.send(None); } } else { let _ = mediator.response_chan.send(None); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 104483d2b3f..854e3eadeef 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -71,7 +71,7 @@ use util::ipc::OptionalOpaqueIpcSender; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry}; -pub use script_msg::{ServiceWorkerMsg, ScopeThings, SWManagerMsg, SWManagerSenders}; +pub use script_msg::{ServiceWorkerMsg, ScopeThings, SWManagerMsg, SWManagerSenders, DOMMessage}; /// The address of a node. Layout sends these back. They must be validated via /// `from_untrusted_node_address` before they can be used, because we do not trust layout. @@ -212,7 +212,7 @@ pub enum ConstellationControlMsg { /// Report an error from a CSS parser for the given pipeline ReportCSSError(PipelineId, String, usize, usize, String), /// Reload the given page. - Reload(PipelineId), + Reload(PipelineId) } impl fmt::Debug for ConstellationControlMsg { @@ -241,7 +241,7 @@ impl fmt::Debug for ConstellationControlMsg { DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent", FramedContentChanged(..) => "FramedContentChanged", ReportCSSError(..) => "ReportCSSError", - Reload(..) => "Reload", + Reload(..) => "Reload" }) } } diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index c19766263a5..aaa22732937 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -136,6 +136,9 @@ pub enum ScriptMsg { LogEntry(Option<PipelineId>, Option<String>, LogEntry), /// Notifies the constellation that this pipeline has exited. PipelineExited(PipelineId), + /// Send messages from postMessage calls from serviceworker + /// to constellation for storing in service worker manager + ForwardDOMMessage(DOMMessage, Url), /// Store the data required to activate a service worker for the given scope RegisterServiceWorker(ScopeThings, Url), /// Requests that the compositor shut down. @@ -159,6 +162,10 @@ pub struct ScopeThings { pub worker_id: WorkerId, } +/// Message that gets passed to service worker scope on postMessage +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct DOMMessage(pub Vec<u8>); + /// Channels to allow service worker manager to communicate with constellation and resource thread pub struct SWManagerSenders { /// sender for communicating with constellation @@ -174,6 +181,8 @@ pub enum ServiceWorkerMsg { RegisterServiceWorker(ScopeThings, Url), /// Timeout message sent by active service workers Timeout(Url), + /// Message sent by constellation to forward to a running service worker + ForwardDOMMessage(DOMMessage, Url), /// Exit the service worker manager Exit, } @@ -182,5 +191,6 @@ pub enum ServiceWorkerMsg { #[derive(Deserialize, Serialize)] pub enum SWManagerMsg { /// Provide the constellation with a means of communicating with the Service Worker Manager - OwnSender(IpcSender<ServiceWorkerMsg>), + OwnSender(IpcSender<ServiceWorkerMsg>) + } diff --git a/tests/html/service-worker/demo_iframe.html b/tests/html/service-worker/demo_iframe.html index 1da0e4d7882..c4da41ffe1f 100644 --- a/tests/html/service-worker/demo_iframe.html +++ b/tests/html/service-worker/demo_iframe.html @@ -8,6 +8,5 @@ <div> <a href="http://github.com/servo/servo">Servo Project</a> </div> - </body> </html> diff --git a/tests/html/service-worker/dummy.js b/tests/html/service-worker/dummy.js new file mode 100644 index 00000000000..e9478cb299e --- /dev/null +++ b/tests/html/service-worker/dummy.js @@ -0,0 +1,3 @@ +function test_importScripts() { + console.log('Hi from dummy.js'); +}
\ No newline at end of file diff --git a/tests/html/service-worker/iframe_sw.js b/tests/html/service-worker/iframe_sw.js index d9c76b8d6f1..64adbeaa9ab 100644 --- a/tests/html/service-worker/iframe_sw.js +++ b/tests/html/service-worker/iframe_sw.js @@ -1,7 +1,11 @@ self.addEventListener('activate', function(e) { - console.log("iframe service worker active"); + console.log("iframe service worker active"); }); self.addEventListener('fetch', function(e) { - console.log("A fetch event detected by /iframe service worker"); -});
\ No newline at end of file + console.log("A fetch event detected by /iframe service worker"); +}); + +self.addEventListener('message', function(e) { + console.log(e.data.payload.msg + ' from '+ e.data.payload.worker_id); +}) diff --git a/tests/html/service-worker/index.html b/tests/html/service-worker/index.html index a20fcbb67e4..ef971d0a461 100644 --- a/tests/html/service-worker/index.html +++ b/tests/html/service-worker/index.html @@ -16,36 +16,56 @@ <button onclick="register_zero()">Register for Root (/)</button> <button onclick="register_one()">Register for Dashboard (/dashboard)</button> <button onclick="register_two()">Register for Profile (/profile)</button> + <button onclick="post_message_root()">Msg to Root service worker</button> </div> - <script> + var reg = navigator.serviceWorker.register('iframe_sw.js', { 'scope': '/demo_iframe.html' }); console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); document.getElementById('iframe_sw').src = 'demo_iframe.html'; + post_message('/demo_iframe'); function register_zero() { - var reg = navigator.serviceWorker.register('sw.js', { 'scope': './' }); + var reg = navigator.serviceWorker.register('sw.js', { 'scope': './*' }); console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); } + function post_message_root() { + // Testing repeated calls of postMessage + for(var i=0;i<100;i++){ + navigator.serviceWorker.controller.postMessage({num:'Dummy message count: '+i}); + } + } + function register_one() { var reg = navigator.serviceWorker.register('sw_dashboard.js', { 'scope': '/dashboard' }); console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); + post_message('/dashboard'); } function register_two() { var reg = navigator.serviceWorker.register('sw_profile.js', { 'scope': '/profile' }); console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); + post_message('/profile'); } navigator.serviceWorker.addEventListener('controllerchange', function(e) { - console.log("active Service Worker changed"); + console.log("navigator.serviceWorker.controller changed"); }); + function post_message(worker_id) { + navigator.serviceWorker.controller.postMessage({ + payload:{ + msg:'Hi there! This is a dummy message for testing of postMessage calls from Service Workers', + worker_id:worker_id + } + }); + } + </script> </body> </html> diff --git a/tests/html/service-worker/sw.js b/tests/html/service-worker/sw.js index e69de29bb2d..04649167e5a 100644 --- a/tests/html/service-worker/sw.js +++ b/tests/html/service-worker/sw.js @@ -0,0 +1,11 @@ +self.addEventListener('activate', function(e) { + console.log('Root service worker active'); +}); + +self.addEventListener('fetch', function(e) { + console.log("A fetch event detected by / service worker"); +}); + +self.addEventListener('message', function(e) { + console.log(e.data.num); +}) diff --git a/tests/html/service-worker/sw_dashboard.js b/tests/html/service-worker/sw_dashboard.js index 472fe03cca1..5789e0fc361 100644 --- a/tests/html/service-worker/sw_dashboard.js +++ b/tests/html/service-worker/sw_dashboard.js @@ -1,9 +1,11 @@ -importScripts('dashboard_helper.js'); - self.addEventListener('activate', function(e) { - status_from_dashboard(); + console.log('Dashboard service worker active'); }); self.addEventListener('fetch', function(e) { - console.log("A fetch event detected by /dashboard service worker"); -});
\ No newline at end of file + console.log("A fetch event detected by /dashboard service worker"); +}); + +self.addEventListener('message', function(e) { + console.log(e.data.payload.msg + ' from '+ e.data.payload.worker_id); +}) diff --git a/tests/html/service-worker/sw_profile.js b/tests/html/service-worker/sw_profile.js index 8834ece7719..e1871f125c8 100644 --- a/tests/html/service-worker/sw_profile.js +++ b/tests/html/service-worker/sw_profile.js @@ -1,8 +1,12 @@ self.addEventListener('activate', function(e) { - console.log("profile service worker active"); + console.log("profile service worker active"); }); self.addEventListener('fetch', function(e) { - console.log("A fetch event detected by /profile service worker"); -});
\ No newline at end of file + console.log("A fetch event detected by /profile service worker"); +}); + +self.addEventListener('message', function(e) { + console.log(e.data.payload.msg + ' from '+ e.data.payload.worker_id); +}) |