Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement postMessage for ServiceWorkers #12910

Merged
merged 3 commits into from Sep 15, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -1000,6 +1000,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");
}
}
}
}

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

@@ -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());
Ok(worker_registration)
@@ -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)]
@@ -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)
@@ -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;
@@ -392,12 +392,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")
}
}

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

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.