diff options
Diffstat (limited to 'components/script/dom/serviceworkerglobalscope.rs')
-rw-r--r-- | components/script/dom/serviceworkerglobalscope.rs | 643 |
1 files changed, 396 insertions, 247 deletions
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index ef69014c109..089b8c83590 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -1,141 +1,282 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use devtools; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::devtools; +use crate::dom::abstractworker::WorkerScriptMsg; +use crate::dom::abstractworkerglobalscope::{run_worker_event_loop, WorkerEventLoopMethods}; +use crate::dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; +use crate::dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods; +use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::structuredclone; +use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; +use crate::dom::event::Event; +use crate::dom::eventtarget::EventTarget; +use crate::dom::extendableevent::ExtendableEvent; +use crate::dom::extendablemessageevent::ExtendableMessageEvent; +use crate::dom::globalscope::GlobalScope; +use crate::dom::identityhub::Identities; +use crate::dom::worker::TrustedWorkerAddress; +use crate::dom::workerglobalscope::WorkerGlobalScope; +use crate::fetch::load_whole_resource; +use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; +use crate::script_runtime::{ + new_rt_and_cx, CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext, + Runtime, ScriptChan, +}; +use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; +use crate::task_source::TaskSourceName; +use crossbeam_channel::{after, unbounded, Receiver, Sender}; use devtools_traits::DevtoolScriptControlMsg; -use dom::abstractworker::WorkerScriptMsg; -use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; -use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; -use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{Root, RootCollection}; -use dom::bindings::reflector::DomObject; -use dom::bindings::str::DOMString; -use dom::event::Event; -use dom::eventtarget::EventTarget; -use dom::extendableevent::ExtendableEvent; -use dom::extendablemessageevent::ExtendableMessageEvent; -use dom::globalscope::GlobalScope; -use dom::workerglobalscope::WorkerGlobalScope; use dom_struct::dom_struct; -use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; +use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; -use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext}; +use js::jsapi::{JSContext, JS_AddInterruptCallback}; use js::jsval::UndefinedValue; -use js::rust::Runtime; -use net_traits::{load_whole_resource, IpcSend, CustomResponseMediator}; -use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; -use script_runtime::{CommonScriptMsg, StackRootTLS, get_reports, new_rt_and_cx, ScriptChan}; -use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg, WorkerScriptLoadOrigin}; -use servo_config::prefs::PREFS; +use msg::constellation_msg::PipelineId; +use net_traits::request::{CredentialsMode, Destination, ParserMetadata, Referrer, RequestBuilder}; +use net_traits::{CustomResponseMediator, IpcSend}; +use parking_lot::Mutex; +use script_traits::{ScopeThings, ServiceWorkerMsg, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; +use servo_config::pref; use servo_rand::random; use servo_url::{MutableOrigin, ServoUrl}; -use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; -use std::thread; -use std::time::Duration; -use style::thread_state::{self, IN_WORKER, SCRIPT}; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::thread::{self, JoinHandle}; +use std::time::{Duration, Instant}; +use style::thread_state::{self, ThreadState}; /// Messages used to control service worker event loop pub enum ServiceWorkerScriptMsg { /// Message common to all workers CommonWorker(WorkerScriptMsg), - // Message to request a custom response by the service worker - Response(CustomResponseMediator) + /// Message to request a custom response by the service worker + Response(CustomResponseMediator), + /// Wake-up call from the task queue. + WakeUp, +} + +impl QueuedTaskConversion for ServiceWorkerScriptMsg { + fn task_source_name(&self) -> Option<&TaskSourceName> { + let script_msg = match self { + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg, + _ => return None, + }; + match script_msg { + CommonScriptMsg::Task(_category, _boxed, _pipeline_id, task_source) => { + Some(&task_source) + }, + _ => None, + } + } + + fn pipeline_id(&self) -> Option<PipelineId> { + // Workers always return None, since the pipeline_id is only used to check for document activity, + // and this check does not apply to worker event-loops. + None + } + + fn into_queued_task(self) -> Option<QueuedTask> { + let script_msg = match self { + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg, + _ => return None, + }; + let (category, boxed, pipeline_id, task_source) = match script_msg { + CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => { + (category, boxed, pipeline_id, task_source) + }, + _ => return None, + }; + Some((None, category, boxed, pipeline_id, task_source)) + } + + fn from_queued_task(queued_task: QueuedTask) -> Self { + let (_worker, category, boxed, pipeline_id, task_source) = queued_task; + let script_msg = CommonScriptMsg::Task(category, boxed, pipeline_id, task_source); + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) + } + + fn inactive_msg() -> Self { + // Inactive is only relevant in the context of a browsing-context event-loop. + panic!("Workers should never receive messages marked as inactive"); + } + + fn wake_up_msg() -> Self { + ServiceWorkerScriptMsg::WakeUp + } + + fn is_wake_up(&self) -> bool { + match self { + ServiceWorkerScriptMsg::WakeUp => true, + _ => false, + } + } +} + +/// Messages sent from the owning registration. +pub enum ServiceWorkerControlMsg { + /// Shutdown. + Exit, } pub enum MixedMessage { FromServiceWorker(ServiceWorkerScriptMsg), FromDevtools(DevtoolScriptControlMsg), - FromTimeoutThread(()) + FromControl(ServiceWorkerControlMsg), } -#[derive(JSTraceable, Clone)] +#[derive(Clone, JSTraceable)] pub struct ServiceWorkerChan { - pub sender: Sender<ServiceWorkerScriptMsg> + pub sender: Sender<ServiceWorkerScriptMsg>, } impl ScriptChan for ServiceWorkerChan { fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { self.sender - .send(ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(msg))) + .send(ServiceWorkerScriptMsg::CommonWorker( + WorkerScriptMsg::Common(msg), + )) .map_err(|_| ()) } - fn clone(&self) -> Box<ScriptChan + Send> { - box ServiceWorkerChan { + fn clone(&self) -> Box<dyn ScriptChan + Send> { + Box::new(ServiceWorkerChan { sender: self.sender.clone(), - } + }) } } +unsafe_no_jsmanaged_fields!(TaskQueue<ServiceWorkerScriptMsg>); + #[dom_struct] pub struct ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, - #[ignore_heap_size_of = "Defined in std"] - receiver: Receiver<ServiceWorkerScriptMsg>, - #[ignore_heap_size_of = "Defined in std"] + + #[ignore_malloc_size_of = "Defined in std"] + task_queue: TaskQueue<ServiceWorkerScriptMsg>, + + #[ignore_malloc_size_of = "Defined in std"] own_sender: Sender<ServiceWorkerScriptMsg>, - #[ignore_heap_size_of = "Defined in std"] - timer_event_port: Receiver<()>, - #[ignore_heap_size_of = "Defined in std"] + + /// A port on which a single "time-out" message can be received, + /// indicating the sw should stop running, + /// while still draining the task-queue + // and running all enqueued, and not cancelled, tasks. + #[ignore_malloc_size_of = "Defined in std"] + time_out_port: Receiver<Instant>, + + #[ignore_malloc_size_of = "Defined in std"] swmanager_sender: IpcSender<ServiceWorkerMsg>, + scope_url: ServoUrl, + + /// A receiver of control messages, + /// currently only used to signal shutdown. + #[ignore_malloc_size_of = "Channels are hard"] + control_receiver: Receiver<ServiceWorkerControlMsg>, +} + +impl WorkerEventLoopMethods for ServiceWorkerGlobalScope { + type WorkerMsg = ServiceWorkerScriptMsg; + type ControlMsg = ServiceWorkerControlMsg; + type Event = MixedMessage; + + fn task_queue(&self) -> &TaskQueue<ServiceWorkerScriptMsg> { + &self.task_queue + } + + fn handle_event(&self, event: MixedMessage) -> bool { + self.handle_mixed_message(event) + } + + fn handle_worker_post_event(&self, _worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset> { + None + } + + fn from_control_msg(&self, msg: ServiceWorkerControlMsg) -> MixedMessage { + MixedMessage::FromControl(msg) + } + + fn from_worker_msg(&self, msg: ServiceWorkerScriptMsg) -> MixedMessage { + MixedMessage::FromServiceWorker(msg) + } + + fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { + MixedMessage::FromDevtools(msg) + } + + fn control_receiver(&self) -> &Receiver<ServiceWorkerControlMsg> { + &self.control_receiver + } } impl ServiceWorkerGlobalScope { - fn new_inherited(init: WorkerGlobalScopeInit, - worker_url: ServoUrl, - from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, - runtime: Runtime, - own_sender: Sender<ServiceWorkerScriptMsg>, - receiver: Receiver<ServiceWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<()>, - swmanager_sender: IpcSender<ServiceWorkerMsg>, - scope_url: ServoUrl) - -> ServiceWorkerGlobalScope { + fn new_inherited( + init: WorkerGlobalScopeInit, + worker_url: ServoUrl, + from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, + runtime: Runtime, + own_sender: Sender<ServiceWorkerScriptMsg>, + receiver: Receiver<ServiceWorkerScriptMsg>, + time_out_port: Receiver<Instant>, + swmanager_sender: IpcSender<ServiceWorkerMsg>, + scope_url: ServoUrl, + control_receiver: Receiver<ServiceWorkerControlMsg>, + closing: Arc<AtomicBool>, + ) -> ServiceWorkerGlobalScope { ServiceWorkerGlobalScope { - workerglobalscope: WorkerGlobalScope::new_inherited(init, - worker_url, - runtime, - from_devtools_receiver, - timer_event_chan, - None), - receiver: receiver, - timer_event_port: timer_event_port, + workerglobalscope: WorkerGlobalScope::new_inherited( + init, + DOMString::new(), + WorkerType::Classic, // FIXME(cybai): Should be provided from `Run Service Worker` + worker_url, + runtime, + from_devtools_receiver, + closing, + Arc::new(Mutex::new(Identities::new())), + ), + task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender: own_sender, + time_out_port, swmanager_sender: swmanager_sender, - scope_url: scope_url + scope_url: scope_url, + control_receiver, } } #[allow(unsafe_code)] - pub fn new(init: WorkerGlobalScopeInit, - worker_url: ServoUrl, - from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, - runtime: Runtime, - own_sender: Sender<ServiceWorkerScriptMsg>, - receiver: Receiver<ServiceWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<()>, - swmanager_sender: IpcSender<ServiceWorkerMsg>, - scope_url: ServoUrl) - -> Root<ServiceWorkerGlobalScope> { + pub fn new( + init: WorkerGlobalScopeInit, + worker_url: ServoUrl, + from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, + runtime: Runtime, + own_sender: Sender<ServiceWorkerScriptMsg>, + receiver: Receiver<ServiceWorkerScriptMsg>, + time_out_port: Receiver<Instant>, + swmanager_sender: IpcSender<ServiceWorkerMsg>, + scope_url: ServoUrl, + control_receiver: Receiver<ServiceWorkerControlMsg>, + closing: Arc<AtomicBool>, + ) -> DomRoot<ServiceWorkerGlobalScope> { let cx = runtime.cx(); - let scope = box ServiceWorkerGlobalScope::new_inherited(init, - worker_url, - from_devtools_receiver, - runtime, - own_sender, - receiver, - timer_event_chan, - timer_event_port, - swmanager_sender, - scope_url); - unsafe { - ServiceWorkerGlobalScopeBinding::Wrap(cx, scope) - } + let scope = Box::new(ServiceWorkerGlobalScope::new_inherited( + init, + worker_url, + from_devtools_receiver, + runtime, + own_sender, + receiver, + time_out_port, + swmanager_sender, + scope_url, + control_receiver, + closing, + )); + unsafe { ServiceWorkerGlobalScopeBinding::Wrap(SafeJSContext::from_ptr(cx), scope) } } pub fn origin(&self) -> MutableOrigin { @@ -143,188 +284,192 @@ impl ServiceWorkerGlobalScope { } #[allow(unsafe_code)] - pub fn run_serviceworker_scope(scope_things: ScopeThings, - own_sender: Sender<ServiceWorkerScriptMsg>, - receiver: Receiver<ServiceWorkerScriptMsg>, - devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, - swmanager_sender: IpcSender<ServiceWorkerMsg>, - scope_url: ServoUrl) { - let ScopeThings { script_url, - init, - worker_load_origin, - .. } = scope_things; + // https://html.spec.whatwg.org/multipage/#run-a-worker + pub fn run_serviceworker_scope( + scope_things: ScopeThings, + own_sender: Sender<ServiceWorkerScriptMsg>, + receiver: Receiver<ServiceWorkerScriptMsg>, + devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, + swmanager_sender: IpcSender<ServiceWorkerMsg>, + scope_url: ServoUrl, + control_receiver: Receiver<ServiceWorkerControlMsg>, + context_sender: Sender<ContextForRequestInterrupt>, + closing: Arc<AtomicBool>, + ) -> JoinHandle<()> { + let ScopeThings { + script_url, + init, + worker_load_origin, + .. + } = scope_things; let serialized_worker_url = script_url.to_string(); - thread::Builder::new().name(format!("ServiceWorker for {}", serialized_worker_url)).spawn(move || { - thread_state::initialize(SCRIPT | IN_WORKER); - let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); - - let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin; - - let request = RequestInit { - url: script_url.clone(), - type_: RequestType::Script, - destination: Destination::ServiceWorker, - credentials_mode: CredentialsMode::Include, - use_url_credentials: true, - origin: script_url, - pipeline_id: pipeline_id, - referrer_url: referrer_url, - referrer_policy: referrer_policy, - .. RequestInit::default() - }; - - let (url, source) = match load_whole_resource(request, - &init.resource_threads.sender()) { - Err(_) => { - println!("error loading script {}", serialized_worker_url); - return; - } - Ok((metadata, bytes)) => { - (metadata.final_url, String::from_utf8(bytes).unwrap()) + let origin = scope_url.origin(); + thread::Builder::new() + .name(format!("ServiceWorker for {}", serialized_worker_url)) + .spawn(move || { + thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER); + let runtime = new_rt_and_cx(None); + let _ = context_sender.send(ContextForRequestInterrupt::new(runtime.cx())); + + let roots = RootCollection::new(); + let _stack_roots = ThreadLocalStackRoots::new(&roots); + + let WorkerScriptLoadOrigin { + referrer_url, + referrer_policy, + pipeline_id, + } = worker_load_origin; + + // Service workers are time limited + // https://w3c.github.io/ServiceWorker/#service-worker-lifetime + let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64; + let time_out_port = after(Duration::new(sw_lifetime_timeout, 0)); + + let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded(); + ROUTER + .route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan); + + let resource_threads_sender = init.resource_threads.sender(); + let global = ServiceWorkerGlobalScope::new( + init, + script_url.clone(), + devtools_mpsc_port, + runtime, + own_sender, + receiver, + time_out_port, + swmanager_sender, + scope_url, + control_receiver, + closing, + ); + + let referrer = referrer_url + .map(|url| Referrer::ReferrerUrl(url)) + .unwrap_or_else(|| global.upcast::<GlobalScope>().get_referrer()); + + let request = RequestBuilder::new(script_url, referrer) + .destination(Destination::ServiceWorker) + .credentials_mode(CredentialsMode::Include) + .parser_metadata(ParserMetadata::NotParserInserted) + .use_url_credentials(true) + .pipeline_id(Some(pipeline_id)) + .referrer_policy(referrer_policy) + .origin(origin); + + let (_url, source) = + match load_whole_resource(request, &resource_threads_sender, &*global.upcast()) + { + Err(_) => { + println!("error loading script {}", serialized_worker_url); + return; + }, + Ok((metadata, bytes)) => { + (metadata.final_url, String::from_utf8(bytes).unwrap()) + }, + }; + + let scope = global.upcast::<WorkerGlobalScope>(); + + unsafe { + // Handle interrupt requests + JS_AddInterruptCallback(*scope.get_cx(), Some(interrupt_callback)); } - }; - - let runtime = unsafe { new_rt_and_cx() }; - - let (devtools_mpsc_chan, devtools_mpsc_port) = channel(); - ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_receiver, devtools_mpsc_chan); - // TODO XXXcreativcoder use this timer_ipc_port, when we have a service worker instance here - let (timer_ipc_chan, _timer_ipc_port) = ipc::channel().unwrap(); - let (timer_chan, timer_port) = channel(); - let global = ServiceWorkerGlobalScope::new( - init, url, devtools_mpsc_port, runtime, - own_sender, receiver, - timer_ipc_chan, timer_port, swmanager_sender, scope_url); - let scope = global.upcast::<WorkerGlobalScope>(); - - unsafe { - // Handle interrupt requests - JS_SetInterruptCallback(scope.runtime(), Some(interrupt_callback)); - } - - scope.execute_script(DOMString::from(source)); - // Service workers are time limited - thread::Builder::new().name("SWTimeoutThread".to_owned()).spawn(move || { - let sw_lifetime_timeout = PREFS.get("dom.serviceworker.timeout_seconds").as_u64().unwrap(); - thread::sleep(Duration::new(sw_lifetime_timeout, 0)); - let _ = timer_chan.send(()); - }).expect("Thread spawning failed"); - - global.dispatch_activate(); - let reporter_name = format!("service-worker-reporter-{}", random::<u64>()); - scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| { - // https://html.spec.whatwg.org/multipage/#event-loop-processing-model - // Step 1 - while let Ok(event) = global.receive_event() { - // Step 3 - if !global.handle_event(event) { - break; - } - // Step 6 - global.upcast::<WorkerGlobalScope>().perform_a_microtask_checkpoint(); - } - }, reporter_name, scope.script_chan(), CommonScriptMsg::CollectReports); - }).expect("Thread spawning failed"); + + scope.execute_script(DOMString::from(source)); + + global.dispatch_activate(); + let reporter_name = format!("service-worker-reporter-{}", random::<u64>()); + scope + .upcast::<GlobalScope>() + .mem_profiler_chan() + .run_with_memory_reporting( + || { + // Step 29, Run the responsible event loop specified + // by inside settings until it is destroyed. + // The worker processing model remains on this step + // until the event loop is destroyed, + // which happens after the closing flag is set to true, + // or until the worker has run beyond its allocated time. + while !scope.is_closing() && !global.has_timed_out() { + run_worker_event_loop(&*global, None); + } + }, + reporter_name, + scope.script_chan(), + CommonScriptMsg::CollectReports, + ); + scope.clear_js_runtime(); + }) + .expect("Thread spawning failed") } - fn handle_event(&self, event: MixedMessage) -> bool { - match event { - MixedMessage::FromDevtools(msg) => { - match msg { - DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => - devtools::handle_evaluate_js(self.upcast(), string, sender), - DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) => - devtools::handle_get_cached_messages(pipe_id, message_types, sender), - DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => - devtools::handle_wants_live_notifications(self.upcast(), bool_val), - _ => debug!("got an unusable devtools control message inside the worker!"), - } - true - } + fn handle_mixed_message(&self, msg: MixedMessage) -> bool { + match msg { + MixedMessage::FromDevtools(msg) => match msg { + DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => { + devtools::handle_evaluate_js(self.upcast(), string, sender) + }, + DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => { + devtools::handle_wants_live_notifications(self.upcast(), bool_val) + }, + _ => debug!("got an unusable devtools control message inside the worker!"), + }, MixedMessage::FromServiceWorker(msg) => { self.handle_script_event(msg); - true - } - MixedMessage::FromTimeoutThread(_) => { - let _ = self.swmanager_sender.send(ServiceWorkerMsg::Timeout(self.scope_url.clone())); - false - } + }, + MixedMessage::FromControl(ServiceWorkerControlMsg::Exit) => { + return false; + }, } + true + } + + fn has_timed_out(&self) -> bool { + // TODO: https://w3c.github.io/ServiceWorker/#service-worker-lifetime + false } fn handle_script_event(&self, msg: ServiceWorkerScriptMsg) { use self::ServiceWorkerScriptMsg::*; match msg { - CommonWorker(WorkerScriptMsg::DOMMessage(data)) => { + 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(scope.upcast(), message.handle_mut()); - ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle()); - }, - CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable))) => { - runnable.handler() + let _ac = enter_realm(&*scope); + rooted!(in(*scope.get_cx()) let mut message = UndefinedValue()); + if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut()) + { + ExtendableMessageEvent::dispatch_jsval( + target, + scope.upcast(), + message.handle(), + ports, + ); + } else { + ExtendableMessageEvent::dispatch_error(target, scope.upcast()); + } }, - CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan))) => { - let scope = self.upcast::<WorkerGlobalScope>(); - let cx = scope.get_cx(); - let path_seg = format!("url({})", scope.get_url()); - let reports = get_reports(cx, path_seg); - reports_chan.send(reports); + CommonWorker(WorkerScriptMsg::Common(msg)) => { + self.upcast::<WorkerGlobalScope>().process_event(msg); }, Response(mediator) => { // TODO XXXcreativcoder This will eventually use a FetchEvent interface to fire event // when we have the Request and Response dom api's implemented - // https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/index.html#fetch-event-section + // https://w3c.github.io/ServiceWorker/#fetchevent-interface self.upcast::<EventTarget>().fire_event(atom!("fetch")); let _ = mediator.response_chan.send(None); - } - } - } - - #[allow(unsafe_code)] - fn receive_event(&self) -> Result<MixedMessage, RecvError> { - let scope = self.upcast::<WorkerGlobalScope>(); - let worker_port = &self.receiver; - let devtools_port = scope.from_devtools_receiver(); - let timer_event_port = &self.timer_event_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); - unsafe { - worker_handle.add(); - if scope.from_devtools_sender().is_some() { - devtools_handle.add(); - } - timer_port_handle.add(); - } - - let ret = sel.wait(); - if ret == worker_handle.id() { - Ok(MixedMessage::FromServiceWorker(try!(worker_port.recv()))) - }else if ret == devtools_handle.id() { - Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) - } else if ret == timer_port_handle.id() { - Ok(MixedMessage::FromTimeoutThread(try!(timer_event_port.recv()))) - } else { - panic!("unexpected select result!") + }, + WakeUp => {}, } } - 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() - } + pub fn script_chan(&self) -> Box<dyn ScriptChan + Send> { + Box::new(ServiceWorkerChan { + sender: self.own_sender.clone(), + }) } fn dispatch_activate(&self) { @@ -336,9 +481,10 @@ impl ServiceWorkerGlobalScope { #[allow(unsafe_code)] unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool { + let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); + let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)); let worker = - Root::downcast::<WorkerGlobalScope>(GlobalScope::from_context(cx)) - .expect("global is not a worker scope"); + DomRoot::downcast::<WorkerGlobalScope>(global).expect("global is not a worker scope"); assert!(worker.is::<ServiceWorkerGlobalScope>()); // A false response causes the script to terminate @@ -346,6 +492,9 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool { } impl ServiceWorkerGlobalScopeMethods for ServiceWorkerGlobalScope { - // https://w3c.github.io/ServiceWorker/#service-worker-global-scope-onmessage-attribute + // https://w3c.github.io/ServiceWorker/#dom-serviceworkerglobalscope-onmessage event_handler!(message, GetOnmessage, SetOnmessage); + + // https://w3c.github.io/ServiceWorker/#dom-serviceworkerglobalscope-onmessageerror + event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror); } |