aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/serviceworkerglobalscope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/serviceworkerglobalscope.rs')
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs643
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);
}