mirror of
https://github.com/servo/servo.git
synced 2025-06-06 00:25:37 +00:00
implement related sw interface and register method
This commit is contained in:
parent
0594d58bc8
commit
15a2064c0d
33 changed files with 1285 additions and 217 deletions
|
@ -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)
|
||||
|
|
|
@ -80,6 +80,7 @@ pub enum ProfilerCategory {
|
|||
ScriptUpdateReplacedElement,
|
||||
ScriptWebSocketEvent,
|
||||
ScriptWorkerEvent,
|
||||
ScriptServiceWorkerEvent,
|
||||
ApplicationHeartbeat,
|
||||
}
|
||||
|
||||
|
|
104
components/script/dom/abstractworker.rs
Normal file
104
components/script/dom/abstractworker.rs
Normal file
|
@ -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<Url>,
|
||||
pub referrer_policy: Option<ReferrerPolicy>,
|
||||
pub request_source: RequestSource,
|
||||
pub pipeline_id: Option<PipelineId>
|
||||
}
|
||||
|
||||
impl LoadOrigin for WorkerScriptLoadOrigin {
|
||||
fn referrer_url(&self) -> Option<Url> {
|
||||
self.referrer_url.clone()
|
||||
}
|
||||
fn referrer_policy(&self) -> Option<ReferrerPolicy> {
|
||||
self.referrer_policy.clone()
|
||||
}
|
||||
fn request_source(&self) -> RequestSource {
|
||||
self.request_source.clone()
|
||||
}
|
||||
fn pipeline_id(&self) -> Option<PipelineId> {
|
||||
self.pipeline_id.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimpleWorkerErrorHandler<T: Reflectable> {
|
||||
pub addr: Trusted<T>,
|
||||
}
|
||||
|
||||
impl<T: Reflectable> SimpleWorkerErrorHandler<T> {
|
||||
pub fn new(addr: Trusted<T>) -> SimpleWorkerErrorHandler<T> {
|
||||
SimpleWorkerErrorHandler {
|
||||
addr: addr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkerErrorHandler<T: Reflectable> {
|
||||
pub addr: Trusted<T>,
|
||||
pub msg: DOMString,
|
||||
pub file_name: DOMString,
|
||||
pub line_num: u32,
|
||||
pub col_num: u32,
|
||||
}
|
||||
|
||||
impl<T: Reflectable> WorkerErrorHandler<T> {
|
||||
pub fn new(addr: Trusted<T>, msg: DOMString, file_name: DOMString, line_num: u32, col_num: u32)
|
||||
-> WorkerErrorHandler<T> {
|
||||
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 {}
|
65
components/script/dom/abstractworkerglobalscope.rs
Normal file
65
components/script/dom/abstractworkerglobalscope.rs
Normal file
|
@ -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<T: Reflectable> {
|
||||
pub sender: Sender<(Trusted<T>, CommonScriptMsg)>,
|
||||
pub worker: Trusted<T>,
|
||||
}
|
||||
|
||||
impl<T: Reflectable + 'static> ScriptChan for SendableWorkerScriptChan<T> {
|
||||
fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
|
||||
self.sender.send((self.worker.clone(), msg)).map_err(|_| ())
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<ScriptChan + Send> {
|
||||
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<T: Reflectable> {
|
||||
pub sender: Sender<(Trusted<T>, WorkerScriptMsg)>,
|
||||
pub worker: Trusted<T>,
|
||||
}
|
||||
|
||||
impl<T: Reflectable + 'static> ScriptChan for WorkerThreadWorkerChan<T> {
|
||||
fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
|
||||
self.sender
|
||||
.send((self.worker.clone(), WorkerScriptMsg::Common(msg)))
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<ScriptChan + Send> {
|
||||
box WorkerThreadWorkerChan {
|
||||
sender: self.sender.clone(),
|
||||
worker: self.worker.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> ScriptPort for Receiver<(Trusted<T>, WorkerScriptMsg)> {
|
||||
fn recv(&self) -> Result<CommonScriptMsg, ()> {
|
||||
match self.recv().map(|(_, msg)| msg) {
|
||||
Ok(WorkerScriptMsg::Common(script_msg)) => Ok(script_msg),
|
||||
Ok(WorkerScriptMsg::DOMMessage(_)) => panic!("unexpected worker event message!"),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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<ScriptChan + Send> {
|
||||
#[inline]
|
||||
|
|
59
components/script/dom/client.rs
Normal file
59
components/script/dom/client.rs
Normal file
|
@ -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<JS<ServiceWorker>>,
|
||||
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<Client> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<ScriptChan + Send> {
|
||||
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<ScriptChan + Send> {
|
||||
box WorkerThreadWorkerChan {
|
||||
sender: self.sender.clone(),
|
||||
worker: self.worker.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptPort for Receiver<(TrustedWorkerAddress, WorkerScriptMsg)> {
|
||||
fn recv(&self) -> Result<CommonScriptMsg, ()> {
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<JS<Bluetooth>>,
|
||||
plugins: MutNullableHeap<JS<PluginArray>>,
|
||||
mime_types: MutNullableHeap<JS<MimeTypeArray>>,
|
||||
serviceWorker: MutNullableHeap<JS<ServiceWorkerContainer>>,
|
||||
}
|
||||
|
||||
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<ServiceWorkerContainer> {
|
||||
self.serviceWorker.or_init(|| ServiceWorkerContainer::new(self.global().r()))
|
||||
}
|
||||
}
|
||||
|
|
172
components/script/dom/serviceworker.rs
Normal file
172
components/script/dom/serviceworker.rs
Normal file
|
@ -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<ServiceWorker>;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct ServiceWorker {
|
||||
eventtarget: EventTarget,
|
||||
script_url: DOMRefCell<String>,
|
||||
state: Cell<ServiceWorkerState>,
|
||||
closing: Arc<AtomicBool>,
|
||||
#[ignore_heap_size_of = "Defined in std"]
|
||||
sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
#[ignore_heap_size_of = "Defined in rust-mozjs"]
|
||||
runtime: Arc<Mutex<Option<SharedRt>>>,
|
||||
skip_waiting: Cell<bool>
|
||||
}
|
||||
|
||||
impl ServiceWorker {
|
||||
fn new_inherited(sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
closing: Arc<AtomicBool>,
|
||||
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<AtomicBool>,
|
||||
sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
script_url: &str,
|
||||
skip_waiting: bool) -> Root<ServiceWorker> {
|
||||
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::<EventTarget>().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::<Event>().fire(worker.upcast());
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn init_service_worker(global: GlobalRef,
|
||||
script_url: Url,
|
||||
skip_waiting: bool) -> Root<ServiceWorker> {
|
||||
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<ServiceWorker> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn handler(self: Box<SimpleWorkerErrorHandler<ServiceWorker>>) {
|
||||
let this = *self;
|
||||
ServiceWorker::dispatch_simple_error(this.addr);
|
||||
}
|
||||
}
|
||||
|
||||
impl Runnable for WorkerErrorHandler<ServiceWorker> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn handler(self: Box<WorkerErrorHandler<ServiceWorker>>) {
|
||||
let this = *self;
|
||||
ServiceWorker::handle_error_message(this.addr, this.msg, this.file_name, this.line_num, this.col_num);
|
||||
}
|
||||
}
|
104
components/script/dom/serviceworkercontainer.rs
Normal file
104
components/script/dom/serviceworkercontainer.rs
Normal file
|
@ -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<JS<ServiceWorker>>,
|
||||
}
|
||||
|
||||
impl ServiceWorkerContainer {
|
||||
fn new_inherited() -> ServiceWorkerContainer {
|
||||
ServiceWorkerContainer {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
controller: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(global: GlobalRef) -> Root<ServiceWorkerContainer> {
|
||||
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<Root<ServiceWorker>> {
|
||||
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<Root<ServiceWorkerRegistration>> {
|
||||
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)
|
||||
}
|
||||
}
|
382
components/script/dom/serviceworkerglobalscope.rs
Normal file
382
components/script/dom/serviceworkerglobalscope.rs
Normal file
|
@ -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<TrustedServiceWorkerAddress>,
|
||||
}
|
||||
|
||||
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<Option<CustomResponse>>),
|
||||
}
|
||||
|
||||
#[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<T> has unclear ownership like JS<T>"]
|
||||
worker: DOMRefCell<Option<TrustedServiceWorkerAddress>>,
|
||||
#[ignore_heap_size_of = "Can't measure trait objects"]
|
||||
/// Sender to the parent thread.
|
||||
parent_sender: Box<ScriptChan + Send>,
|
||||
#[ignore_heap_size_of = "Defined in std"]
|
||||
service_worker_client: Trusted<Client>
|
||||
}
|
||||
|
||||
impl ServiceWorkerGlobalScope {
|
||||
fn new_inherited(init: WorkerGlobalScopeInit,
|
||||
worker_url: Url,
|
||||
id: PipelineId,
|
||||
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
|
||||
runtime: Runtime,
|
||||
parent_sender: Box<ScriptChan + Send>,
|
||||
own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
timer_event_chan: IpcSender<TimerEvent>,
|
||||
timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>,
|
||||
client: Trusted<Client>)
|
||||
-> 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<DevtoolScriptControlMsg>,
|
||||
runtime: Runtime,
|
||||
parent_sender: Box<ScriptChan + Send>,
|
||||
own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
timer_event_chan: IpcSender<TimerEvent>,
|
||||
timer_event_port: Receiver<(TrustedServiceWorkerAddress, TimerEvent)>,
|
||||
client: Trusted<Client>)
|
||||
-> Root<ServiceWorkerGlobalScope> {
|
||||
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<DevtoolScriptControlMsg>,
|
||||
main_thread_rt: Arc<Mutex<Option<SharedRt>>>,
|
||||
worker: TrustedServiceWorkerAddress,
|
||||
parent_sender: Box<ScriptChan + Send>,
|
||||
own_sender: Sender<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
receiver: Receiver<(TrustedServiceWorkerAddress, WorkerScriptMsg)>,
|
||||
client: Trusted<Client>,
|
||||
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::<WorkerGlobalScope>();
|
||||
|
||||
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::<u64>());
|
||||
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::<WorkerGlobalScope>();
|
||||
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::<WorkerGlobalScope>();
|
||||
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::<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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn receive_event(&self) -> Result<MixedMessage, RecvError> {
|
||||
let scope = self.upcast::<WorkerGlobalScope>();
|
||||
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<ScriptChan + Send> {
|
||||
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<ScriptChan + Send>, Box<ScriptPort + Send>) {
|
||||
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::<ServiceWorkerGlobalScope>());
|
||||
|
||||
// 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);
|
||||
}
|
67
components/script/dom/serviceworkerregistration.rs
Normal file
67
components/script/dom/serviceworkerregistration.rs
Normal file
|
@ -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<JS<ServiceWorker>>,
|
||||
installing: Option<JS<ServiceWorker>>,
|
||||
waiting: Option<JS<ServiceWorker>>,
|
||||
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<ServiceWorkerRegistration> {
|
||||
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<Root<ServiceWorker>> {
|
||||
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<Root<ServiceWorker>> {
|
||||
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<Root<ServiceWorker>> {
|
||||
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())
|
||||
}
|
||||
}
|
21
components/script/dom/webidls/Client.webidl
Normal file
21
components/script/dom/webidls/Client.webidl
Normal file
|
@ -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<Transferable> transfer);
|
||||
};
|
||||
|
||||
enum FrameType {
|
||||
"auxiliary",
|
||||
"top-level",
|
||||
"nested",
|
||||
"none"
|
||||
};
|
|
@ -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 {
|
||||
|
|
25
components/script/dom/webidls/ServiceWorker.webidl
Normal file
25
components/script/dom/webidls/ServiceWorker.webidl
Normal file
|
@ -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<Transferable> transfer*/);
|
||||
|
||||
// event
|
||||
attribute EventHandler onstatechange;
|
||||
};
|
||||
|
||||
ServiceWorker implements AbstractWorker;
|
||||
|
||||
enum ServiceWorkerState {
|
||||
"installing",
|
||||
"installed",
|
||||
"activating",
|
||||
"activated",
|
||||
"redundant"
|
||||
};
|
27
components/script/dom/webidls/ServiceWorkerContainer.webidl
Normal file
27
components/script/dom/webidls/ServiceWorkerContainer.webidl
Normal file
|
@ -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<ServiceWorkerRegistration> ready;
|
||||
|
||||
[NewObject, Throws] ServiceWorkerRegistration register(USVString scriptURL, optional RegistrationOptions options);
|
||||
|
||||
//[NewObject] /*Promise<any>*/ any getRegistration(optional USVString clientURL = "");
|
||||
//[NewObject] /* Promise */<sequence<ServiceWorkerRegistration>> 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";
|
||||
};
|
|
@ -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<void> 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
|
||||
};
|
|
@ -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<void> update();
|
||||
// [NewObject] Promise<boolean> unregister();
|
||||
|
||||
// event
|
||||
// attribute EventHandler onupdatefound;
|
||||
};
|
|
@ -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<Worker>;
|
||||
|
||||
|
@ -48,29 +45,6 @@ pub struct Worker {
|
|||
runtime: Arc<Mutex<Option<SharedRt>>>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WorkerScriptLoadOrigin {
|
||||
referrer_url: Option<Url>,
|
||||
referrer_policy: Option<ReferrerPolicy>,
|
||||
request_source: RequestSource,
|
||||
pipeline_id: Option<PipelineId>
|
||||
}
|
||||
|
||||
impl LoadOrigin for WorkerScriptLoadOrigin {
|
||||
fn referrer_url(&self) -> Option<Url> {
|
||||
self.referrer_url.clone()
|
||||
}
|
||||
fn referrer_policy(&self) -> Option<ReferrerPolicy> {
|
||||
self.referrer_policy.clone()
|
||||
}
|
||||
fn request_source(&self) -> RequestSource {
|
||||
self.request_source.clone()
|
||||
}
|
||||
fn pipeline_id(&self) -> Option<PipelineId> {
|
||||
self.pipeline_id.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new_inherited(sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
||||
closing: Arc<AtomicBool>) -> 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<SimpleWorkerErrorHandler>) {
|
||||
impl Runnable for SimpleWorkerErrorHandler<Worker> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn handler(self: Box<SimpleWorkerErrorHandler<Worker>>) {
|
||||
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<WorkerErrorHandler>) {
|
||||
impl Runnable for WorkerErrorHandler<Worker> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn handler(self: Box<WorkerErrorHandler<Worker>>) {
|
||||
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 {}
|
||||
|
|
|
@ -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<AtomicBool>,
|
||||
}
|
||||
|
||||
pub fn prepare_workerscope_init(global: GlobalRef,
|
||||
worker_type: String,
|
||||
worker_url: Url,
|
||||
devtools_sender: IpcSender<DevtoolScriptControlMsg>,
|
||||
closing: Arc<AtomicBool>) -> 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<ScriptChan + Send> {
|
||||
let dedicated =
|
||||
self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
match dedicated {
|
||||
Some(dedicated) => dedicated.script_chan(),
|
||||
None => panic!("need to implement a sender for SharedWorker"),
|
||||
let service_worker = self.downcast::<ServiceWorkerGlobalScope>();
|
||||
if let Some(dedicated) = dedicated {
|
||||
return dedicated.script_chan();
|
||||
} else if let Some(service_worker) = service_worker {
|
||||
return service_worker.script_chan();
|
||||
} else {
|
||||
panic!("need to implement a sender for SharedWorker")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pipeline(&self) -> PipelineId {
|
||||
let dedicated =
|
||||
self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
match dedicated {
|
||||
Some(dedicated) => dedicated.pipeline(),
|
||||
None => panic!("need to add a pipeline for SharedWorker"),
|
||||
let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
let service_worker = self.downcast::<ServiceWorkerGlobalScope>();
|
||||
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<ScriptChan + Send>, Box<ScriptPort + Send>) {
|
||||
let dedicated =
|
||||
self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
match dedicated {
|
||||
Some(dedicated) => dedicated.new_script_pair(),
|
||||
None => panic!("need to implement creating isolated event loops for SharedWorker"),
|
||||
let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
let service_worker = self.downcast::<ServiceWorkerGlobalScope>();
|
||||
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::<DedicatedWorkerGlobalScope>();
|
||||
match dedicated {
|
||||
Some(dedicated) => dedicated.process_event(msg),
|
||||
None => panic!("need to implement processing single events for SharedWorker"),
|
||||
let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
|
||||
let service_worker = self.downcast::<ServiceWorkerGlobalScope>();
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<CommonScriptMsg, ()> {
|
||||
self.recv().map(|(_, msg)| msg).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulates internal communication of shared messages within the script thread.
|
||||
#[derive(JSTraceable)]
|
||||
pub struct SendableMainThreadScriptChan(pub Sender<CommonScriptMsg>);
|
||||
|
@ -308,6 +316,8 @@ pub struct ScriptThread {
|
|||
browsing_context: MutNullableHeap<JS<BrowsingContext>>,
|
||||
/// A list of data pertaining to loads that have not yet received a network response
|
||||
incomplete_loads: DOMRefCell<Vec<InProgressLoad>>,
|
||||
/// A map to store service worker registrations for a given origin
|
||||
registration_map: DOMRefCell<HashMap<Url, JS<ServiceWorkerRegistration>>>,
|
||||
/// 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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -65,6 +65,13 @@ impl PrefValue {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_u64(&self) -> Option<u64> {
|
||||
match *self {
|
||||
PrefValue::Number(x) => Some(x as u64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for PrefValue {
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
prefs: ["dom.serviceworker.enabled:true"]
|
|
@ -0,0 +1 @@
|
|||
console.log("Hey Servo; lets cache something! :)");
|
|
@ -0,0 +1,52 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
|
||||
function register_sw(script_url, scope_str='') {
|
||||
var registration = navigator.serviceWorker.register(script_url, {'scope': scope_str });
|
||||
return registration;
|
||||
}
|
||||
|
||||
test(function (){
|
||||
assert_true('serviceWorker' in navigator);
|
||||
}, "Test: Asserts ServiceWorkerContainer in Navigator");
|
||||
|
||||
test(function() {
|
||||
var sw_reg = register_sw('sw.js');
|
||||
assert_class_string(sw_reg, "ServiceWorkerRegistration");
|
||||
assert_class_string(sw_reg.active, "ServiceWorker");
|
||||
assert_class_string(navigator.serviceWorker.controller, "ServiceWorker");
|
||||
}, "Test: Asserts Active Service Worker and its Registration");
|
||||
|
||||
test(function() {
|
||||
var sw_reg = register_sw('resources/sw.js', './');
|
||||
assert_equals(sw_reg.scope, location.href.replace("service-worker-registration.html", ""));
|
||||
}, "Test: Service Worker Registration property scope Url when no scope");
|
||||
|
||||
test(function() {
|
||||
var sw_reg = register_sw('resources/sw.js', '/some/nested/cache/directory');
|
||||
var expected_scope_url = location.protocol + '//' + location.host + '/some/nested/cache/directory';
|
||||
assert_equals(sw_reg.scope, expected_scope_url);
|
||||
}, "Test: Service Worker Registration property scope when provided a scope");
|
||||
|
||||
test(function () {
|
||||
assert_throws(new TypeError(),
|
||||
function() { var sw_reg = register_sw('./in%5Csome%5fdir/sw.js'); },
|
||||
"Invalid URL Path");
|
||||
}, "Test: Throws Error when Invalid Url Path");
|
||||
|
||||
test(function () {
|
||||
assert_throws(new TypeError(),
|
||||
function() { var sw_reg = register_sw('sw.js', './mal%5fformed/sco%5Cpe/'); },
|
||||
"Invalid URL Path");
|
||||
}, "Test: Throws Error when Invalid Scope");
|
||||
|
||||
test(function() {
|
||||
var sw_reg = register_sw('resources/sw.js');
|
||||
assert_equals(sw_reg.active.scriptURL, location.href.replace("service-worker-registration.html", "resources/sw.js"));
|
||||
}, "Test: Active Service Worker ScriptURL property");
|
||||
|
||||
</script>
|
1
tests/wpt/mozilla/tests/mozilla/service-workers/sw.js
Normal file
1
tests/wpt/mozilla/tests/mozilla/service-workers/sw.js
Normal file
|
@ -0,0 +1 @@
|
|||
console.log("Hey Servo; lets cache something! :)");
|
Loading…
Add table
Add a link
Reference in a new issue