store senders instead of buffering messages

This commit is contained in:
Rahul Sharma 2016-08-24 23:22:02 +05:30
parent 0996b38ade
commit 9dcb7348a2
17 changed files with 193 additions and 188 deletions

View file

@ -738,12 +738,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// store service worker manager for communicating with it. // store service worker manager for communicating with it.
self.swmanager_chan = Some(sw_sender); 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);
}
}
} }
} }
@ -1006,6 +1000,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
debug!("constellation got store registration scope message"); debug!("constellation got store registration scope message");
self.handle_register_serviceworker(scope_things, scope); 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");
}
}
} }
} }

View file

@ -11,6 +11,7 @@ use js::jsapi::{HandleValue, MutableHandleValue};
use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_STRUCTURED_CLONE_VERSION}; use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_STRUCTURED_CLONE_VERSION};
use js::jsapi::{JS_ClearPendingException, JS_WriteStructuredClone}; use js::jsapi::{JS_ClearPendingException, JS_WriteStructuredClone};
use libc::size_t; use libc::size_t;
use script_traits::DOMMessage;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
@ -46,15 +47,18 @@ impl StructuredCloneData {
}) })
} }
/// Converts a StructuredCloneData to Vec<u64> /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
pub fn move_to_arraybuffer(self) -> Vec<u64> { pub fn move_to_arraybuffer(self) -> DOMMessage {
unsafe { unsafe {
slice::from_raw_parts(self.data, self.nbytes).to_vec() DOMMessage(slice::from_raw_parts(self.data as *mut u8, self.nbytes).to_vec())
} }
} }
/// Converts back to StructuredCloneData using a pointer and no of bytes /// Converts back to StructuredCloneData
pub fn make_structured_clone(data: *mut u64, nbytes: size_t) -> StructuredCloneData { pub fn make_structured_clone(data: DOMMessage) -> StructuredCloneData {
let DOMMessage(mut data) = data;
let nbytes = data.len();
let data = data.as_mut_ptr() as *mut u64;
StructuredCloneData { StructuredCloneData {
data: data, data: data,
nbytes: nbytes nbytes: nbytes

View file

@ -6,7 +6,7 @@ use dom::abstractworker::SimpleWorkerErrorHandler;
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap}; use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap};
use dom::bindings::error::ErrorResult; use dom::bindings::error::{ErrorResult, Error};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root; use dom::bindings::js::Root;
@ -15,10 +15,9 @@ use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::USVString; use dom::bindings::str::USVString;
use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::structuredclone::StructuredCloneData;
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{HandleValue, JSContext}; use js::jsapi::{HandleValue, JSContext};
use script_thread::Runnable; use script_thread::Runnable;
use script_traits::DOMMessage; use script_traits::ScriptMsg;
use std::cell::Cell; use std::cell::Cell;
use url::Url; use url::Url;
@ -28,28 +27,29 @@ pub type TrustedServiceWorkerAddress = Trusted<ServiceWorker>;
pub struct ServiceWorker { pub struct ServiceWorker {
eventtarget: EventTarget, eventtarget: EventTarget,
script_url: DOMRefCell<String>, script_url: DOMRefCell<String>,
scope_url: DOMRefCell<String>,
state: Cell<ServiceWorkerState>, state: Cell<ServiceWorkerState>,
#[ignore_heap_size_of = "Defined in std"]
msg_sender: DOMRefCell<Option<IpcSender<DOMMessage>>>,
skip_waiting: Cell<bool> skip_waiting: Cell<bool>
} }
impl ServiceWorker { impl ServiceWorker {
fn new_inherited(script_url: &str, fn new_inherited(script_url: &str,
skip_waiting: bool) -> ServiceWorker { skip_waiting: bool,
scope_url: &str) -> ServiceWorker {
ServiceWorker { ServiceWorker {
eventtarget: EventTarget::new_inherited(), eventtarget: EventTarget::new_inherited(),
script_url: DOMRefCell::new(String::from(script_url)), script_url: DOMRefCell::new(String::from(script_url)),
state: Cell::new(ServiceWorkerState::Installing), state: Cell::new(ServiceWorkerState::Installing),
skip_waiting: Cell::new(skip_waiting), scope_url: DOMRefCell::new(String::from(scope_url)),
msg_sender: DOMRefCell::new(None) skip_waiting: Cell::new(skip_waiting)
} }
} }
pub fn new(global: GlobalRef, pub fn new(global: GlobalRef,
script_url: &str, script_url: &str,
scope_url: &str,
skip_waiting: bool) -> Root<ServiceWorker> { 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, skip_waiting, scope_url), global, Wrap)
} }
pub fn dispatch_simple_error(address: TrustedServiceWorkerAddress) { pub fn dispatch_simple_error(address: TrustedServiceWorkerAddress) {
@ -68,18 +68,13 @@ impl ServiceWorker {
pub fn install_serviceworker(global: GlobalRef, pub fn install_serviceworker(global: GlobalRef,
script_url: Url, script_url: Url,
scope_url: &str,
skip_waiting: bool) -> Root<ServiceWorker> { skip_waiting: bool) -> Root<ServiceWorker> {
ServiceWorker::new(global, ServiceWorker::new(global,
script_url.as_str(), script_url.as_str(),
scope_url,
skip_waiting) 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 { impl ServiceWorkerMethods for ServiceWorker {
@ -95,13 +90,15 @@ impl ServiceWorkerMethods for ServiceWorker {
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-postmessage // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-postmessage
fn PostMessage(&self, cx: *mut JSContext, message: HandleValue) -> ErrorResult { fn PostMessage(&self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
let data = try!(StructuredCloneData::write(cx, message)); // Step 1
let msg_vec = DOMMessage(data.move_to_arraybuffer()); if let ServiceWorkerState::Redundant = self.state.get() {
if let Some(ref sender) = *self.msg_sender.borrow() { return Err(Error::InvalidState);
let _ = sender.send(msg_vec);
} else {
warn!("Could not communicate message to ServiceWorkerGlobalScope");
} }
// Step 7
let data = try!(StructuredCloneData::write(cx, message));
let msg_vec = data.move_to_arraybuffer();
let scope_url = Url::parse(&*self.scope_url.borrow()).unwrap();
let _ = self.global().r().constellation_chan().send(ScriptMsg::ForwardDOMMessage(msg_vec, scope_url));
Ok(()) Ok(())
} }

View file

@ -13,7 +13,6 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, RootCollection}; use dom::bindings::js::{Root, RootCollection};
use dom::bindings::reflector::Reflectable; use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
use dom::messageevent::MessageEvent; use dom::messageevent::MessageEvent;
use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::WorkerGlobalScope;
@ -26,12 +25,11 @@ use msg::constellation_msg::PipelineId;
use net_traits::{LoadContext, load_whole_resource, IpcSend, CustomResponseMediator}; use net_traits::{LoadContext, load_whole_resource, IpcSend, CustomResponseMediator};
use rand::random; use rand::random;
use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx, ScriptChan}; use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx, ScriptChan};
use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg, DOMMessage}; use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg};
use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use style::thread_state; use style::thread_state::{self, IN_WORKER, SCRIPT};
use style::thread_state::{IN_WORKER, SCRIPT};
use url::Url; use url::Url;
use util::prefs::PREFS; use util::prefs::PREFS;
use util::thread::spawn_named; use util::thread::spawn_named;
@ -47,11 +45,9 @@ pub enum ServiceWorkerScriptMsg {
pub enum MixedMessage { pub enum MixedMessage {
FromServiceWorker(ServiceWorkerScriptMsg), FromServiceWorker(ServiceWorkerScriptMsg),
FromDevtools(DevtoolScriptControlMsg), FromDevtools(DevtoolScriptControlMsg),
FromTimeoutThread(()), FromTimeoutThread(())
PostMessage(DOMMessage)
} }
// Required for run_with_memory_reporting
#[derive(JSTraceable, Clone)] #[derive(JSTraceable, Clone)]
pub struct ServiceWorkerChan { pub struct ServiceWorkerChan {
pub sender: Sender<ServiceWorkerScriptMsg> pub sender: Sender<ServiceWorkerScriptMsg>
@ -83,9 +79,6 @@ pub struct ServiceWorkerGlobalScope {
timer_event_port: Receiver<()>, timer_event_port: Receiver<()>,
#[ignore_heap_size_of = "Defined in std"] #[ignore_heap_size_of = "Defined in std"]
swmanager_sender: IpcSender<ServiceWorkerMsg>, swmanager_sender: IpcSender<ServiceWorkerMsg>,
#[ignore_heap_size_of = "Defined in std"]
msg_port: Receiver<DOMMessage>,
#[ignore_heap_size_of = "Defined in std"]
scope_url: Url, scope_url: Url,
} }
@ -100,8 +93,7 @@ impl ServiceWorkerGlobalScope {
timer_event_chan: IpcSender<TimerEvent>, timer_event_chan: IpcSender<TimerEvent>,
timer_event_port: Receiver<()>, timer_event_port: Receiver<()>,
swmanager_sender: IpcSender<ServiceWorkerMsg>, swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url, scope_url: Url)
msg_port: Receiver<DOMMessage>)
-> ServiceWorkerGlobalScope { -> ServiceWorkerGlobalScope {
ServiceWorkerGlobalScope { ServiceWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(init, workerglobalscope: WorkerGlobalScope::new_inherited(init,
@ -115,8 +107,7 @@ impl ServiceWorkerGlobalScope {
timer_event_port: timer_event_port, timer_event_port: timer_event_port,
own_sender: own_sender, own_sender: own_sender,
swmanager_sender: swmanager_sender, swmanager_sender: swmanager_sender,
scope_url: scope_url, scope_url: scope_url
msg_port: msg_port
} }
} }
@ -130,8 +121,7 @@ impl ServiceWorkerGlobalScope {
timer_event_chan: IpcSender<TimerEvent>, timer_event_chan: IpcSender<TimerEvent>,
timer_event_port: Receiver<()>, timer_event_port: Receiver<()>,
swmanager_sender: IpcSender<ServiceWorkerMsg>, swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url, scope_url: Url)
msg_port: Receiver<DOMMessage>)
-> Root<ServiceWorkerGlobalScope> { -> Root<ServiceWorkerGlobalScope> {
let cx = runtime.cx(); let cx = runtime.cx();
let scope = box ServiceWorkerGlobalScope::new_inherited(init, let scope = box ServiceWorkerGlobalScope::new_inherited(init,
@ -144,8 +134,7 @@ impl ServiceWorkerGlobalScope {
timer_event_chan, timer_event_chan,
timer_event_port, timer_event_port,
swmanager_sender, swmanager_sender,
scope_url, scope_url);
msg_port);
ServiceWorkerGlobalScopeBinding::Wrap(cx, scope) ServiceWorkerGlobalScopeBinding::Wrap(cx, scope)
} }
@ -155,8 +144,7 @@ impl ServiceWorkerGlobalScope {
receiver: Receiver<ServiceWorkerScriptMsg>, receiver: Receiver<ServiceWorkerScriptMsg>,
devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>,
swmanager_sender: IpcSender<ServiceWorkerMsg>, swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: Url, scope_url: Url) {
msg_port: Receiver<DOMMessage>) {
let ScopeThings { script_url, let ScopeThings { script_url,
pipeline_id, pipeline_id,
init, init,
@ -191,7 +179,7 @@ impl ServiceWorkerGlobalScope {
let global = ServiceWorkerGlobalScope::new( let global = ServiceWorkerGlobalScope::new(
init, url, pipeline_id, devtools_mpsc_port, runtime, init, url, pipeline_id, devtools_mpsc_port, runtime,
own_sender, receiver, own_sender, receiver,
timer_ipc_chan, timer_port, swmanager_sender, scope_url, msg_port); timer_ipc_chan, timer_port, swmanager_sender, scope_url);
let scope = global.upcast::<WorkerGlobalScope>(); let scope = global.upcast::<WorkerGlobalScope>();
unsafe { unsafe {
@ -238,17 +226,6 @@ impl ServiceWorkerGlobalScope {
self.handle_script_event(msg); self.handle_script_event(msg);
true 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(_) => { MixedMessage::FromTimeoutThread(_) => {
let _ = self.swmanager_sender.send(ServiceWorkerMsg::Timeout(self.scope_url.clone())); let _ = self.swmanager_sender.send(ServiceWorkerMsg::Timeout(self.scope_url.clone()));
false false
@ -260,7 +237,14 @@ impl ServiceWorkerGlobalScope {
use self::ServiceWorkerScriptMsg::*; use self::ServiceWorkerScriptMsg::*;
match msg { match msg {
CommonWorker(WorkerScriptMsg::DOMMessage(_)) => { }, 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::Common(CommonScriptMsg::RunnableMsg(_, runnable))) => { CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable))) => {
runnable.handler() runnable.handler()
}, },
@ -287,20 +271,17 @@ impl ServiceWorkerGlobalScope {
let worker_port = &self.receiver; let worker_port = &self.receiver;
let devtools_port = scope.from_devtools_receiver(); let devtools_port = scope.from_devtools_receiver();
let timer_event_port = &self.timer_event_port; let timer_event_port = &self.timer_event_port;
let msg_port = &self.msg_port;
let sel = Select::new(); let sel = Select::new();
let mut worker_handle = sel.handle(worker_port); let mut worker_handle = sel.handle(worker_port);
let mut devtools_handle = sel.handle(devtools_port); let mut devtools_handle = sel.handle(devtools_port);
let mut timer_port_handle = sel.handle(timer_event_port); let mut timer_port_handle = sel.handle(timer_event_port);
let mut msg_port_handle = sel.handle(msg_port);
unsafe { unsafe {
worker_handle.add(); worker_handle.add();
if scope.from_devtools_sender().is_some() { if scope.from_devtools_sender().is_some() {
devtools_handle.add(); devtools_handle.add();
} }
timer_port_handle.add(); timer_port_handle.add();
msg_port_handle.add();
} }
let ret = sel.wait(); let ret = sel.wait();
@ -310,8 +291,6 @@ impl ServiceWorkerGlobalScope {
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
} else if ret == timer_port_handle.id() { } else if ret == timer_port_handle.id() {
Ok(MixedMessage::FromTimeoutThread(try!(timer_event_port.recv()))) Ok(MixedMessage::FromTimeoutThread(try!(timer_event_port.recv())))
} else if ret == msg_port_handle.id() {
Ok(MixedMessage::PostMessage(try!(msg_port.recv())))
} else { } else {
panic!("unexpected select result!") panic!("unexpected select result!")
} }

View file

@ -6,11 +6,10 @@ use dom::bindings::codegen::Bindings::ServiceWorkerBinding::ServiceWorkerState;
use dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::{ServiceWorkerRegistrationMethods, Wrap}; use dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::{ServiceWorkerRegistrationMethods, Wrap};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root}; use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::USVString; use dom::bindings::str::USVString;
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
use dom::serviceworker::{ServiceWorker, TrustedServiceWorkerAddress}; use dom::serviceworker::ServiceWorker;
use dom::serviceworkercontainer::Controllable; use dom::serviceworkercontainer::Controllable;
use dom::workerglobalscope::prepare_workerscope_init; use dom::workerglobalscope::prepare_workerscope_init;
use script_traits::{WorkerScriptLoadOrigin, ScopeThings}; use script_traits::{WorkerScriptLoadOrigin, ScopeThings};
@ -40,7 +39,7 @@ impl ServiceWorkerRegistration {
script_url: Url, script_url: Url,
scope: String, scope: String,
container: &Controllable) -> Root<ServiceWorkerRegistration> { 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, true);
active_worker.set_transition_state(ServiceWorkerState::Installed); active_worker.set_transition_state(ServiceWorkerState::Installed);
container.set_controller(&*active_worker.clone()); container.set_controller(&*active_worker.clone());
reflect_dom_object(box ServiceWorkerRegistration::new_inherited(&*active_worker, scope), global, Wrap) reflect_dom_object(box ServiceWorkerRegistration::new_inherited(&*active_worker, scope), global, Wrap)
@ -50,10 +49,6 @@ impl ServiceWorkerRegistration {
self.active.as_ref().unwrap() 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 { pub fn create_scope_things(global: GlobalRef, script_url: Url) -> ScopeThings {
let worker_load_origin = WorkerScriptLoadOrigin { let worker_load_origin = WorkerScriptLoadOrigin {
referrer_url: None, referrer_url: None,

View file

@ -399,7 +399,7 @@ impl WorkerGlobalScope {
} else if let Some(service_worker) = service_worker { } else if let Some(service_worker) = service_worker {
return service_worker.script_chan(); return service_worker.script_chan();
} else { } else {
panic!("need to implement a sender for SharedWorker/ServiceWorker") panic!("need to implement a sender for SharedWorker")
} }
} }

View file

@ -42,7 +42,7 @@ use dom::element::Element;
use dom::event::{Event, EventBubbles, EventCancelable}; use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlanchorelement::HTMLAnchorElement;
use dom::node::{Node, NodeDamage, window_from_node}; use dom::node::{Node, NodeDamage, window_from_node};
use dom::serviceworker::{TrustedServiceWorkerAddress, ServiceWorker}; use dom::serviceworker::TrustedServiceWorkerAddress;
use dom::serviceworkerregistration::ServiceWorkerRegistration; use dom::serviceworkerregistration::ServiceWorkerRegistration;
use dom::servohtmlparser::ParserContext; use dom::servohtmlparser::ParserContext;
use dom::uievent::UIEvent; use dom::uievent::UIEvent;
@ -84,7 +84,7 @@ use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
use script_traits::webdriver_msg::WebDriverScriptCommand; use script_traits::webdriver_msg::WebDriverScriptCommand;
use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult}; use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult};
use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent};
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg, DOMMessage}; use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource}; use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource};
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData}; use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData};
use std::borrow::ToOwned; use std::borrow::ToOwned;
@ -924,8 +924,6 @@ impl ScriptThread {
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg), self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
ConstellationControlMsg::Reload(pipeline_id) => ConstellationControlMsg::Reload(pipeline_id) =>
self.handle_reload(pipeline_id), self.handle_reload(pipeline_id),
ConstellationControlMsg::ConnectServiceWorker(scope_url, chan) =>
self.connect_serviceworker_to_scope(scope_url, chan),
msg @ ConstellationControlMsg::AttachLayout(..) | msg @ ConstellationControlMsg::AttachLayout(..) |
msg @ ConstellationControlMsg::Viewport(..) | msg @ ConstellationControlMsg::Viewport(..) |
msg @ ConstellationControlMsg::SetScrollState(..) | msg @ ConstellationControlMsg::SetScrollState(..) |
@ -1462,15 +1460,6 @@ impl ScriptThread {
} }
} }
// Set the sender to the corresponding scope of the service worker object.
fn connect_serviceworker_to_scope(&self, scope_url: Url, chan: IpcSender<DOMMessage>) {
let ref maybe_registration_ref = *self.registration_map.borrow();
if let Some(ref registration) = maybe_registration_ref.get(&scope_url) {
let trusted_worker = registration.get_trusted_worker();
ServiceWorker::store_sender(trusted_worker, chan);
}
}
/// Handles a request for the window title. /// Handles a request for the window title.
fn handle_get_title_msg(&self, pipeline_id: PipelineId) { fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
let document = match self.root_browsing_context().find(pipeline_id) { let document = match self.root_browsing_context().find(pipeline_id) {

View file

@ -8,12 +8,14 @@
//! active_workers map //! active_workers map
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use dom::abstractworker::WorkerScriptMsg;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg}; use dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg};
use dom::serviceworkerregistration::longest_prefix_match; use dom::serviceworkerregistration::longest_prefix_match;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use net_traits::{CustomResponseMediator, CoreResourceMsg}; 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::collections::HashMap;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError}; use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use url::Url; use url::Url;
@ -29,29 +31,25 @@ pub struct ServiceWorkerManager {
// map of registered service worker descriptors // map of registered service worker descriptors
registered_workers: HashMap<Url, ScopeThings>, registered_workers: HashMap<Url, ScopeThings>,
// map of active service worker descriptors // 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 to send messages here
own_sender: IpcSender<ServiceWorkerMsg>, own_sender: IpcSender<ServiceWorkerMsg>,
// receiver to receive messages from constellation // receiver to receive messages from constellation
own_port: Receiver<ServiceWorkerMsg>, own_port: Receiver<ServiceWorkerMsg>,
// to receive resource messages // to receive resource messages
resource_receiver: Receiver<CustomResponseMediator>, resource_receiver: Receiver<CustomResponseMediator>
// to send message to constellation
constellation_sender: IpcSender<SWManagerMsg>
} }
impl ServiceWorkerManager { impl ServiceWorkerManager {
fn new(own_sender: IpcSender<ServiceWorkerMsg>, fn new(own_sender: IpcSender<ServiceWorkerMsg>,
from_constellation_receiver: Receiver<ServiceWorkerMsg>, from_constellation_receiver: Receiver<ServiceWorkerMsg>,
resource_port: Receiver<CustomResponseMediator>, resource_port: Receiver<CustomResponseMediator>) -> ServiceWorkerManager {
constellation_sender: IpcSender<SWManagerMsg>) -> ServiceWorkerManager {
ServiceWorkerManager { ServiceWorkerManager {
registered_workers: HashMap::new(), registered_workers: HashMap::new(),
active_workers: HashMap::new(), active_workers: HashMap::new(),
own_sender: own_sender, own_sender: own_sender,
own_port: from_constellation_receiver, own_port: from_constellation_receiver,
resource_receiver: resource_port, resource_receiver: resource_port
constellation_sender: constellation_sender
} }
} }
@ -62,69 +60,53 @@ impl ServiceWorkerManager {
let resource_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(resource_port); let resource_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(resource_port);
let _ = sw_senders.resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan)); let _ = sw_senders.resource_sender.send(CoreResourceMsg::NetworkMediator(resource_chan));
let _ = sw_senders.swmanager_sender.send(SWManagerMsg::OwnSender(own_sender.clone())); let _ = sw_senders.swmanager_sender.send(SWManagerMsg::OwnSender(own_sender.clone()));
let constellation_sender = sw_senders.swmanager_sender.clone();
spawn_named("ServiceWorkerManager".to_owned(), move || { spawn_named("ServiceWorkerManager".to_owned(), move || {
ServiceWorkerManager::new(own_sender, ServiceWorkerManager::new(own_sender,
from_constellation, from_constellation,
resource_port, resource_port).handle_message();
constellation_sender).handle_message();
}); });
} }
pub fn prepare_activation(&mut self, load_url: &Url) -> Option<Sender<ServiceWorkerScriptMsg>> { pub fn get_matching_scope(&self, load_url: &Url) -> Option<Url> {
let mut scope_url = None;
for scope in self.registered_workers.keys() { for scope in self.registered_workers.keys() {
if longest_prefix_match(&scope, load_url) { if longest_prefix_match(&scope, load_url) {
scope_url = Some(scope.clone()); return Some(scope.clone());
break;
}
}
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));
};
let (msg_chan, msg_port) = ipc::channel().unwrap();
let msg_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(msg_port);
ServiceWorkerGlobalScope::run_serviceworker_scope(scope_things.clone(),
sender.clone(),
receiver,
devtools_receiver,
self.own_sender.clone(),
scope_url.clone(),
msg_port);
// Send the message to constellation which then talks to the script thread for storing this msg_chan
let connection_msg = SWManagerMsg::ConnectServiceWorker(scope_url.clone(),
scope_things.pipeline_id,
msg_chan);
let _ = self.constellation_sender.send(connection_msg);
// We store the activated worker
self.active_workers.insert(scope_url, scope_things.clone());
return Some(sender);
} else {
warn!("Unable to activate service worker");
} }
} }
None None
} }
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(),
};
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
}
}
fn handle_message(&mut self) { fn handle_message(&mut self) {
while let Ok(message) = self.receive_message() { while let Ok(message) = self.receive_message() {
let should_continue = match message { let should_continue = match message {
@ -141,6 +123,12 @@ impl ServiceWorkerManager {
} }
} }
#[inline(always)]
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
let data = StructuredCloneData::make_structured_clone(msg);
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::DOMMessage(data)));
}
fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool { fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool {
match msg { match msg {
ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope) => { ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope) => {
@ -155,7 +143,19 @@ impl ServiceWorkerManager {
if self.active_workers.contains_key(&scope) { if self.active_workers.contains_key(&scope) {
let _ = self.active_workers.remove(&scope); let _ = self.active_workers.remove(&scope);
} else { } 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 true
} }
@ -163,12 +163,20 @@ impl ServiceWorkerManager {
} }
} }
#[inline]
fn handle_message_from_resource(&mut self, mediator: CustomResponseMediator) -> bool { fn handle_message_from_resource(&mut self, mediator: CustomResponseMediator) -> bool {
if serviceworker_enabled() { if serviceworker_enabled() {
let worker_sender = self.prepare_activation(&mediator.load_url); if let Some(scope) = self.get_matching_scope(&mediator.load_url) {
if let Some(ref sender) = worker_sender { if self.active_workers.contains_key(&scope) {
let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator)); 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 { } else {
let _ = mediator.response_chan.send(None); let _ = mediator.response_chan.send(None);

View file

@ -207,9 +207,7 @@ pub enum ConstellationControlMsg {
/// Report an error from a CSS parser for the given pipeline /// Report an error from a CSS parser for the given pipeline
ReportCSSError(PipelineId, String, usize, usize, String), ReportCSSError(PipelineId, String, usize, usize, String),
/// Reload the given page. /// Reload the given page.
Reload(PipelineId), Reload(PipelineId)
/// Requests the script thread to connect service worker object to its scope
ConnectServiceWorker(Url, IpcSender<DOMMessage>)
} }
impl fmt::Debug for ConstellationControlMsg { impl fmt::Debug for ConstellationControlMsg {
@ -238,8 +236,7 @@ impl fmt::Debug for ConstellationControlMsg {
DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent", DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent",
FramedContentChanged(..) => "FramedContentChanged", FramedContentChanged(..) => "FramedContentChanged",
ReportCSSError(..) => "ReportCSSError", ReportCSSError(..) => "ReportCSSError",
Reload(..) => "Reload", Reload(..) => "Reload"
ConnectServiceWorker(..) => "ConnectServiceWorker"
}) })
} }
} }

View file

@ -135,6 +135,9 @@ pub enum ScriptMsg {
LogEntry(Option<PipelineId>, Option<String>, LogEntry), LogEntry(Option<PipelineId>, Option<String>, LogEntry),
/// Notifies the constellation that this pipeline has exited. /// Notifies the constellation that this pipeline has exited.
PipelineExited(PipelineId), 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 /// Store the data required to activate a service worker for the given scope
RegisterServiceWorker(ScopeThings, Url), RegisterServiceWorker(ScopeThings, Url),
/// Requests that the compositor shut down. /// Requests that the compositor shut down.
@ -159,8 +162,8 @@ pub struct ScopeThings {
} }
/// Message that gets passed to service worker scope on postMessage /// Message that gets passed to service worker scope on postMessage
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Clone)]
pub struct DOMMessage(pub Vec<u64>); pub struct DOMMessage(pub Vec<u8>);
/// Channels to allow service worker manager to communicate with constellation and resource thread /// Channels to allow service worker manager to communicate with constellation and resource thread
pub struct SWManagerSenders { pub struct SWManagerSenders {
@ -177,6 +180,8 @@ pub enum ServiceWorkerMsg {
RegisterServiceWorker(ScopeThings, Url), RegisterServiceWorker(ScopeThings, Url),
/// Timeout message sent by active service workers /// Timeout message sent by active service workers
Timeout(Url), Timeout(Url),
/// Backup message
ForwardDOMMessage(DOMMessage, Url),
/// Exit the service worker manager /// Exit the service worker manager
Exit, Exit,
} }
@ -185,8 +190,6 @@ pub enum ServiceWorkerMsg {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum SWManagerMsg { pub enum SWManagerMsg {
/// Provide the constellation with a means of communicating with the Service Worker Manager /// Provide the constellation with a means of communicating with the Service Worker Manager
OwnSender(IpcSender<ServiceWorkerMsg>), OwnSender(IpcSender<ServiceWorkerMsg>)
/// Message to ask to get a Trusted<ServiceWorker> to constellation
ConnectServiceWorker(Url, PipelineId, IpcSender<DOMMessage>)
} }

View file

@ -8,6 +8,5 @@
<div> <div>
<a href="http://github.com/servo/servo">Servo Project</a> <a href="http://github.com/servo/servo">Servo Project</a>
</div> </div>
</body> </body>
</html> </html>

View file

@ -0,0 +1,3 @@
function test_importScripts() {
console.log('Hi from dummy.js');
}

View file

@ -1,11 +1,11 @@
self.addEventListener('activate', function(e) { self.addEventListener('activate', function(e) {
console.log("iframe service worker active"); console.log("iframe service worker active");
}); });
self.addEventListener('fetch', function(e) { self.addEventListener('fetch', function(e) {
console.log("A fetch event detected by /iframe service worker"); console.log("A fetch event detected by /iframe service worker");
}); });
self.addEventListener('message', function(e) { self.addEventListener('message', function(e) {
console.log('Post message payload ' + e.data.msg); console.log(e.data.payload.msg + ' from '+ e.data.payload.worker_id);
}) })

View file

@ -16,43 +16,56 @@
<button onclick="register_zero()">Register for Root (/)</button> <button onclick="register_zero()">Register for Root (/)</button>
<button onclick="register_one()">Register for Dashboard (/dashboard)</button> <button onclick="register_one()">Register for Dashboard (/dashboard)</button>
<button onclick="register_two()">Register for Profile (/profile)</button> <button onclick="register_two()">Register for Profile (/profile)</button>
<button onclick="post_message_root()">Msg to Root service worker</button>
</div> </div>
<script> <script>
var reg = navigator.serviceWorker.register('iframe_sw.js', { 'scope': '/demo_iframe.html' }); var reg = navigator.serviceWorker.register('iframe_sw.js', { 'scope': '/demo_iframe.html' });
console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("From client worker registered: ", navigator.serviceWorker.controller);
console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL);
document.getElementById('iframe_sw').src = 'demo_iframe.html'; document.getElementById('iframe_sw').src = 'demo_iframe.html';
var payload = { msg: 'Hi from /iframe service worker' }; post_message('/demo_iframe');
// The delay is due to the sender being set in the service worker object after the scope starts.
// This won't be needed when service workers are wrapped with Promises.
setTimeout(function() {
navigator.serviceWorker.controller.postMessage(payload);
}, 1000);
function register_zero() { 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("From client worker registered: ", navigator.serviceWorker.controller);
console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); 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() { function register_one() {
var reg = navigator.serviceWorker.register('sw_dashboard.js', { 'scope': '/dashboard' }); var reg = navigator.serviceWorker.register('sw_dashboard.js', { 'scope': '/dashboard' });
console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("From client worker registered: ", navigator.serviceWorker.controller);
console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL);
post_message('/dashboard');
} }
function register_two() { function register_two() {
var reg = navigator.serviceWorker.register('sw_profile.js', { 'scope': '/profile' }); var reg = navigator.serviceWorker.register('sw_profile.js', { 'scope': '/profile' });
console.log("From client worker registered: ", navigator.serviceWorker.controller); console.log("From client worker registered: ", navigator.serviceWorker.controller);
console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL); console.log("Registered script source: "+ navigator.serviceWorker.controller.scriptURL);
post_message('/profile');
} }
navigator.serviceWorker.addEventListener('controllerchange', function(e) { 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> </script>
</body> </body>
</html> </html>

View file

@ -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);
})

View file

@ -1,9 +1,11 @@
importScripts('dashboard_helper.js');
self.addEventListener('activate', function(e) { self.addEventListener('activate', function(e) {
status_from_dashboard(); console.log('Dashboard service worker active');
}); });
self.addEventListener('fetch', function(e) { self.addEventListener('fetch', function(e) {
console.log("A fetch event detected by /dashboard service worker"); 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);
})

View file

@ -1,8 +1,12 @@
self.addEventListener('activate', function(e) { self.addEventListener('activate', function(e) {
console.log("profile service worker active"); console.log("profile service worker active");
}); });
self.addEventListener('fetch', function(e) { self.addEventListener('fetch', function(e) {
console.log("A fetch event detected by /profile service worker"); 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);
})