From 15a2064c0d7b468724b43d1cb6157d506ad19093 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Fri, 15 Apr 2016 17:40:45 +0530 Subject: [PATCH] implement related sw interface and register method --- components/profile/time.rs | 1 + components/profile_traits/time.rs | 1 + components/script/dom/abstractworker.rs | 104 +++++ .../script/dom/abstractworkerglobalscope.rs | 65 +++ components/script/dom/bindings/str.rs | 1 + components/script/dom/bindings/trace.rs | 7 +- components/script/dom/client.rs | 59 +++ .../script/dom/dedicatedworkerglobalscope.rs | 69 +--- components/script/dom/mod.rs | 7 + components/script/dom/navigator.rs | 8 + components/script/dom/serviceworker.rs | 172 ++++++++ .../script/dom/serviceworkercontainer.rs | 104 +++++ .../script/dom/serviceworkerglobalscope.rs | 382 ++++++++++++++++++ .../script/dom/serviceworkerregistration.rs | 67 +++ components/script/dom/webidls/Client.webidl | 21 + .../script/dom/webidls/Navigator.webidl | 5 + .../script/dom/webidls/ServiceWorker.webidl | 25 ++ .../dom/webidls/ServiceWorkerContainer.webidl | 27 ++ .../webidls/ServiceWorkerGlobalScope.webidl | 23 ++ .../webidls/ServiceWorkerRegistration.webidl | 20 + components/script/dom/worker.rs | 145 +------ components/script/dom/workerglobalscope.rs | 90 ++++- components/script/script_runtime.rs | 1 + components/script/script_thread.rs | 26 +- components/script_traits/lib.rs | 2 +- components/util/prefs.rs | 7 + python/tidy/servo_tidy/tidy.py | 1 + resources/prefs.json | 1 + tests/wpt/mozilla/meta/MANIFEST.json | 6 + .../meta/mozilla/service-workers/__dir__.ini | 1 + .../mozilla/service-workers/resources/sw.js | 1 + .../service-worker-registration.html | 52 +++ .../tests/mozilla/service-workers/sw.js | 1 + 33 files changed, 1285 insertions(+), 217 deletions(-) create mode 100644 components/script/dom/abstractworker.rs create mode 100644 components/script/dom/abstractworkerglobalscope.rs create mode 100644 components/script/dom/client.rs create mode 100644 components/script/dom/serviceworker.rs create mode 100644 components/script/dom/serviceworkercontainer.rs create mode 100644 components/script/dom/serviceworkerglobalscope.rs create mode 100644 components/script/dom/serviceworkerregistration.rs create mode 100644 components/script/dom/webidls/Client.webidl create mode 100644 components/script/dom/webidls/ServiceWorker.webidl create mode 100644 components/script/dom/webidls/ServiceWorkerContainer.webidl create mode 100644 components/script/dom/webidls/ServiceWorkerGlobalScope.webidl create mode 100644 components/script/dom/webidls/ServiceWorkerRegistration.webidl create mode 100644 tests/wpt/mozilla/meta/mozilla/service-workers/__dir__.ini create mode 100644 tests/wpt/mozilla/tests/mozilla/service-workers/resources/sw.js create mode 100644 tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.html create mode 100644 tests/wpt/mozilla/tests/mozilla/service-workers/sw.js diff --git a/components/profile/time.rs b/components/profile/time.rs index 6271c8745e19..9c310fe92706 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -149,6 +149,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptStylesheetLoad => "Script Stylesheet Load", ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event", ProfilerCategory::ScriptWorkerEvent => "Script Worker Event", + ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event", ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat", }; format!("{}{}", padding, name) diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs index 4083f3f3c14d..5b969ecd8f7e 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -80,6 +80,7 @@ pub enum ProfilerCategory { ScriptUpdateReplacedElement, ScriptWebSocketEvent, ScriptWorkerEvent, + ScriptServiceWorkerEvent, ApplicationHeartbeat, } diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs new file mode 100644 index 000000000000..f9ff583a0ddd --- /dev/null +++ b/components/script/dom/abstractworker.rs @@ -0,0 +1,104 @@ +/* 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 dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::Reflectable; +use dom::bindings::str::DOMString; +use dom::bindings::structuredclone::StructuredCloneData; +use js::jsapi::{JSRuntime, JS_RequestInterruptCallback}; +use js::rust::Runtime; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; +use net_traits::{LoadOrigin, RequestSource}; +use script_runtime::CommonScriptMsg; +use url::Url; + +/// Messages used to control the worker event loops +pub enum WorkerScriptMsg { + /// Common variants associated with the script messages + Common(CommonScriptMsg), + /// Message sent through Worker.postMessage + DOMMessage(StructuredCloneData), +} + +#[derive(Clone)] +pub struct WorkerScriptLoadOrigin { + pub referrer_url: Option, + pub referrer_policy: Option, + pub request_source: RequestSource, + pub pipeline_id: Option +} + +impl LoadOrigin for WorkerScriptLoadOrigin { + fn referrer_url(&self) -> Option { + self.referrer_url.clone() + } + fn referrer_policy(&self) -> Option { + self.referrer_policy.clone() + } + fn request_source(&self) -> RequestSource { + self.request_source.clone() + } + fn pipeline_id(&self) -> Option { + self.pipeline_id.clone() + } +} + +pub struct SimpleWorkerErrorHandler { + pub addr: Trusted, +} + +impl SimpleWorkerErrorHandler { + pub fn new(addr: Trusted) -> SimpleWorkerErrorHandler { + SimpleWorkerErrorHandler { + addr: addr + } + } +} + +pub struct WorkerErrorHandler { + pub addr: Trusted, + pub msg: DOMString, + pub file_name: DOMString, + pub line_num: u32, + pub col_num: u32, +} + +impl WorkerErrorHandler { + pub fn new(addr: Trusted, msg: DOMString, file_name: DOMString, line_num: u32, col_num: u32) + -> WorkerErrorHandler { + WorkerErrorHandler { + addr: addr, + msg: msg, + file_name: file_name, + line_num: line_num, + col_num: col_num, + } + } +} + +#[derive(Copy, Clone)] +pub struct SharedRt { + pub rt: *mut JSRuntime +} + +impl SharedRt { + pub fn new(rt: &Runtime) -> SharedRt { + SharedRt { + rt: rt.rt() + } + } + + #[allow(unsafe_code)] + pub fn request_interrupt(&self) { + unsafe { + JS_RequestInterruptCallback(self.rt); + } + } + + pub fn rt(&self) -> *mut JSRuntime { + self.rt + } +} +#[allow(unsafe_code)] +unsafe impl Send for SharedRt {} diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs new file mode 100644 index 000000000000..e584e41c62aa --- /dev/null +++ b/components/script/dom/abstractworkerglobalscope.rs @@ -0,0 +1,65 @@ +/* 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 dom::abstractworker::WorkerScriptMsg; +use dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::Reflectable; +use script_runtime::{ScriptChan, CommonScriptMsg, ScriptPort}; +use std::sync::mpsc::{Receiver, Sender}; + +/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with +/// common event loop messages. While this SendableWorkerScriptChan is alive, the associated +/// Worker object will remain alive. +#[derive(JSTraceable, Clone)] +pub struct SendableWorkerScriptChan { + pub sender: Sender<(Trusted, CommonScriptMsg)>, + pub worker: Trusted, +} + +impl ScriptChan for SendableWorkerScriptChan { + fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { + self.sender.send((self.worker.clone(), msg)).map_err(|_| ()) + } + + fn clone(&self) -> Box { + box SendableWorkerScriptChan { + sender: self.sender.clone(), + worker: self.worker.clone(), + } + } +} + +/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with +/// worker event loop messages. While this SendableWorkerScriptChan is alive, the associated +/// Worker object will remain alive. +#[derive(JSTraceable, Clone)] +pub struct WorkerThreadWorkerChan { + pub sender: Sender<(Trusted, WorkerScriptMsg)>, + pub worker: Trusted, +} + +impl ScriptChan for WorkerThreadWorkerChan { + fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { + self.sender + .send((self.worker.clone(), WorkerScriptMsg::Common(msg))) + .map_err(|_| ()) + } + + fn clone(&self) -> Box { + box WorkerThreadWorkerChan { + sender: self.sender.clone(), + worker: self.worker.clone(), + } + } +} + +impl ScriptPort for Receiver<(Trusted, WorkerScriptMsg)> { + fn recv(&self) -> Result { + match self.recv().map(|(_, msg)| msg) { + Ok(WorkerScriptMsg::Common(script_msg)) => Ok(script_msg), + Ok(WorkerScriptMsg::DOMMessage(_)) => panic!("unexpected worker event message!"), + Err(_) => Err(()), + } + } +} diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index 4c5f4b4fd6a1..3ee685fbe024 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -81,6 +81,7 @@ impl ops::Deref for ByteString { /// A string that is constructed from a UCS-2 buffer by replacing invalid code /// points with the replacement character. +#[derive(Clone, HeapSizeOf)] pub struct USVString(pub String); diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 10a815fffa71..8a4913bfe204 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -34,12 +34,12 @@ use canvas_traits::{CompositionOrBlending, LineCapStyle, LineJoinStyle, Repetiti use cssparser::RGBA; use devtools_traits::CSSError; use devtools_traits::WorkerId; +use dom::abstractworker::SharedRt; use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, Reflector}; -use dom::bindings::str::DOMString; +use dom::bindings::str::{DOMString, USVString}; use dom::bindings::utils::WindowProxyHandler; -use dom::worker::SharedRt; use encoding::types::EncodingRef; use euclid::length::Length as EuclidLength; use euclid::matrix2d::Matrix2D; @@ -81,6 +81,7 @@ use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::mpsc::{Receiver, Sender}; +use std::time::SystemTime; use string_cache::{Atom, Namespace, QualName}; use style::attr::{AttrIdentifier, AttrValue}; use style::element_state::*; @@ -320,8 +321,10 @@ no_jsmanaged_fields!(ElementSnapshot); no_jsmanaged_fields!(HttpsState); no_jsmanaged_fields!(SharedRt); no_jsmanaged_fields!(TouchpadPressurePhase); +no_jsmanaged_fields!(USVString); no_jsmanaged_fields!(ReferrerPolicy); no_jsmanaged_fields!(ResourceThreads); +no_jsmanaged_fields!(SystemTime); impl JSTraceable for Box { #[inline] diff --git a/components/script/dom/client.rs b/components/script/dom/client.rs new file mode 100644 index 000000000000..1039ff1a04ae --- /dev/null +++ b/components/script/dom/client.rs @@ -0,0 +1,59 @@ +/* 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 dom::bindings::codegen::Bindings::ClientBinding::FrameType; +use dom::bindings::codegen::Bindings::ClientBinding::{ClientMethods, Wrap}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::JS; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::{DOMString, USVString}; +use dom::serviceworker::ServiceWorker; +use dom::window::Window; +use url::Url; +use uuid::Uuid; + +#[dom_struct] +pub struct Client { + reflector_: Reflector, + active_worker: Option>, + url: USVString, + frame_type: FrameType, + #[ignore_heap_size_of = "Defined in uuid"] + id: Uuid +} + +impl Client { + fn new_inherited(url: Url) -> Client { + Client { + reflector_: Reflector::new(), + active_worker: None, + url: USVString(url.as_str().to_owned()), + frame_type: FrameType::None, + id: Uuid::new_v4() + } + } + + pub fn new(window: &Window) -> Root { + reflect_dom_object(box Client::new_inherited(window.get_url()), GlobalRef::Window(window), Wrap) + } +} + +impl ClientMethods for Client { + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#client-url-attribute + fn Url(&self) -> USVString { + self.url.clone() + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#client-frametype + fn FrameType(&self) -> FrameType { + self.frame_type + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#client-id + fn Id(&self) -> DOMString { + let uid_str = format!("{}", self.id); + DOMString::from_string(uid_str) + } +} diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 3dae1d3cff49..dfa1672715bb 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -4,6 +4,8 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; +use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg, SharedRt , SimpleWorkerErrorHandler}; +use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; @@ -17,8 +19,7 @@ use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::messageevent::MessageEvent; -use dom::worker::{SimpleWorkerErrorHandler, SharedRt, TrustedWorkerAddress}; -use dom::worker::{WorkerScriptLoadOrigin, WorkerMessageHandler}; +use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler}; use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::WorkerGlobalScopeInit; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -40,70 +41,6 @@ use url::Url; use util::thread::spawn_named_with_send_on_panic; use util::thread_state::{IN_WORKER, SCRIPT}; -/// Messages used to control the worker event loops -pub enum WorkerScriptMsg { - /// Common variants associated with the script messages - Common(CommonScriptMsg), - /// Message sent through Worker.postMessage - DOMMessage(StructuredCloneData), -} - -/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with -/// common event loop messages. While this SendableWorkerScriptChan is alive, the associated -/// Worker object will remain alive. -#[derive(JSTraceable, Clone)] -pub struct SendableWorkerScriptChan { - sender: Sender<(TrustedWorkerAddress, CommonScriptMsg)>, - worker: TrustedWorkerAddress, -} - -impl ScriptChan for SendableWorkerScriptChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.sender.send((self.worker.clone(), msg)).map_err(|_| ()) - } - - fn clone(&self) -> Box { - box SendableWorkerScriptChan { - sender: self.sender.clone(), - worker: self.worker.clone(), - } - } -} - -/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with -/// worker event loop messages. While this SendableWorkerScriptChan is alive, the associated -/// Worker object will remain alive. -#[derive(JSTraceable, Clone)] -pub struct WorkerThreadWorkerChan { - sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - worker: TrustedWorkerAddress, -} - -impl ScriptChan for WorkerThreadWorkerChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.sender - .send((self.worker.clone(), WorkerScriptMsg::Common(msg))) - .map_err(|_| ()) - } - - fn clone(&self) -> Box { - box WorkerThreadWorkerChan { - sender: self.sender.clone(), - worker: self.worker.clone(), - } - } -} - -impl ScriptPort for Receiver<(TrustedWorkerAddress, WorkerScriptMsg)> { - fn recv(&self) -> Result { - match self.recv().map(|(_, msg)| msg) { - Ok(WorkerScriptMsg::Common(script_msg)) => Ok(script_msg), - Ok(WorkerScriptMsg::DOMMessage(_)) => panic!("unexpected worker event message!"), - Err(_) => Err(()), - } - } -} - /// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular /// value for the duration of this object's lifetime. This ensures that the related Worker /// object only lives as long as necessary (ie. while events are being executed), while diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index a5d8e7a707ae..ff1a46389982 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -209,6 +209,8 @@ pub mod types { include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs")); } +pub mod abstractworker; +pub mod abstractworkerglobalscope; pub mod activation; pub mod attr; pub mod beforeunloadevent; @@ -231,6 +233,7 @@ pub mod canvasgradient; pub mod canvaspattern; pub mod canvasrenderingcontext2d; pub mod characterdata; +pub mod client; pub mod closeevent; pub mod comment; pub mod console; @@ -363,6 +366,10 @@ pub mod progressevent; pub mod radionodelist; pub mod range; pub mod screen; +pub mod serviceworker; +pub mod serviceworkercontainer; +pub mod serviceworkerglobalscope; +pub mod serviceworkerregistration; pub mod servohtmlparser; pub mod servoxmlparser; pub mod storage; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 8a17b2dbc17b..75bd2c13c23e 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -12,6 +12,7 @@ use dom::bluetooth::Bluetooth; use dom::mimetypearray::MimeTypeArray; use dom::navigatorinfo; use dom::pluginarray::PluginArray; +use dom::serviceworkercontainer::ServiceWorkerContainer; use dom::window::Window; #[dom_struct] @@ -20,6 +21,7 @@ pub struct Navigator { bluetooth: MutNullableHeap>, plugins: MutNullableHeap>, mime_types: MutNullableHeap>, + serviceWorker: MutNullableHeap>, } impl Navigator { @@ -29,6 +31,7 @@ impl Navigator { bluetooth: Default::default(), plugins: Default::default(), mime_types: Default::default(), + serviceWorker: Default::default(), } } @@ -99,4 +102,9 @@ impl NavigatorMethods for Navigator { fn JavaEnabled(&self) -> bool { false } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#navigator-service-worker-attribute + fn ServiceWorker(&self) -> Root { + self.serviceWorker.or_init(|| ServiceWorkerContainer::new(self.global().r())) + } } diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs new file mode 100644 index 000000000000..5122bcf5f0db --- /dev/null +++ b/components/script/dom/serviceworker.rs @@ -0,0 +1,172 @@ +/* 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 dom::abstractworker::{SimpleWorkerErrorHandler, WorkerErrorHandler}; +use dom::abstractworker::{WorkerScriptMsg, WorkerScriptLoadOrigin, SharedRt}; +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; +use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap}; +use dom::bindings::global::GlobalRef; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::Root; +use dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; +use dom::bindings::str::{DOMString, USVString}; +use dom::client::Client; +use dom::errorevent::ErrorEvent; +use dom::event::{Event, EventBubbles, EventCancelable}; +use dom::eventtarget::EventTarget; +use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; +use dom::workerglobalscope::prepare_workerscope_init; +use ipc_channel::ipc; +use js::jsapi::RootedValue; +use js::jsval::UndefinedValue; +use script_thread::Runnable; +use std::cell::Cell; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::{Sender, channel}; +use std::sync::{Arc, Mutex}; +use url::Url; + +pub type TrustedServiceWorkerAddress = Trusted; + +#[dom_struct] +pub struct ServiceWorker { + eventtarget: EventTarget, + script_url: DOMRefCell, + state: Cell, + closing: Arc, + #[ignore_heap_size_of = "Defined in std"] + sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + #[ignore_heap_size_of = "Defined in rust-mozjs"] + runtime: Arc>>, + skip_waiting: Cell +} + +impl ServiceWorker { + fn new_inherited(sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + closing: Arc, + script_url: &str, + skip_waiting: bool) -> ServiceWorker { + ServiceWorker { + eventtarget: EventTarget::new_inherited(), + closing: closing, + sender: sender, + script_url: DOMRefCell::new(String::from(script_url)), + state: Cell::new(ServiceWorkerState::Installing), + runtime: Arc::new(Mutex::new(None)), + skip_waiting: Cell::new(skip_waiting) + } + } + + pub fn new(global: GlobalRef, + closing: Arc, + sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + script_url: &str, + skip_waiting: bool) -> Root { + reflect_dom_object(box ServiceWorker::new_inherited(sender, closing, script_url, skip_waiting), global, Wrap) + } + + pub fn dispatch_simple_error(address: TrustedServiceWorkerAddress) { + let service_worker = address.root(); + service_worker.upcast().fire_simple_event("error"); + } + + pub fn is_closing(&self) -> bool { + self.closing.load(Ordering::SeqCst) + } + + pub fn set_transition_state(&self, state: ServiceWorkerState) { + self.state.set(state); + self.upcast::().fire_simple_event("statechange"); + } + + pub fn handle_error_message(address: TrustedServiceWorkerAddress, message: DOMString, + filename: DOMString, lineno: u32, colno: u32) { + let worker = address.root(); + + if worker.is_closing() { + return; + } + + let global = worker.r().global(); + let error = RootedValue::new(global.r().get_cx(), UndefinedValue()); + let errorevent = ErrorEvent::new(global.r(), atom!("error"), + EventBubbles::Bubbles, EventCancelable::Cancelable, + message, filename, lineno, colno, error.handle()); + errorevent.upcast::().fire(worker.upcast()); + } + + #[allow(unsafe_code)] + pub fn init_service_worker(global: GlobalRef, + script_url: Url, + skip_waiting: bool) -> Root { + let (sender, receiver) = channel(); + let closing = Arc::new(AtomicBool::new(false)); + let worker = ServiceWorker::new(global, + closing.clone(), + sender.clone(), + script_url.as_str(), + skip_waiting); + let worker_ref = Trusted::new(worker.r()); + + let worker_load_origin = WorkerScriptLoadOrigin { + referrer_url: None, + referrer_policy: None, + request_source: global.request_source(), + pipeline_id: Some(global.pipeline()) + }; + + let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); + let init = prepare_workerscope_init(global, + "Service Worker".to_owned(), + script_url.clone(), + devtools_sender.clone(), + closing); + + // represents a service worker client + let sw_client = Client::new(global.as_window()); + let trusted_client = Trusted::new(&*sw_client); + + ServiceWorkerGlobalScope::run_serviceworker_scope( + init, script_url, global.pipeline(), devtools_receiver, worker.runtime.clone(), worker_ref, + global.script_chan(), sender, receiver, trusted_client, worker_load_origin); + + worker + } +} + +impl ServiceWorkerMethods for ServiceWorker { + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-state-attribute + fn State(&self) -> ServiceWorkerState { + self.state.get() + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-url-attribute + fn ScriptURL(&self) -> USVString { + USVString(self.script_url.borrow().clone()) + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container-onerror-attribute + event_handler!(error, GetOnerror, SetOnerror); + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#ref-for-service-worker-onstatechange-attribute-1 + event_handler!(statechange, GetOnstatechange, SetOnstatechange); +} + +impl Runnable for SimpleWorkerErrorHandler { + #[allow(unrooted_must_root)] + fn handler(self: Box>) { + let this = *self; + ServiceWorker::dispatch_simple_error(this.addr); + } +} + +impl Runnable for WorkerErrorHandler { + #[allow(unrooted_must_root)] + fn handler(self: Box>) { + let this = *self; + ServiceWorker::handle_error_message(this.addr, this.msg, this.file_name, this.line_num, this.col_num); + } +} diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs new file mode 100644 index 000000000000..744daba7b91e --- /dev/null +++ b/components/script/dom/serviceworkercontainer.rs @@ -0,0 +1,104 @@ +/* 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 dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions; +use dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::{ServiceWorkerContainerMethods, Wrap}; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JS, MutNullableHeap, Root}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; +use dom::bindings::str::USVString; +use dom::eventtarget::EventTarget; +use dom::serviceworker::ServiceWorker; +use dom::serviceworkerregistration::ServiceWorkerRegistration; +use script_thread::ScriptThread; +use std::ascii::AsciiExt; +use std::default::Default; + +#[dom_struct] +pub struct ServiceWorkerContainer { + eventtarget: EventTarget, + controller: MutNullableHeap>, +} + +impl ServiceWorkerContainer { + fn new_inherited() -> ServiceWorkerContainer { + ServiceWorkerContainer { + eventtarget: EventTarget::new_inherited(), + controller: Default::default(), + } + } + + pub fn new(global: GlobalRef) -> Root { + reflect_dom_object(box ServiceWorkerContainer::new_inherited(), global, Wrap) + } +} + +pub trait Controllable { + fn set_controller(&self, active_worker: &ServiceWorker); +} + +impl Controllable for ServiceWorkerContainer { + fn set_controller(&self, active_worker: &ServiceWorker) { + self.controller.set(Some(active_worker)) + } +} + +impl ServiceWorkerContainerMethods for ServiceWorkerContainer { + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container-controller-attribute + fn GetController(&self) -> Option> { + return self.controller.get() + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container-register-method + fn Register(&self, + script_url: USVString, + options: &RegistrationOptions) -> Fallible> { + let USVString(ref script_url) = script_url; + // Step 3-4 + let script_url = match self.global().r().api_base_url().join(script_url) { + Ok(url) => url, + Err(_) => return Err(Error::Type("Invalid script URL".to_owned())) + }; + // Step 5 + match script_url.scheme() { + "https" | "http" => {}, + _ => return Err(Error::Type("Only secure origins are allowed".to_owned())) + } + // Step 6 + if script_url.path().to_ascii_lowercase().contains("%2f") || + script_url.path().to_ascii_lowercase().contains("%5c") { + return Err(Error::Type("Script URL contains forbidden characters".to_owned())); + } + // Step 8-9 + let scope = match options.scope { + Some(ref scope) => { + let &USVString(ref inner_scope) = scope; + match self.global().r().api_base_url().join(inner_scope) { + Ok(url) => url, + Err(_) => return Err(Error::Type("Invalid scope URL".to_owned())) + } + }, + None => script_url.join("./").unwrap() + }; + // Step 11 + match scope.scheme() { + "https" | "http" => {}, + _ => return Err(Error::Type("Only secure origins are allowed".to_owned())) + } + // Step 12 + if scope.path().to_ascii_lowercase().contains("%2f") || + scope.path().to_ascii_lowercase().contains("%5c") { + 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(), + self); + ScriptThread::set_registration(scope, &*worker_registration); + Ok(worker_registration) + } +} diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs new file mode 100644 index 000000000000..4039be054038 --- /dev/null +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -0,0 +1,382 @@ +/* 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; +use devtools_traits::DevtoolScriptControlMsg; +use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg, SharedRt, SimpleWorkerErrorHandler}; +use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; +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; +use dom::bindings::global::{GlobalRef, global_root_from_context}; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{Root, RootCollection}; +use dom::bindings::refcounted::{Trusted, LiveDOMReferences}; +use dom::bindings::reflector::Reflectable; +use dom::bindings::str::DOMString; +use dom::client::Client; +use dom::messageevent::MessageEvent; +use dom::serviceworker::TrustedServiceWorkerAddress; +use dom::workerglobalscope::WorkerGlobalScope; +use dom::workerglobalscope::WorkerGlobalScopeInit; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::router::ROUTER; +use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext, RootedValue}; +use js::jsval::UndefinedValue; +use js::rust::Runtime; +use msg::constellation_msg::PipelineId; +use net_traits::{LoadContext, load_whole_resource, CustomResponse, IpcSend}; +use rand::random; +use script_runtime::ScriptThreadEventCategory::ServiceWorkerEvent; +use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; +use script_traits::{TimerEvent, TimerSource}; +use std::mem::replace; +use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; +use std::sync::{Arc, Mutex}; +use std::time::Instant; +use url::Url; +use util::prefs; +use util::thread::spawn_named; +use util::thread_state; +use util::thread_state::{IN_WORKER, SCRIPT}; + +/// Set the `worker` field of a related ServiceWorkerGlobalScope object to a particular +/// value for the duration of this object's lifetime. This ensures that the related Worker +/// object only lives as long as necessary (ie. while events are being executed), while +/// providing a reference that can be cloned freely. +struct AutoWorkerReset<'a> { + workerscope: &'a ServiceWorkerGlobalScope, + old_worker: Option, +} + +impl<'a> AutoWorkerReset<'a> { + fn new(workerscope: &'a ServiceWorkerGlobalScope, + worker: TrustedServiceWorkerAddress) + -> AutoWorkerReset<'a> { + AutoWorkerReset { + workerscope: workerscope, + old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)), + } + } +} + +impl<'a> Drop for AutoWorkerReset<'a> { + fn drop(&mut self) { + *self.workerscope.worker.borrow_mut() = self.old_worker.clone(); + } +} + +enum MixedMessage { + FromServiceWorker((TrustedServiceWorkerAddress, WorkerScriptMsg)), + FromScheduler((TrustedServiceWorkerAddress, TimerEvent)), + FromDevtools(DevtoolScriptControlMsg), + FromNetwork(IpcSender>), +} + +#[dom_struct] +pub struct ServiceWorkerGlobalScope { + workerglobalscope: WorkerGlobalScope, + id: PipelineId, + #[ignore_heap_size_of = "Defined in std"] + receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + #[ignore_heap_size_of = "Defined in std"] + own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + #[ignore_heap_size_of = "Defined in std"] + timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, + #[ignore_heap_size_of = "Trusted has unclear ownership like JS"] + worker: DOMRefCell>, + #[ignore_heap_size_of = "Can't measure trait objects"] + /// Sender to the parent thread. + parent_sender: Box, + #[ignore_heap_size_of = "Defined in std"] + service_worker_client: Trusted +} + +impl ServiceWorkerGlobalScope { + fn new_inherited(init: WorkerGlobalScopeInit, + worker_url: Url, + id: PipelineId, + from_devtools_receiver: Receiver, + runtime: Runtime, + parent_sender: Box, + own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + timer_event_chan: IpcSender, + timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, + client: Trusted) + -> ServiceWorkerGlobalScope { + ServiceWorkerGlobalScope { + workerglobalscope: WorkerGlobalScope::new_inherited(init, + worker_url, + runtime, + from_devtools_receiver, + timer_event_chan), + id: id, + receiver: receiver, + own_sender: own_sender, + timer_event_port: timer_event_port, + parent_sender: parent_sender, + worker: DOMRefCell::new(None), + service_worker_client: client + } + } + + pub fn new(init: WorkerGlobalScopeInit, + worker_url: Url, + id: PipelineId, + from_devtools_receiver: Receiver, + runtime: Runtime, + parent_sender: Box, + own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + timer_event_chan: IpcSender, + timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>, + client: Trusted) + -> Root { + let cx = runtime.cx(); + let scope = box ServiceWorkerGlobalScope::new_inherited(init, + worker_url, + id, + from_devtools_receiver, + runtime, + parent_sender, + own_sender, + receiver, + timer_event_chan, + timer_event_port, + client); + ServiceWorkerGlobalScopeBinding::Wrap(cx, scope) + } + + #[allow(unsafe_code)] + pub fn run_serviceworker_scope(init: WorkerGlobalScopeInit, + worker_url: Url, + id: PipelineId, + from_devtools_receiver: IpcReceiver, + main_thread_rt: Arc>>, + worker: TrustedServiceWorkerAddress, + parent_sender: Box, + own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>, + client: Trusted, + worker_load_origin: WorkerScriptLoadOrigin) { + let serialized_worker_url = worker_url.to_string(); + spawn_named(format!("ServiceWorker for {}", serialized_worker_url), move || { + thread_state::initialize(SCRIPT | IN_WORKER); + + let roots = RootCollection::new(); + let _stack_roots_tls = StackRootTLS::new(&roots); + + let (url, source) = match load_whole_resource(LoadContext::Script, + &init.resource_threads.sender(), + worker_url, + &worker_load_origin) { + Err(_) => { + println!("error loading script {}", serialized_worker_url); + parent_sender.send(CommonScriptMsg::RunnableMsg(ServiceWorkerEvent, + box SimpleWorkerErrorHandler::new(worker))).unwrap(); + return; + } + Ok((metadata, bytes)) => { + (metadata.final_url, String::from_utf8(bytes).unwrap()) + } + }; + + let runtime = unsafe { new_rt_and_cx() }; + *main_thread_rt.lock().unwrap() = Some(SharedRt::new(&runtime)); + + let (devtools_mpsc_chan, devtools_mpsc_port) = channel(); + ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan); + + let (timer_tx, timer_rx) = channel(); + let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); + let worker_for_route = worker.clone(); + ROUTER.add_route(timer_ipc_port.to_opaque(), box move |message| { + let event = message.to().unwrap(); + timer_tx.send((worker_for_route.clone(), event)).unwrap(); + }); + + let global = ServiceWorkerGlobalScope::new( + init, url, id, devtools_mpsc_port, runtime, + parent_sender.clone(), own_sender, receiver, + timer_ipc_chan, timer_rx, client); + // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter + // registration (#6631), so we instead use a random number and cross our fingers. + let scope = global.upcast::(); + + unsafe { + // Handle interrupt requests + JS_SetInterruptCallback(scope.runtime(), Some(interrupt_callback)); + } + + if scope.is_closing() { + return; + } + + { + let _ar = AutoWorkerReset::new(global.r(), worker); + scope.execute_script(DOMString::from(source)); + } + + + let reporter_name = format!("service-worker-reporter-{}", random::()); + scope.mem_profiler_chan().run_with_memory_reporting(|| { + // Service workers are time limited + let sw_lifetime = Instant::now(); + let sw_lifetime_timeout = prefs::get_pref("dom.serviceworker.timeout_seconds").as_u64().unwrap(); + while let Ok(event) = global.receive_event() { + if scope.is_closing() { + break; + } + global.handle_event(event); + if sw_lifetime.elapsed().as_secs() == sw_lifetime_timeout { + break; + } + } + }, reporter_name, parent_sender, CommonScriptMsg::CollectReports); + }); + } + + fn handle_event(&self, event: MixedMessage) { + match event { + MixedMessage::FromDevtools(msg) => { + let global_ref = GlobalRef::Worker(self.upcast()); + match msg { + DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => + devtools::handle_evaluate_js(&global_ref, 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(&global_ref, bool_val), + _ => debug!("got an unusable devtools control message inside the worker!"), + } + }, + MixedMessage::FromScheduler((linked_worker, timer_event)) => { + match timer_event { + TimerEvent(TimerSource::FromWorker, id) => { + let _ar = AutoWorkerReset::new(self, linked_worker); + let scope = self.upcast::(); + scope.handle_fire_timer(id); + }, + TimerEvent(_, _) => { + panic!("The service worker received a TimerEvent from a window.") + } + } + } + MixedMessage::FromServiceWorker((linked_worker, msg)) => { + let _ar = AutoWorkerReset::new(self, linked_worker); + self.handle_script_event(msg); + }, + MixedMessage::FromNetwork(network_sender) => { + // We send None as of now + let _ = network_sender.send(None); + } + } + } + + fn handle_script_event(&self, msg: WorkerScriptMsg) { + match msg { + WorkerScriptMsg::DOMMessage(data) => { + let scope = self.upcast::(); + let target = self.upcast(); + let _ac = JSAutoCompartment::new(scope.get_cx(), + scope.reflector().get_jsobject().get()); + let mut message = RootedValue::new(scope.get_cx(), UndefinedValue()); + data.read(GlobalRef::Worker(scope), message.handle_mut()); + MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle()); + }, + WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => { + runnable.handler() + }, + WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => { + LiveDOMReferences::cleanup(addr); + }, + WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => { + let scope = self.upcast::(); + let cx = scope.get_cx(); + let path_seg = format!("url({})", scope.get_url()); + let reports = get_reports(cx, path_seg); + reports_chan.send(reports); + }, + } + } + + #[allow(unsafe_code)] + fn receive_event(&self) -> Result { + let scope = self.upcast::(); + let worker_port = &self.receiver; + let timer_event_port = &self.timer_event_port; + let devtools_port = scope.from_devtools_receiver(); + let msg_port = scope.custom_message_port(); + + let sel = Select::new(); + let mut worker_handle = sel.handle(worker_port); + let mut timer_event_handle = sel.handle(timer_event_port); + let mut devtools_handle = sel.handle(devtools_port); + let mut msg_port_handle = sel.handle(msg_port); + unsafe { + worker_handle.add(); + timer_event_handle.add(); + if scope.from_devtools_sender().is_some() { + devtools_handle.add(); + } + msg_port_handle.add(); + } + let ret = sel.wait(); + if ret == worker_handle.id() { + Ok(MixedMessage::FromServiceWorker(try!(worker_port.recv()))) + } else if ret == timer_event_handle.id() { + Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv()))) + } else if ret == devtools_handle.id() { + Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) + } else if ret == msg_port_handle.id() { + Ok(MixedMessage::FromNetwork(try!(msg_port.recv()))) + } else { + panic!("unexpected select result!") + } + } + + pub fn script_chan(&self) -> Box { + box WorkerThreadWorkerChan { + sender: self.own_sender.clone(), + worker: self.worker.borrow().as_ref().unwrap().clone(), + } + } + + pub fn pipeline(&self) -> PipelineId { + self.id + } + + pub fn process_event(&self, msg: CommonScriptMsg) { + self.handle_script_event(WorkerScriptMsg::Common(msg)); + } + + pub fn new_script_pair(&self) -> (Box, Box) { + let (tx, rx) = channel(); + let chan = box SendableWorkerScriptChan { + sender: tx, + worker: self.worker.borrow().as_ref().unwrap().clone(), + }; + (chan, box rx) + } +} + +#[allow(unsafe_code)] +unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool { + let global = global_root_from_context(cx); + let worker = match global.r() { + GlobalRef::Worker(w) => w, + _ => panic!("global for worker is not a worker scope") + }; + assert!(worker.is::()); + + // A false response causes the script to terminate + !worker.is_closing() +} + +impl ServiceWorkerGlobalScopeMethods for ServiceWorkerGlobalScope { + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-global-scope-onmessage-attribute + event_handler!(message, GetOnmessage, SetOnmessage); +} diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs new file mode 100644 index 000000000000..358331afffc8 --- /dev/null +++ b/components/script/dom/serviceworkerregistration.rs @@ -0,0 +1,67 @@ +/* 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 dom::bindings::codegen::Bindings::ServiceWorkerBinding::ServiceWorkerState; +use dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::{ServiceWorkerRegistrationMethods, Wrap}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::USVString; +use dom::eventtarget::EventTarget; +use dom::serviceworker::ServiceWorker; +use dom::serviceworkercontainer::Controllable; +use url::Url; + +#[dom_struct] +pub struct ServiceWorkerRegistration { + eventtarget: EventTarget, + active: Option>, + installing: Option>, + waiting: Option>, + scope: String, +} + +impl ServiceWorkerRegistration { + fn new_inherited(active_sw: &ServiceWorker, scope: String) -> ServiceWorkerRegistration { + ServiceWorkerRegistration { + eventtarget: EventTarget::new_inherited(), + active: Some(JS::from_ref(active_sw)), + installing: None, + waiting: None, + scope: scope + } + } + #[allow(unrooted_must_root)] + pub fn new(global: GlobalRef, + script_url: Url, + scope: String, + container: &Controllable) -> Root { + let active_worker = ServiceWorker::init_service_worker(global, script_url, 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) + } +} + +impl ServiceWorkerRegistrationMethods for ServiceWorkerRegistration { + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-registration-installing-attribute + fn GetInstalling(&self) -> Option> { + self.installing.as_ref().map(|sw| Root::from_ref(&**sw)) + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-registration-active-attribute + fn GetActive(&self) -> Option> { + self.active.as_ref().map(|sw| Root::from_ref(&**sw)) + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-registration-waiting-attribute + fn GetWaiting(&self) -> Option> { + self.waiting.as_ref().map(|sw| Root::from_ref(&**sw)) + } + + // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-registration-scope-attribute + fn Scope(&self) -> USVString { + USVString(self.scope.clone()) + } +} diff --git a/components/script/dom/webidls/Client.webidl b/components/script/dom/webidls/Client.webidl new file mode 100644 index 000000000000..b74218aa7876 --- /dev/null +++ b/components/script/dom/webidls/Client.webidl @@ -0,0 +1,21 @@ +/* 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/. */ + +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#client + +// [Exposed=ServiceWorker] +[Pref="dom.serviceworker.enabled"] +interface Client { + readonly attribute USVString url; + readonly attribute FrameType frameType; + readonly attribute DOMString id; + //void postMessage(any message, optional sequence transfer); +}; + +enum FrameType { + "auxiliary", + "top-level", + "nested", + "none" +}; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index 4d9ead9f7f95..ca9b4a36a4a2 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -31,6 +31,11 @@ interface NavigatorBluetooth { readonly attribute Bluetooth bluetooth; }; +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#navigator-service-worker +partial interface Navigator { + [SameObject, Pref="dom.serviceworker.enabled"] readonly attribute ServiceWorkerContainer serviceWorker; +}; + // https://html.spec.whatwg.org/multipage/#navigatorlanguage [NoInterfaceObject/*, Exposed=Window,Worker*/] interface NavigatorLanguage { diff --git a/components/script/dom/webidls/ServiceWorker.webidl b/components/script/dom/webidls/ServiceWorker.webidl new file mode 100644 index 000000000000..4718b164ce0c --- /dev/null +++ b/components/script/dom/webidls/ServiceWorker.webidl @@ -0,0 +1,25 @@ +/* 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/. */ + +// http://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-obj +//[Exposed=(Window,Worker)] +[Pref="dom.serviceworker.enabled"] +interface ServiceWorker : EventTarget { + readonly attribute USVString scriptURL; + readonly attribute ServiceWorkerState state; + //[Throws] void postMessage(any message/*, optional sequence transfer*/); + + // event + attribute EventHandler onstatechange; +}; + +ServiceWorker implements AbstractWorker; + +enum ServiceWorkerState { + "installing", + "installed", + "activating", + "activated", + "redundant" +}; diff --git a/components/script/dom/webidls/ServiceWorkerContainer.webidl b/components/script/dom/webidls/ServiceWorkerContainer.webidl new file mode 100644 index 000000000000..3c28bb8994d1 --- /dev/null +++ b/components/script/dom/webidls/ServiceWorkerContainer.webidl @@ -0,0 +1,27 @@ +/* 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/. */ + +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-container +// [Exposed=(Window,Worker)] +[Pref="dom.serviceworker.enabled"] +interface ServiceWorkerContainer : EventTarget { + [Unforgeable] readonly attribute ServiceWorker? controller; + //[SameObject] readonly attribute Promise ready; + + [NewObject, Throws] ServiceWorkerRegistration register(USVString scriptURL, optional RegistrationOptions options); + + //[NewObject] /*Promise*/ any getRegistration(optional USVString clientURL = ""); + //[NewObject] /* Promise */> getRegistrations(); + + + // events + //attribute EventHandler oncontrollerchange; + //attribute EventHandler onerror; + //attribute EventHandler onmessage; // event.source of message events is ServiceWorker object +}; + +dictionary RegistrationOptions { + USVString scope; + //WorkerType type = "classic"; +}; diff --git a/components/script/dom/webidls/ServiceWorkerGlobalScope.webidl b/components/script/dom/webidls/ServiceWorkerGlobalScope.webidl new file mode 100644 index 000000000000..0b630fb3b1e3 --- /dev/null +++ b/components/script/dom/webidls/ServiceWorkerGlobalScope.webidl @@ -0,0 +1,23 @@ +/* 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/. */ + +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-global-scope + +[Global, Pref="dom.serviceworker.enabled"/*=(Worker,ServiceWorker), Exposed=ServiceWorker*/] +interface ServiceWorkerGlobalScope : WorkerGlobalScope { + // A container for a list of Client objects that correspond to + // browsing contexts (or shared workers) that are on the origin of this SW + //[SameObject] readonly attribute Clients clients; + //[SameObject] readonly attribute ServiceWorkerRegistration registration; + + //[NewObject] Promise skipWaiting(); + + //attribute EventHandler oninstall; + //attribute EventHandler onactivate; + //attribute EventHandler onfetch; + //attribute EventHandler onforeignfetch; + + // event + attribute EventHandler onmessage; // event.source of the message events is Client object +}; diff --git a/components/script/dom/webidls/ServiceWorkerRegistration.webidl b/components/script/dom/webidls/ServiceWorkerRegistration.webidl new file mode 100644 index 000000000000..c6c36f224759 --- /dev/null +++ b/components/script/dom/webidls/ServiceWorkerRegistration.webidl @@ -0,0 +1,20 @@ +/* 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/. */ + +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-registration-obj +//[Exposed=(Window,Worker)] +[Pref="dom.serviceworker.enabled"] +interface ServiceWorkerRegistration : EventTarget { + [Unforgeable] readonly attribute ServiceWorker? installing; + [Unforgeable] readonly attribute ServiceWorker? waiting; + [Unforgeable] readonly attribute ServiceWorker? active; + + readonly attribute USVString scope; + + // [NewObject] Promise update(); + // [NewObject] Promise unregister(); + + // event + // attribute EventHandler onupdatefound; +}; diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 11802a8e0b2f..5762e2aa63f6 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -2,7 +2,9 @@ * 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_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; + +use dom::abstractworker::{SimpleWorkerErrorHandler, SharedRt, WorkerErrorHandler}; +use dom::abstractworker::{WorkerScriptLoadOrigin, WorkerScriptMsg}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::WorkerBinding; use dom::bindings::codegen::Bindings::WorkerBinding::WorkerMethods; @@ -14,24 +16,19 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; -use dom::dedicatedworkerglobalscope::{DedicatedWorkerGlobalScope, WorkerScriptMsg}; +use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::errorevent::ErrorEvent; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::messageevent::MessageEvent; -use dom::workerglobalscope::WorkerGlobalScopeInit; +use dom::workerglobalscope::prepare_workerscope_init; use ipc_channel::ipc; -use js::jsapi::{HandleValue, JSContext, JSRuntime, RootedValue}; -use js::jsapi::{JSAutoCompartment, JS_RequestInterruptCallback}; +use js::jsapi::{HandleValue, JSContext, RootedValue, JSAutoCompartment}; use js::jsval::UndefinedValue; -use js::rust::Runtime; -use msg::constellation_msg::{PipelineId, ReferrerPolicy}; -use net_traits::{RequestSource, LoadOrigin}; use script_thread::Runnable; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; -use url::Url; pub type TrustedWorkerAddress = Trusted; @@ -48,29 +45,6 @@ pub struct Worker { runtime: Arc>> } -#[derive(Clone)] -pub struct WorkerScriptLoadOrigin { - referrer_url: Option, - referrer_policy: Option, - request_source: RequestSource, - pipeline_id: Option -} - -impl LoadOrigin for WorkerScriptLoadOrigin { - fn referrer_url(&self) -> Option { - self.referrer_url.clone() - } - fn referrer_policy(&self) -> Option { - self.referrer_policy.clone() - } - fn request_source(&self) -> RequestSource { - self.request_source.clone() - } - fn pipeline_id(&self) -> Option { - self.pipeline_id.clone() - } -} - impl Worker { fn new_inherited(sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, closing: Arc) -> Worker { @@ -99,15 +73,10 @@ impl Worker { Err(_) => return Err(Error::Syntax), }; - let resource_threads = global.resource_threads(); - let constellation_chan = global.constellation_chan().clone(); - let scheduler_chan = global.scheduler_chan().clone(); - let (sender, receiver) = channel(); let closing = Arc::new(AtomicBool::new(false)); let worker = Worker::new(global, sender.clone(), closing.clone()); let worker_ref = Trusted::new(worker.r()); - let worker_id = global.get_next_worker_id(); let worker_load_origin = WorkerScriptLoadOrigin { referrer_url: None, @@ -117,34 +86,12 @@ impl Worker { }; let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); - let optional_sender = match global.devtools_chan() { - Some(ref chan) => { - let pipeline_id = global.pipeline(); - let title = format!("Worker for {}", worker_url); - let page_info = DevtoolsPageInfo { - title: title, - url: worker_url.clone(), - }; - chan.send(ScriptToDevtoolsControlMsg::NewGlobal((pipeline_id, Some(worker_id)), - devtools_sender.clone(), - page_info)).unwrap(); - Some(devtools_sender) - }, - None => None, - }; - let init = WorkerGlobalScopeInit { - resource_threads: resource_threads, - mem_profiler_chan: global.mem_profiler_chan().clone(), - time_profiler_chan: global.time_profiler_chan().clone(), - to_devtools_sender: global.devtools_chan(), - from_devtools_sender: optional_sender, - constellation_chan: constellation_chan, - scheduler_chan: scheduler_chan, - panic_chan: global.panic_chan().clone(), - worker_id: worker_id, - closing: closing, - }; + let init = prepare_workerscope_init(global, + "Worker".to_owned(), + worker_url.clone(), + devtools_sender.clone(), + closing); DedicatedWorkerGlobalScope::run_worker_scope( init, worker_url, global.pipeline(), devtools_receiver, worker.runtime.clone(), worker_ref, @@ -245,76 +192,18 @@ impl Runnable for WorkerMessageHandler { } } -pub struct SimpleWorkerErrorHandler { - addr: TrustedWorkerAddress, -} - -impl SimpleWorkerErrorHandler { - pub fn new(addr: TrustedWorkerAddress) -> SimpleWorkerErrorHandler { - SimpleWorkerErrorHandler { - addr: addr - } - } -} - -impl Runnable for SimpleWorkerErrorHandler { - fn handler(self: Box) { +impl Runnable for SimpleWorkerErrorHandler { + #[allow(unrooted_must_root)] + fn handler(self: Box>) { let this = *self; Worker::dispatch_simple_error(this.addr); } } -pub struct WorkerErrorHandler { - addr: TrustedWorkerAddress, - msg: DOMString, - file_name: DOMString, - line_num: u32, - col_num: u32, -} - -impl WorkerErrorHandler { - pub fn new(addr: TrustedWorkerAddress, msg: DOMString, file_name: DOMString, line_num: u32, col_num: u32) - -> WorkerErrorHandler { - WorkerErrorHandler { - addr: addr, - msg: msg, - file_name: file_name, - line_num: line_num, - col_num: col_num, - } - } -} - -impl Runnable for WorkerErrorHandler { - fn handler(self: Box) { +impl Runnable for WorkerErrorHandler { + #[allow(unrooted_must_root)] + fn handler(self: Box>) { let this = *self; Worker::handle_error_message(this.addr, this.msg, this.file_name, this.line_num, this.col_num); } } - -#[derive(Copy, Clone)] -pub struct SharedRt { - rt: *mut JSRuntime -} - -impl SharedRt { - pub fn new(rt: &Runtime) -> SharedRt { - SharedRt { - rt: rt.rt() - } - } - - #[allow(unsafe_code)] - pub fn request_interrupt(&self) { - unsafe { - JS_RequestInterruptCallback(self.rt); - } - } - - pub fn rt(&self) -> *mut JSRuntime { - self.rt - } -} - -#[allow(unsafe_code)] -unsafe impl Send for SharedRt {} diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 95c476649b85..06b3894ded3f 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -2,7 +2,7 @@ * 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_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; +use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId, DevtoolsPageInfo}; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception}; @@ -15,6 +15,7 @@ use dom::console::Console; use dom::crypto::Crypto; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::eventtarget::EventTarget; +use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use dom::window::{base64_atob, base64_btoa}; use dom::workerlocation::WorkerLocation; use dom::workernavigator::WorkerNavigator; @@ -57,6 +58,44 @@ pub struct WorkerGlobalScopeInit { pub closing: Arc, } +pub fn prepare_workerscope_init(global: GlobalRef, + worker_type: String, + worker_url: Url, + devtools_sender: IpcSender, + closing: Arc) -> WorkerGlobalScopeInit { + let worker_id = global.get_next_worker_id(); + let optional_sender = match global.devtools_chan() { + Some(ref chan) => { + let pipeline_id = global.pipeline(); + let title = format!("{} for {}", worker_type, worker_url); + let page_info = DevtoolsPageInfo { + title: title, + url: worker_url, + }; + chan.send(ScriptToDevtoolsControlMsg::NewGlobal((pipeline_id, Some(worker_id)), + devtools_sender.clone(), + page_info)).unwrap(); + Some(devtools_sender) + }, + None => None, + }; + + let init = WorkerGlobalScopeInit { + resource_threads: global.resource_threads(), + mem_profiler_chan: global.mem_profiler_chan().clone(), + to_devtools_sender: global.devtools_chan(), + time_profiler_chan: global.time_profiler_chan().clone(), + from_devtools_sender: optional_sender, + constellation_chan: global.constellation_chan().clone(), + panic_chan: global.panic_chan().clone(), + scheduler_chan: global.scheduler_chan().clone(), + worker_id: worker_id, + closing: closing, + }; + + init +} + // https://html.spec.whatwg.org/multipage/#the-workerglobalscope-common-interface #[dom_struct] pub struct WorkerGlobalScope { @@ -392,36 +431,49 @@ impl WorkerGlobalScope { pub fn script_chan(&self) -> Box { let dedicated = self.downcast::(); - match dedicated { - Some(dedicated) => dedicated.script_chan(), - None => panic!("need to implement a sender for SharedWorker"), + let service_worker = self.downcast::(); + 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") } } pub fn pipeline(&self) -> PipelineId { - let dedicated = - self.downcast::(); - match dedicated { - Some(dedicated) => dedicated.pipeline(), - None => panic!("need to add a pipeline for SharedWorker"), + let dedicated = self.downcast::(); + let service_worker = self.downcast::(); + if let Some(dedicated) = dedicated { + return dedicated.pipeline(); + } else if let Some(service_worker) = service_worker { + return service_worker.pipeline(); + } else { + panic!("need to implement a sender for SharedWorker") } } pub fn new_script_pair(&self) -> (Box, Box) { - let dedicated = - self.downcast::(); - match dedicated { - Some(dedicated) => dedicated.new_script_pair(), - None => panic!("need to implement creating isolated event loops for SharedWorker"), + let dedicated = self.downcast::(); + let service_worker = self.downcast::(); + if let Some(dedicated) = dedicated { + return dedicated.new_script_pair(); + } else if let Some(service_worker) = service_worker { + return service_worker.new_script_pair(); + } else { + panic!("need to implement a sender for SharedWorker") } } pub fn process_event(&self, msg: CommonScriptMsg) { - let dedicated = - self.downcast::(); - match dedicated { - Some(dedicated) => dedicated.process_event(msg), - None => panic!("need to implement processing single events for SharedWorker"), + let dedicated = self.downcast::(); + let service_worker = self.downcast::(); + if let Some(dedicated) = dedicated { + return dedicated.process_event(msg); + } else if let Some(service_worker) = service_worker { + return service_worker.process_event(msg); + } else { + panic!("need to implement a sender for SharedWorker") } } diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 061b80502b83..25fdcb579cb2 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -69,6 +69,7 @@ pub enum ScriptThreadEventCategory { UpdateReplacedElement, WebSocketEvent, WorkerEvent, + ServiceWorkerEvent } /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 8e358156e887..35ae783f5ff8 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -40,6 +40,8 @@ use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlanchorelement::HTMLAnchorElement; use dom::node::{Node, NodeDamage, window_from_node}; +use dom::serviceworker::TrustedServiceWorkerAddress; +use dom::serviceworkerregistration::ServiceWorkerRegistration; use dom::servohtmlparser::ParserContext; use dom::uievent::UIEvent; use dom::window::{ReflowReason, ScriptHelpers, Window}; @@ -86,7 +88,7 @@ use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSou use script_traits::{TouchEventType, TouchId}; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::option::Option; use std::rc::Rc; use std::result::Result; @@ -264,6 +266,12 @@ impl ScriptPort for Receiver<(TrustedWorkerAddress, MainThreadScriptMsg)> { } } +impl ScriptPort for Receiver<(TrustedServiceWorkerAddress, CommonScriptMsg)> { + fn recv(&self) -> Result { + self.recv().map(|(_, msg)| msg).map_err(|_| ()) + } +} + /// Encapsulates internal communication of shared messages within the script thread. #[derive(JSTraceable)] pub struct SendableMainThreadScriptChan(pub Sender); @@ -308,6 +316,8 @@ pub struct ScriptThread { browsing_context: MutNullableHeap>, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell>, + /// A map to store service worker registrations for a given origin + registration_map: DOMRefCell>>, /// A handle to the image cache thread. image_cache_thread: ImageCacheThread, /// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if @@ -486,6 +496,14 @@ impl ScriptThread { }) } + // stores a service worker registration + pub fn set_registration(scope_url: Url, registration:&ServiceWorkerRegistration) { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.borrow().unwrap() }; + script_thread.handle_serviceworker_registration(scope_url, registration); + }); + } + pub fn parsing_complete(id: PipelineId) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.borrow().unwrap() }; @@ -548,6 +566,7 @@ impl ScriptThread { ScriptThread { browsing_context: MutNullableHeap::new(None), incomplete_loads: DOMRefCell::new(vec!()), + registration_map: DOMRefCell::new(HashMap::new()), image_cache_thread: state.image_cache_thread, image_cache_channel: ImageCacheChan(ipc_image_cache_channel), @@ -865,6 +884,7 @@ impl ScriptThread { ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent, ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent, ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent, + ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent }; profile(profiler_cat, None, self.time_profiler_chan.clone(), f) } else { @@ -1340,6 +1360,10 @@ impl ScriptThread { } } + fn handle_serviceworker_registration(&self, scope: Url, registration: &ServiceWorkerRegistration) { + self.registration_map.borrow_mut().insert(scope, JS::from_ref(registration)); + } + /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index da9c49f88342..aa278960368a 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -272,7 +272,7 @@ pub enum TimerSource { /// The event was requested from a window (ScriptThread). FromWindow(PipelineId), /// The event was requested from a worker (DedicatedGlobalWorkerScope). - FromWorker + FromWorker, } /// The id to be used for a TimerEvent is defined by the corresponding TimerEventRequest. diff --git a/components/util/prefs.rs b/components/util/prefs.rs index 83363f93c35c..9c713df02e99 100644 --- a/components/util/prefs.rs +++ b/components/util/prefs.rs @@ -65,6 +65,13 @@ impl PrefValue { _ => None, } } + + pub fn as_u64(&self) -> Option { + match *self { + PrefValue::Number(x) => Some(x as u64), + _ => None, + } + } } impl ToJson for PrefValue { diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py index 48f7a0705bce..9a824fc17543 100644 --- a/python/tidy/servo_tidy/tidy.py +++ b/python/tidy/servo_tidy/tidy.py @@ -522,6 +522,7 @@ def check_webidl_spec(file_name, contents): "//w3c.github.io", "//heycam.github.io/webidl", "//webbluetoothcg.github.io/web-bluetooth/", + "//slightlyoff.github.io/ServiceWorker/spec/service_worker/", # Not a URL "// This interface is entirely internal to Servo, and should not be" + " accessible to\n// web pages." diff --git a/resources/prefs.json b/resources/prefs.json index c3aea5eae364..0289050a1625 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -3,6 +3,7 @@ "dom.forcetouch.enabled": false, "dom.mouseevent.which.enabled": false, "dom.mozbrowser.enabled": false, + "dom.serviceworker.timeout_seconds": 60, "dom.testbinding.enabled": false, "gfx.webrender.enabled": false, "js.baseline.enabled": true, diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 7fabf3c762c1..22f228b99ccf 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -6898,6 +6898,12 @@ "url": "/_mozilla/mozilla/sequence-hole.html" } ], + "mozilla/service-workers/service-worker-registration.html": [ + { + "path": "mozilla/service-workers/service-worker-registration.html", + "url": "/_mozilla/mozilla/service-workers/service-worker-registration.html" + } + ], "mozilla/storage.html": [ { "path": "mozilla/storage.html", diff --git a/tests/wpt/mozilla/meta/mozilla/service-workers/__dir__.ini b/tests/wpt/mozilla/meta/mozilla/service-workers/__dir__.ini new file mode 100644 index 000000000000..e4456cb47568 --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/service-workers/__dir__.ini @@ -0,0 +1 @@ +prefs: ["dom.serviceworker.enabled:true"] diff --git a/tests/wpt/mozilla/tests/mozilla/service-workers/resources/sw.js b/tests/wpt/mozilla/tests/mozilla/service-workers/resources/sw.js new file mode 100644 index 000000000000..53ed1bc7e117 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/service-workers/resources/sw.js @@ -0,0 +1 @@ +console.log("Hey Servo; lets cache something! :)"); diff --git a/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.html b/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.html new file mode 100644 index 000000000000..eba63a9ba749 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.html @@ -0,0 +1,52 @@ + + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/service-workers/sw.js b/tests/wpt/mozilla/tests/mozilla/service-workers/sw.js new file mode 100644 index 000000000000..53ed1bc7e117 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/service-workers/sw.js @@ -0,0 +1 @@ +console.log("Hey Servo; lets cache something! :)");