Skip to content

Commit

Permalink
Make service workers talk to their serviceworkerglobalscopes
Browse files Browse the repository at this point in the history
  • Loading branch information
creativcoder committed Sep 7, 2016
1 parent 72279cc commit 0996b38
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 46 deletions.
6 changes: 6 additions & 0 deletions components/constellation/constellation.rs
Expand Up @@ -738,6 +738,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// store service worker manager for communicating with it.
self.swmanager_chan = Some(sw_sender);
}
SWManagerMsg::ConnectServiceWorker(scope_url, pipeline_id, msg_chan) => {
if let Some(ref parent_info) = self.pipelines.get(&pipeline_id) {
let from_cons_msg = ConstellationControlMsg::ConnectServiceWorker(scope_url, msg_chan);
let _ = parent_info.script_chan.send(from_cons_msg);
}
}
}
}

Expand Down
16 changes: 16 additions & 0 deletions components/script/dom/bindings/structuredclone.rs
Expand Up @@ -12,6 +12,7 @@ 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 {
Expand Down Expand Up @@ -45,6 +46,21 @@ impl StructuredCloneData {
})
}

/// Converts a StructuredCloneData to Vec<u64>
pub fn move_to_arraybuffer(self) -> Vec<u64> {
unsafe {
slice::from_raw_parts(self.data, self.nbytes).to_vec()
}
}

/// Converts back to StructuredCloneData using a pointer and no of bytes
pub fn make_structured_clone(data: *mut u64, nbytes: size_t) -> StructuredCloneData {
StructuredCloneData {
data: data,
nbytes: nbytes
}
}

/// Reads a structured clone.
///
/// Panics if `JS_ReadStructuredClone` fails.
Expand Down
33 changes: 28 additions & 5 deletions components/script/dom/serviceworker.rs
Expand Up @@ -2,20 +2,24 @@
* 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;
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::str::USVString;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::eventtarget::EventTarget;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{HandleValue, JSContext};
use script_thread::Runnable;
use script_traits::DOMMessage;
use std::cell::Cell;
use std::sync::mpsc::Sender;
use url::Url;

pub type TrustedServiceWorkerAddress = Trusted<ServiceWorker>;
Expand All @@ -26,7 +30,7 @@ pub struct ServiceWorker {
script_url: DOMRefCell<String>,
state: Cell<ServiceWorkerState>,
#[ignore_heap_size_of = "Defined in std"]
sender: Option<Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>>,
msg_sender: DOMRefCell<Option<IpcSender<DOMMessage>>>,
skip_waiting: Cell<bool>
}

Expand All @@ -35,10 +39,10 @@ impl ServiceWorker {
skip_waiting: bool) -> ServiceWorker {
ServiceWorker {
eventtarget: EventTarget::new_inherited(),
sender: None,
script_url: DOMRefCell::new(String::from(script_url)),
state: Cell::new(ServiceWorkerState::Installing),
skip_waiting: Cell::new(skip_waiting)
skip_waiting: Cell::new(skip_waiting),
msg_sender: DOMRefCell::new(None)
}
}

Expand Down Expand Up @@ -69,6 +73,13 @@ impl ServiceWorker {
script_url.as_str(),
skip_waiting)
}

pub fn store_sender(trusted_worker: TrustedServiceWorkerAddress, sender: IpcSender<DOMMessage>) {
let worker = trusted_worker.root();
// This channel is used for sending message from the ServiceWorker object to its
// corresponding ServiceWorkerGlobalScope
*worker.msg_sender.borrow_mut() = Some(sender);
}
}

impl ServiceWorkerMethods for ServiceWorker {
Expand All @@ -82,6 +93,18 @@ 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 {
let data = try!(StructuredCloneData::write(cx, message));
let msg_vec = DOMMessage(data.move_to_arraybuffer());
if let Some(ref sender) = *self.msg_sender.borrow() {
let _ = sender.send(msg_vec);
} else {
warn!("Could not communicate message to ServiceWorkerGlobalScope");
}
Ok(())
}

// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container-onerror-attribute
event_handler!(error, GetOnerror, SetOnerror);

Expand Down
84 changes: 61 additions & 23 deletions components/script/dom/serviceworkerglobalscope.rs
Expand Up @@ -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;
Expand All @@ -14,9 +13,9 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, RootCollection};
use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString;
use dom::bindings::structuredclone::StructuredCloneData;
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;
Expand All @@ -26,8 +25,8 @@ 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_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg};
use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx, ScriptChan};
use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg, DOMMessage};
use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
use std::thread;
use std::time::Duration;
Expand All @@ -49,6 +48,27 @@ pub enum MixedMessage {
FromServiceWorker(ServiceWorkerScriptMsg),
FromDevtools(DevtoolScriptControlMsg),
FromTimeoutThread(()),
PostMessage(DOMMessage)
}

// Required for run_with_memory_reporting
#[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]
Expand All @@ -61,12 +81,12 @@ 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
msg_port: Receiver<DOMMessage>,
#[ignore_heap_size_of = "Defined in std"]
scope_url: Url,
}

impl ServiceWorkerGlobalScope {
Expand All @@ -80,7 +100,8 @@ impl ServiceWorkerGlobalScope {
timer_event_chan: IpcSender<TimerEvent>,
timer_event_port: Receiver<()>,
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url)
scope_url: Url,
msg_port: Receiver<DOMMessage>)
-> ServiceWorkerGlobalScope {
ServiceWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(init,
Expand All @@ -93,9 +114,9 @@ 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
scope_url: scope_url,
msg_port: msg_port
}
}

Expand All @@ -109,7 +130,8 @@ impl ServiceWorkerGlobalScope {
timer_event_chan: IpcSender<TimerEvent>,
timer_event_port: Receiver<()>,
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url)
scope_url: Url,
msg_port: Receiver<DOMMessage>)
-> Root<ServiceWorkerGlobalScope> {
let cx = runtime.cx();
let scope = box ServiceWorkerGlobalScope::new_inherited(init,
Expand All @@ -122,7 +144,8 @@ impl ServiceWorkerGlobalScope {
timer_event_chan,
timer_event_port,
swmanager_sender,
scope_url);
scope_url,
msg_port);
ServiceWorkerGlobalScopeBinding::Wrap(cx, scope)
}

Expand All @@ -132,7 +155,8 @@ impl ServiceWorkerGlobalScope {
receiver: Receiver<ServiceWorkerScriptMsg>,
devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>,
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url) {
scope_url: Url,
msg_port: Receiver<DOMMessage>) {
let ScopeThings { script_url,
pipeline_id,
init,
Expand Down Expand Up @@ -167,7 +191,7 @@ impl ServiceWorkerGlobalScope {
let global = ServiceWorkerGlobalScope::new(
init, url, pipeline_id, devtools_mpsc_port, runtime,
own_sender, receiver,
timer_ipc_chan, timer_port, swmanager_sender, scope_url);
timer_ipc_chan, timer_port, swmanager_sender, scope_url, msg_port);
let scope = global.upcast::<WorkerGlobalScope>();

unsafe {
Expand Down Expand Up @@ -214,6 +238,17 @@ impl ServiceWorkerGlobalScope {
self.handle_script_event(msg);
true
}
MixedMessage::PostMessage(data) => {
let DOMMessage(mut data) = data;
let data = StructuredCloneData::make_structured_clone(data.as_mut_ptr(), data.len());
let scope = self.upcast::<WorkerGlobalScope>();
let target = self.upcast();
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());
true
}
MixedMessage::FromTimeoutThread(_) => {
let _ = self.swmanager_sender.send(ServiceWorkerMsg::Timeout(self.scope_url.clone()));
false
Expand All @@ -225,15 +260,7 @@ impl ServiceWorkerGlobalScope {
use self::ServiceWorkerScriptMsg::*;

match msg {
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());
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());
},
CommonWorker(WorkerScriptMsg::DOMMessage(_)) => { },
CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable))) => {
runnable.handler()
},
Expand All @@ -260,17 +287,20 @@ impl ServiceWorkerGlobalScope {
let worker_port = &self.receiver;
let devtools_port = scope.from_devtools_receiver();
let timer_event_port = &self.timer_event_port;
let msg_port = &self.msg_port;

let sel = Select::new();
let mut worker_handle = sel.handle(worker_port);
let mut devtools_handle = sel.handle(devtools_port);
let mut timer_port_handle = sel.handle(timer_event_port);
let mut msg_port_handle = sel.handle(msg_port);
unsafe {
worker_handle.add();
if scope.from_devtools_sender().is_some() {
devtools_handle.add();
}
timer_port_handle.add();
msg_port_handle.add();
}

let ret = sel.wait();
Expand All @@ -280,6 +310,8 @@ impl ServiceWorkerGlobalScope {
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
} else if ret == timer_port_handle.id() {
Ok(MixedMessage::FromTimeoutThread(try!(timer_event_port.recv())))
} else if ret == msg_port_handle.id() {
Ok(MixedMessage::PostMessage(try!(msg_port.recv())))
} else {
panic!("unexpected select result!")
}
Expand All @@ -292,6 +324,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)]
Expand Down
7 changes: 6 additions & 1 deletion components/script/dom/serviceworkerregistration.rs
Expand Up @@ -6,10 +6,11 @@ use dom::bindings::codegen::Bindings::ServiceWorkerBinding::ServiceWorkerState;
use dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::{ServiceWorkerRegistrationMethods, Wrap};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::USVString;
use dom::eventtarget::EventTarget;
use dom::serviceworker::ServiceWorker;
use dom::serviceworker::{ServiceWorker, TrustedServiceWorkerAddress};
use dom::serviceworkercontainer::Controllable;
use dom::workerglobalscope::prepare_workerscope_init;
use script_traits::{WorkerScriptLoadOrigin, ScopeThings};
Expand Down Expand Up @@ -49,6 +50,10 @@ impl ServiceWorkerRegistration {
self.active.as_ref().unwrap()
}

pub fn get_trusted_worker(&self) -> TrustedServiceWorkerAddress {
Trusted::new(self.active.as_ref().unwrap())
}

pub fn create_scope_things(global: GlobalRef, script_url: Url) -> ScopeThings {
let worker_load_origin = WorkerScriptLoadOrigin {
referrer_url: None,
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/webidls/ServiceWorker.webidl
Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions components/script/dom/workerglobalscope.rs
Expand Up @@ -392,10 +392,12 @@ 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")
}
Expand Down

0 comments on commit 0996b38

Please sign in to comment.