mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
serviceworker: make job queue unique per origin
This commit is contained in:
parent
7e74f73301
commit
89eb7c2aa2
19 changed files with 839 additions and 592 deletions
|
@ -153,14 +153,12 @@ use script_traits::{
|
||||||
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
|
IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerSchedulerMsg,
|
||||||
};
|
};
|
||||||
use script_traits::{
|
use script_traits::{
|
||||||
LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
|
Job, LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
|
||||||
ServiceWorkerManagerFactory,
|
ServiceWorkerManagerFactory,
|
||||||
};
|
};
|
||||||
use script_traits::{MediaSessionActionType, MouseEventType};
|
use script_traits::{MediaSessionActionType, MouseEventType};
|
||||||
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
|
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
|
||||||
use script_traits::{
|
use script_traits::{SWManagerMsg, SWManagerSenders, UpdatePipelineIdReason, WebDriverCommandMsg};
|
||||||
SWManagerMsg, SWManagerSenders, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_config::{opts, pref};
|
use servo_config::{opts, pref};
|
||||||
use servo_rand::{random, Rng, ServoRng, SliceRandom};
|
use servo_rand::{random, Rng, ServoRng, SliceRandom};
|
||||||
|
@ -1983,8 +1981,8 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FromScriptMsg::RegisterServiceWorker(scope_things, scope) => {
|
FromScriptMsg::ScheduleJob(job) => {
|
||||||
self.handle_register_serviceworker(scope_things, scope);
|
self.handle_schedule_serviceworker_job(source_pipeline_id, job);
|
||||||
},
|
},
|
||||||
FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
|
FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
|
||||||
if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
|
if let Some(mgr) = self.sw_managers.get(&scope_url.origin()) {
|
||||||
|
@ -2640,9 +2638,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_register_serviceworker(&mut self, scope_things: ScopeThings, scope: ServoUrl) {
|
/// <https://w3c.github.io/ServiceWorker/#schedule-job-algorithm>
|
||||||
|
/// and
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-job-queue>
|
||||||
|
///
|
||||||
|
/// The Job Queue is essentially the channel to a SW manager,
|
||||||
|
/// which are scoped per origin.
|
||||||
|
fn handle_schedule_serviceworker_job(&mut self, pipeline_id: PipelineId, job: Job) {
|
||||||
|
let origin = job.scope_url.origin();
|
||||||
|
|
||||||
|
if self
|
||||||
|
.check_origin_against_pipeline(&pipeline_id, &origin)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return warn!(
|
||||||
|
"Attempt to schedule a serviceworker job from an origin not matching the origin of the job."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// This match is equivalent to Entry.or_insert_with but allows for early return.
|
// This match is equivalent to Entry.or_insert_with but allows for early return.
|
||||||
let sw_manager = match self.sw_managers.entry(scope.origin()) {
|
let sw_manager = match self.sw_managers.entry(origin.clone()) {
|
||||||
Entry::Occupied(entry) => entry.into_mut(),
|
Entry::Occupied(entry) => entry.into_mut(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let (own_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
let (own_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||||
|
@ -2653,7 +2668,7 @@ where
|
||||||
own_sender: own_sender.clone(),
|
own_sender: own_sender.clone(),
|
||||||
receiver,
|
receiver,
|
||||||
};
|
};
|
||||||
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, scope.origin());
|
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin);
|
||||||
|
|
||||||
if opts::multiprocess() {
|
if opts::multiprocess() {
|
||||||
if content.spawn_multiprocess().is_err() {
|
if content.spawn_multiprocess().is_err() {
|
||||||
|
@ -2665,7 +2680,7 @@ where
|
||||||
entry.insert(own_sender)
|
entry.insert(own_sender)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let _ = sw_manager.send(ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope));
|
let _ = sw_manager.send(ServiceWorkerMsg::ScheduleJob(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_broadcast_storage_event(
|
fn handle_broadcast_storage_event(
|
||||||
|
|
|
@ -178,6 +178,20 @@ impl PipelineNamespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_service_worker_id(&mut self) -> ServiceWorkerId {
|
||||||
|
ServiceWorkerId {
|
||||||
|
namespace_id: self.id,
|
||||||
|
index: ServiceWorkerIndex(self.next_index()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_service_worker_registration_id(&mut self) -> ServiceWorkerRegistrationId {
|
||||||
|
ServiceWorkerRegistrationId {
|
||||||
|
namespace_id: self.id,
|
||||||
|
index: ServiceWorkerRegistrationIndex(self.next_index()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn next_blob_id(&mut self) -> BlobId {
|
fn next_blob_id(&mut self) -> BlobId {
|
||||||
BlobId {
|
BlobId {
|
||||||
namespace_id: self.id,
|
namespace_id: self.id,
|
||||||
|
@ -423,6 +437,74 @@ impl fmt::Display for BroadcastChannelRouterId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct ServiceWorkerIndex(pub NonZeroU32);
|
||||||
|
malloc_size_of_is_0!(ServiceWorkerIndex);
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||||
|
)]
|
||||||
|
pub struct ServiceWorkerId {
|
||||||
|
pub namespace_id: PipelineNamespaceId,
|
||||||
|
pub index: ServiceWorkerIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceWorkerId {
|
||||||
|
pub fn new() -> ServiceWorkerId {
|
||||||
|
PIPELINE_NAMESPACE.with(|tls| {
|
||||||
|
let mut namespace = tls.get().expect("No namespace set for this thread!");
|
||||||
|
let next_service_worker_id = namespace.next_service_worker_id();
|
||||||
|
tls.set(Some(namespace));
|
||||||
|
next_service_worker_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ServiceWorkerId {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let PipelineNamespaceId(namespace_id) = self.namespace_id;
|
||||||
|
let ServiceWorkerIndex(index) = self.index;
|
||||||
|
write!(fmt, "(ServiceWorkerId{},{})", namespace_id, index.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct ServiceWorkerRegistrationIndex(pub NonZeroU32);
|
||||||
|
malloc_size_of_is_0!(ServiceWorkerRegistrationIndex);
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
|
||||||
|
)]
|
||||||
|
pub struct ServiceWorkerRegistrationId {
|
||||||
|
pub namespace_id: PipelineNamespaceId,
|
||||||
|
pub index: ServiceWorkerRegistrationIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceWorkerRegistrationId {
|
||||||
|
pub fn new() -> ServiceWorkerRegistrationId {
|
||||||
|
PIPELINE_NAMESPACE.with(|tls| {
|
||||||
|
let mut namespace = tls.get().expect("No namespace set for this thread!");
|
||||||
|
let next_service_worker_registration_id =
|
||||||
|
namespace.next_service_worker_registration_id();
|
||||||
|
tls.set(Some(namespace));
|
||||||
|
next_service_worker_registration_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ServiceWorkerRegistrationId {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let PipelineNamespaceId(namespace_id) = self.namespace_id;
|
||||||
|
let ServiceWorkerRegistrationIndex(index) = self.index;
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"(ServiceWorkerRegistrationId{},{})",
|
||||||
|
namespace_id,
|
||||||
|
index.get()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub struct BlobIndex(pub NonZeroU32);
|
pub struct BlobIndex(pub NonZeroU32);
|
||||||
malloc_size_of_is_0!(BlobIndex);
|
malloc_size_of_is_0!(BlobIndex);
|
||||||
|
|
|
@ -86,6 +86,7 @@ use msg::constellation_msg::{
|
||||||
BlobId, BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
BlobId, BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
||||||
MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
|
MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
|
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||||
use net_traits::filemanager_thread::RelativePos;
|
use net_traits::filemanager_thread::RelativePos;
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache::{ImageCache, PendingImageId};
|
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||||
|
@ -179,6 +180,9 @@ unsafe_no_jsmanaged_fields!(MessagePortImpl);
|
||||||
unsafe_no_jsmanaged_fields!(MessagePortId);
|
unsafe_no_jsmanaged_fields!(MessagePortId);
|
||||||
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
||||||
|
|
||||||
|
unsafe_no_jsmanaged_fields!(ServiceWorkerId);
|
||||||
|
unsafe_no_jsmanaged_fields!(ServiceWorkerRegistrationId);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(BlobId);
|
unsafe_no_jsmanaged_fields!(BlobId);
|
||||||
|
|
|
@ -47,6 +47,7 @@ impl Client {
|
||||||
self.active_worker.get()
|
self.active_worker.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_controller(&self, worker: &ServiceWorker) {
|
pub fn set_controller(&self, worker: &ServiceWorker) {
|
||||||
self.active_worker.set(Some(worker));
|
self.active_worker.set(Some(worker));
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||||
use crate::dom::performance::Performance;
|
use crate::dom::performance::Performance;
|
||||||
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
|
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
|
use crate::dom::serviceworker::ServiceWorker;
|
||||||
|
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
||||||
use crate::dom::workletglobalscope::WorkletGlobalScope;
|
use crate::dom::workletglobalscope::WorkletGlobalScope;
|
||||||
|
@ -81,6 +83,7 @@ use js::rust::{HandleValue, MutableHandleValue};
|
||||||
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
||||||
use msg::constellation_msg::{
|
use msg::constellation_msg::{
|
||||||
BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
|
BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
|
||||||
|
ServiceWorkerId, ServiceWorkerRegistrationId,
|
||||||
};
|
};
|
||||||
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
||||||
use net_traits::filemanager_thread::{
|
use net_traits::filemanager_thread::{
|
||||||
|
@ -135,6 +138,13 @@ pub struct GlobalScope {
|
||||||
/// The blobs managed by this global, if any.
|
/// The blobs managed by this global, if any.
|
||||||
blob_state: DomRefCell<BlobState>,
|
blob_state: DomRefCell<BlobState>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#environment-settings-object-service-worker-registration-object-map>
|
||||||
|
registration_map:
|
||||||
|
DomRefCell<HashMap<ServiceWorkerRegistrationId, Dom<ServiceWorkerRegistration>>>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#environment-settings-object-service-worker-object-map>
|
||||||
|
worker_map: DomRefCell<HashMap<ServiceWorkerId, Dom<ServiceWorker>>>,
|
||||||
|
|
||||||
/// Pipeline id associated with this global.
|
/// Pipeline id associated with this global.
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
|
||||||
|
@ -567,6 +577,8 @@ impl GlobalScope {
|
||||||
blob_state: DomRefCell::new(BlobState::UnManaged),
|
blob_state: DomRefCell::new(BlobState::UnManaged),
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
crypto: Default::default(),
|
crypto: Default::default(),
|
||||||
|
registration_map: DomRefCell::new(HashMap::new()),
|
||||||
|
worker_map: DomRefCell::new(HashMap::new()),
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
devtools_wants_updates: Default::default(),
|
devtools_wants_updates: Default::default(),
|
||||||
console_timers: DomRefCell::new(Default::default()),
|
console_timers: DomRefCell::new(Default::default()),
|
||||||
|
@ -645,6 +657,72 @@ impl GlobalScope {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-registration-object>
|
||||||
|
pub fn get_serviceworker_registration(
|
||||||
|
&self,
|
||||||
|
script_url: &ServoUrl,
|
||||||
|
scope: &ServoUrl,
|
||||||
|
registration_id: ServiceWorkerRegistrationId,
|
||||||
|
installing_worker: Option<ServiceWorkerId>,
|
||||||
|
_waiting_worker: Option<ServiceWorkerId>,
|
||||||
|
_active_worker: Option<ServiceWorkerId>,
|
||||||
|
) -> DomRoot<ServiceWorkerRegistration> {
|
||||||
|
// Step 1
|
||||||
|
let mut registrations = self.registration_map.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(registration) = registrations.get(®istration_id) {
|
||||||
|
// Step 3
|
||||||
|
return DomRoot::from_ref(&**registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2.1 -> 2.5
|
||||||
|
let new_registration =
|
||||||
|
ServiceWorkerRegistration::new(self, scope.clone(), registration_id.clone());
|
||||||
|
|
||||||
|
// Step 2.6
|
||||||
|
if let Some(worker_id) = installing_worker {
|
||||||
|
let worker = self.get_serviceworker(script_url, scope, worker_id);
|
||||||
|
new_registration.set_installing(&*worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 2.7 (waiting worker)
|
||||||
|
|
||||||
|
// TODO: 2.8 (active worker)
|
||||||
|
|
||||||
|
// Step 2.9
|
||||||
|
registrations.insert(registration_id, Dom::from_ref(&*new_registration));
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
new_registration
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#get-the-service-worker-object>
|
||||||
|
pub fn get_serviceworker(
|
||||||
|
&self,
|
||||||
|
script_url: &ServoUrl,
|
||||||
|
scope: &ServoUrl,
|
||||||
|
worker_id: ServiceWorkerId,
|
||||||
|
) -> DomRoot<ServiceWorker> {
|
||||||
|
// Step 1
|
||||||
|
let mut workers = self.worker_map.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(worker) = workers.get(&worker_id) {
|
||||||
|
// Step 3
|
||||||
|
DomRoot::from_ref(&**worker)
|
||||||
|
} else {
|
||||||
|
// Step 2.1
|
||||||
|
// TODO: step 2.2, worker state.
|
||||||
|
let new_worker =
|
||||||
|
ServiceWorker::new(self, script_url.clone(), scope.clone(), worker_id.clone());
|
||||||
|
|
||||||
|
// Step 2.3
|
||||||
|
workers.insert(worker_id, Dom::from_ref(&*new_worker));
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
new_worker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Complete the transfer of a message-port.
|
/// Complete the transfer of a message-port.
|
||||||
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
|
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
|
||||||
let should_start = if let MessagePortState::Managed(_id, message_ports) =
|
let should_start = if let MessagePortState::Managed(_id, message_ports) =
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
||||||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
if self.serviceworker_registration.active().is_none() {
|
if self.serviceworker_registration.is_active() {
|
||||||
promise.reject_native(&DOMException::new(
|
promise.reject_native(&DOMException::new(
|
||||||
&self.global(),
|
&self.global(),
|
||||||
DOMErrorName::InvalidStateError,
|
DOMErrorName::InvalidStateError,
|
||||||
|
@ -68,7 +68,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
||||||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
if self.serviceworker_registration.active().is_none() {
|
if self.serviceworker_registration.is_active() {
|
||||||
promise.reject_native(&DOMException::new(
|
promise.reject_native(&DOMException::new(
|
||||||
&self.global(),
|
&self.global(),
|
||||||
DOMErrorName::InvalidStateError,
|
DOMErrorName::InvalidStateError,
|
||||||
|
@ -90,7 +90,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
||||||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
if self.serviceworker_registration.active().is_none() {
|
if self.serviceworker_registration.is_active() {
|
||||||
promise.reject_native(&DOMException::new(
|
promise.reject_native(&DOMException::new(
|
||||||
&self.global(),
|
&self.global(),
|
||||||
DOMErrorName::InvalidStateError,
|
DOMErrorName::InvalidStateError,
|
||||||
|
@ -114,7 +114,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
||||||
let mut state = NavigationPreloadState::empty();
|
let mut state = NavigationPreloadState::empty();
|
||||||
|
|
||||||
// 3.
|
// 3.
|
||||||
if let Some(_) = self.serviceworker_registration.active() {
|
if self.serviceworker_registration.is_active() {
|
||||||
if self
|
if self
|
||||||
.serviceworker_registration
|
.serviceworker_registration
|
||||||
.get_navigation_preload_enabled()
|
.get_navigation_preload_enabled()
|
||||||
|
|
|
@ -23,6 +23,7 @@ use crate::task::TaskOnce;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::jsapi::{Heap, JSObject};
|
use js::jsapi::{Heap, JSObject};
|
||||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||||
|
use msg::constellation_msg::ServiceWorkerId;
|
||||||
use script_traits::{DOMMessage, ScriptMsg};
|
use script_traits::{DOMMessage, ScriptMsg};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -35,31 +36,35 @@ pub struct ServiceWorker {
|
||||||
script_url: DomRefCell<String>,
|
script_url: DomRefCell<String>,
|
||||||
scope_url: ServoUrl,
|
scope_url: ServoUrl,
|
||||||
state: Cell<ServiceWorkerState>,
|
state: Cell<ServiceWorkerState>,
|
||||||
skip_waiting: Cell<bool>,
|
worker_id: ServiceWorkerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceWorker {
|
impl ServiceWorker {
|
||||||
fn new_inherited(script_url: &str, skip_waiting: bool, scope_url: ServoUrl) -> ServiceWorker {
|
fn new_inherited(
|
||||||
|
script_url: &str,
|
||||||
|
scope_url: ServoUrl,
|
||||||
|
worker_id: ServiceWorkerId,
|
||||||
|
) -> ServiceWorker {
|
||||||
ServiceWorker {
|
ServiceWorker {
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
script_url: DomRefCell::new(String::from(script_url)),
|
script_url: DomRefCell::new(String::from(script_url)),
|
||||||
state: Cell::new(ServiceWorkerState::Installing),
|
state: Cell::new(ServiceWorkerState::Installing),
|
||||||
scope_url: scope_url,
|
scope_url: scope_url,
|
||||||
skip_waiting: Cell::new(skip_waiting),
|
worker_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_serviceworker(
|
pub fn new(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
script_url: ServoUrl,
|
script_url: ServoUrl,
|
||||||
scope_url: ServoUrl,
|
scope_url: ServoUrl,
|
||||||
skip_waiting: bool,
|
worker_id: ServiceWorkerId,
|
||||||
) -> DomRoot<ServiceWorker> {
|
) -> DomRoot<ServiceWorker> {
|
||||||
reflect_dom_object(
|
reflect_dom_object(
|
||||||
Box::new(ServiceWorker::new_inherited(
|
Box::new(ServiceWorker::new_inherited(
|
||||||
script_url.as_str(),
|
script_url.as_str(),
|
||||||
skip_waiting,
|
|
||||||
scope_url,
|
scope_url,
|
||||||
|
worker_id,
|
||||||
)),
|
)),
|
||||||
global,
|
global,
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions;
|
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions;
|
||||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::ServiceWorkerContainerMethods;
|
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::ServiceWorkerContainerMethods;
|
||||||
use crate::dom::bindings::error::Error;
|
use crate::dom::bindings::error::Error;
|
||||||
|
use crate::dom::bindings::refcounted::TrustedPromise;
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||||
use crate::dom::bindings::str::USVString;
|
use crate::dom::bindings::str::USVString;
|
||||||
|
@ -13,10 +14,17 @@ use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::serviceworker::ServiceWorker;
|
use crate::dom::serviceworker::ServiceWorker;
|
||||||
|
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||||
|
use crate::realms::enter_realm;
|
||||||
use crate::realms::InRealm;
|
use crate::realms::InRealm;
|
||||||
use crate::script_thread::ScriptThread;
|
use crate::task::TaskCanceller;
|
||||||
use crate::serviceworkerjob::{Job, JobType};
|
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
||||||
|
use crate::task_source::TaskSource;
|
||||||
|
use crate::task_source::TaskSourceName;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
use ipc_channel::router::ROUTER;
|
||||||
|
use script_traits::{Job, JobError, JobResult, JobResultValue, JobType, ScriptMsg};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -50,45 +58,33 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
||||||
self.client.get_controller()
|
self.client.get_controller()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)] // Job is unrooted
|
/// https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-register - A
|
||||||
/// https://w3c.github.io/ServiceWorker/#navigator-service-worker-register and - A
|
/// and https://w3c.github.io/ServiceWorker/#start-register - B
|
||||||
/// https://w3c.github.io/ServiceWorker/#start-register-algorithm - B
|
|
||||||
fn Register(
|
fn Register(
|
||||||
&self,
|
&self,
|
||||||
script_url: USVString,
|
script_url: USVString,
|
||||||
options: &RegistrationOptions,
|
options: &RegistrationOptions,
|
||||||
comp: InRealm,
|
comp: InRealm,
|
||||||
) -> Rc<Promise> {
|
) -> Rc<Promise> {
|
||||||
|
// A: Step 2.
|
||||||
|
let global = self.client.global();
|
||||||
|
|
||||||
// A: Step 1
|
// A: Step 1
|
||||||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
let promise = Promise::new_in_current_realm(&*global, comp);
|
||||||
let USVString(ref script_url) = script_url;
|
let USVString(ref script_url) = script_url;
|
||||||
let api_base_url = self.global().api_base_url();
|
|
||||||
// A: Step 3-5
|
// A: Step 3
|
||||||
|
let api_base_url = global.api_base_url();
|
||||||
let script_url = match api_base_url.join(script_url) {
|
let script_url = match api_base_url.join(script_url) {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
// B: Step 1
|
||||||
promise.reject_error(Error::Type("Invalid script URL".to_owned()));
|
promise.reject_error(Error::Type("Invalid script URL".to_owned()));
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// B: Step 2
|
|
||||||
match script_url.scheme() {
|
// A: Step 4-5
|
||||||
"https" | "http" => {},
|
|
||||||
_ => {
|
|
||||||
promise.reject_error(Error::Type("Only secure origins are allowed".to_owned()));
|
|
||||||
return promise;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// B: Step 3
|
|
||||||
if script_url.path().to_ascii_lowercase().contains("%2f") ||
|
|
||||||
script_url.path().to_ascii_lowercase().contains("%5c")
|
|
||||||
{
|
|
||||||
promise.reject_error(Error::Type(
|
|
||||||
"Script URL contains forbidden characters".to_owned(),
|
|
||||||
));
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
// B: Step 4-5
|
|
||||||
let scope = match options.scope {
|
let scope = match options.scope {
|
||||||
Some(ref scope) => {
|
Some(ref scope) => {
|
||||||
let &USVString(ref inner_scope) = scope;
|
let &USVString(ref inner_scope) = scope;
|
||||||
|
@ -102,6 +98,27 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
||||||
},
|
},
|
||||||
None => script_url.join("./").unwrap(),
|
None => script_url.join("./").unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A: Step 6 -> invoke B.
|
||||||
|
|
||||||
|
// B: Step 3
|
||||||
|
match script_url.scheme() {
|
||||||
|
"https" | "http" => {},
|
||||||
|
_ => {
|
||||||
|
promise.reject_error(Error::Type("Only secure origins are allowed".to_owned()));
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// B: Step 4
|
||||||
|
if script_url.path().to_ascii_lowercase().contains("%2f") ||
|
||||||
|
script_url.path().to_ascii_lowercase().contains("%5c")
|
||||||
|
{
|
||||||
|
promise.reject_error(Error::Type(
|
||||||
|
"Script URL contains forbidden characters".to_owned(),
|
||||||
|
));
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
// B: Step 6
|
// B: Step 6
|
||||||
match scope.scheme() {
|
match scope.scheme() {
|
||||||
"https" | "http" => {},
|
"https" | "http" => {},
|
||||||
|
@ -120,17 +137,134 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// B: Step 8
|
// Setup the callback for reject/resolve of the promise,
|
||||||
|
// from steps running "in-parallel" from here in the serviceworker manager.
|
||||||
|
let (task_source, task_canceller) = (
|
||||||
|
global.dom_manipulation_task_source(),
|
||||||
|
global.task_canceller(TaskSourceName::DOMManipulation),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut handler = RegisterJobResultHandler {
|
||||||
|
trusted_promise: Some(TrustedPromise::new(promise.clone())),
|
||||||
|
task_source,
|
||||||
|
task_canceller,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (job_result_sender, job_result_receiver) = ipc::channel().expect("ipc channel failure");
|
||||||
|
|
||||||
|
ROUTER.add_route(
|
||||||
|
job_result_receiver.to_opaque(),
|
||||||
|
Box::new(move |message| {
|
||||||
|
let msg = message.to();
|
||||||
|
match msg {
|
||||||
|
Ok(msg) => handler.handle(msg),
|
||||||
|
Err(err) => warn!("Error receiving a JobResult: {:?}", err),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let scope_things =
|
||||||
|
ServiceWorkerRegistration::create_scope_things(&*global, script_url.clone());
|
||||||
|
|
||||||
|
// B: Step 8 - 13
|
||||||
let job = Job::create_job(
|
let job = Job::create_job(
|
||||||
JobType::Register,
|
JobType::Register,
|
||||||
scope,
|
scope,
|
||||||
script_url,
|
script_url,
|
||||||
promise.clone(),
|
job_result_sender,
|
||||||
options.type_,
|
self.client.creation_url(),
|
||||||
&*self.client,
|
Some(scope_things),
|
||||||
);
|
);
|
||||||
// Job is unrooted here, do not do anything other than immediately scheduling
|
|
||||||
ScriptThread::schedule_job(job);
|
// B: Step 14: schedule job.
|
||||||
|
let _ = global
|
||||||
|
.script_to_constellation_chan()
|
||||||
|
.send(ScriptMsg::ScheduleJob(job));
|
||||||
|
|
||||||
|
// A: Step 7
|
||||||
promise
|
promise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Callback for resolve/reject job promise for Register.
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#register>
|
||||||
|
struct RegisterJobResultHandler {
|
||||||
|
trusted_promise: Option<TrustedPromise>,
|
||||||
|
task_source: DOMManipulationTaskSource,
|
||||||
|
task_canceller: TaskCanceller,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterJobResultHandler {
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#reject-job-promise>
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#resolve-job-promise>
|
||||||
|
/// Handle a result to either resolve or reject the register job promise.
|
||||||
|
pub fn handle(&mut self, result: JobResult) {
|
||||||
|
match result {
|
||||||
|
JobResult::RejectPromise(error) => {
|
||||||
|
let promise = self
|
||||||
|
.trusted_promise
|
||||||
|
.take()
|
||||||
|
.expect("No promise to resolve for SW Register job.");
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
let _ = self.task_source.queue_with_canceller(
|
||||||
|
task!(reject_promise_with_security_error: move || {
|
||||||
|
let promise = promise.root();
|
||||||
|
let _ac = enter_realm(&*promise.global());
|
||||||
|
match error {
|
||||||
|
JobError::TypeError => {
|
||||||
|
promise.reject_error(Error::Type("Failed to register a ServiceWorker".to_string()));
|
||||||
|
},
|
||||||
|
JobError::SecurityError => {
|
||||||
|
promise.reject_error(Error::Security);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}),
|
||||||
|
&self.task_canceller,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: step 2, handle equivalent jobs.
|
||||||
|
},
|
||||||
|
JobResult::ResolvePromise(job, value) => {
|
||||||
|
let promise = self
|
||||||
|
.trusted_promise
|
||||||
|
.take()
|
||||||
|
.expect("No promise to resolve for SW Register job.");
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
let _ = self.task_source.queue_with_canceller(
|
||||||
|
task!(resolve_promise: move || {
|
||||||
|
let promise = promise.root();
|
||||||
|
let global = promise.global();
|
||||||
|
let _ac = enter_realm(&*global);
|
||||||
|
|
||||||
|
// Step 1.1
|
||||||
|
let JobResultValue::Registration {
|
||||||
|
id,
|
||||||
|
installing_worker,
|
||||||
|
waiting_worker,
|
||||||
|
active_worker,
|
||||||
|
} = value;
|
||||||
|
|
||||||
|
// Step 1.2 (Job type is "register").
|
||||||
|
let registration = global.get_serviceworker_registration(
|
||||||
|
&job.script_url,
|
||||||
|
&job.scope_url,
|
||||||
|
id,
|
||||||
|
installing_worker,
|
||||||
|
waiting_worker,
|
||||||
|
active_worker,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 1.4
|
||||||
|
promise.resolve_native(&*registration);
|
||||||
|
}),
|
||||||
|
&self.task_canceller,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: step 2, handle equivalent jobs.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::ServiceWorkerState;
|
|
||||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::ServiceWorkerRegistrationMethods;
|
use crate::dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::ServiceWorkerRegistrationMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::ServiceWorkerUpdateViaCache;
|
use crate::dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::ServiceWorkerUpdateViaCache;
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
||||||
|
@ -16,6 +15,7 @@ use crate::dom::serviceworker::ServiceWorker;
|
||||||
use crate::dom::workerglobalscope::prepare_workerscope_init;
|
use crate::dom::workerglobalscope::prepare_workerscope_init;
|
||||||
use devtools_traits::WorkerId;
|
use devtools_traits::WorkerId;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use msg::constellation_msg::ServiceWorkerRegistrationId;
|
||||||
use script_traits::{ScopeThings, WorkerScriptLoadOrigin};
|
use script_traits::{ScopeThings, WorkerScriptLoadOrigin};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -24,58 +24,60 @@ use uuid::Uuid;
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct ServiceWorkerRegistration {
|
pub struct ServiceWorkerRegistration {
|
||||||
eventtarget: EventTarget,
|
eventtarget: EventTarget,
|
||||||
active: Option<Dom<ServiceWorker>>,
|
active: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||||
installing: Option<Dom<ServiceWorker>>,
|
installing: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||||
waiting: Option<Dom<ServiceWorker>>,
|
waiting: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||||
navigation_preload: MutNullableDom<NavigationPreloadManager>,
|
navigation_preload: MutNullableDom<NavigationPreloadManager>,
|
||||||
scope: ServoUrl,
|
scope: ServoUrl,
|
||||||
navigation_preload_enabled: Cell<bool>,
|
navigation_preload_enabled: Cell<bool>,
|
||||||
navigation_preload_header_value: DomRefCell<Option<ByteString>>,
|
navigation_preload_header_value: DomRefCell<Option<ByteString>>,
|
||||||
update_via_cache: ServiceWorkerUpdateViaCache,
|
update_via_cache: ServiceWorkerUpdateViaCache,
|
||||||
uninstalling: Cell<bool>,
|
uninstalling: Cell<bool>,
|
||||||
|
registration_id: ServiceWorkerRegistrationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceWorkerRegistration {
|
impl ServiceWorkerRegistration {
|
||||||
fn new_inherited(active_sw: &ServiceWorker, scope: ServoUrl) -> ServiceWorkerRegistration {
|
fn new_inherited(
|
||||||
|
scope: ServoUrl,
|
||||||
|
registration_id: ServiceWorkerRegistrationId,
|
||||||
|
) -> ServiceWorkerRegistration {
|
||||||
ServiceWorkerRegistration {
|
ServiceWorkerRegistration {
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
active: Some(Dom::from_ref(active_sw)),
|
active: DomRefCell::new(None),
|
||||||
installing: None,
|
installing: DomRefCell::new(None),
|
||||||
waiting: None,
|
waiting: DomRefCell::new(None),
|
||||||
navigation_preload: MutNullableDom::new(None),
|
navigation_preload: MutNullableDom::new(None),
|
||||||
scope: scope,
|
scope: scope,
|
||||||
navigation_preload_enabled: Cell::new(false),
|
navigation_preload_enabled: Cell::new(false),
|
||||||
navigation_preload_header_value: DomRefCell::new(None),
|
navigation_preload_header_value: DomRefCell::new(None),
|
||||||
update_via_cache: ServiceWorkerUpdateViaCache::Imports,
|
update_via_cache: ServiceWorkerUpdateViaCache::Imports,
|
||||||
uninstalling: Cell::new(false),
|
uninstalling: Cell::new(false),
|
||||||
|
registration_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
script_url: &ServoUrl,
|
|
||||||
scope: ServoUrl,
|
scope: ServoUrl,
|
||||||
|
registration_id: ServiceWorkerRegistrationId,
|
||||||
) -> DomRoot<ServiceWorkerRegistration> {
|
) -> DomRoot<ServiceWorkerRegistration> {
|
||||||
let active_worker =
|
|
||||||
ServiceWorker::install_serviceworker(global, script_url.clone(), scope.clone(), true);
|
|
||||||
active_worker.set_transition_state(ServiceWorkerState::Installed);
|
|
||||||
|
|
||||||
reflect_dom_object(
|
reflect_dom_object(
|
||||||
Box::new(ServiceWorkerRegistration::new_inherited(
|
Box::new(ServiceWorkerRegistration::new_inherited(
|
||||||
&*active_worker,
|
|
||||||
scope,
|
scope,
|
||||||
|
registration_id,
|
||||||
)),
|
)),
|
||||||
global,
|
global,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active(&self) -> Option<&ServiceWorker> {
|
/// Does this registration have an active worker?
|
||||||
self.active.as_ref().map(|sw| &**sw)
|
pub fn is_active(&self) -> bool {
|
||||||
|
self.active.borrow().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_installed(&self) -> &ServiceWorker {
|
pub fn set_installing(&self, worker: &ServiceWorker) {
|
||||||
self.active.as_ref().unwrap()
|
*self.installing.borrow_mut() = Some(Dom::from_ref(worker));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_navigation_preload_header_value(&self) -> Option<ByteString> {
|
pub fn get_navigation_preload_header_value(&self) -> Option<ByteString> {
|
||||||
|
@ -124,13 +126,14 @@ impl ServiceWorkerRegistration {
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#get-newest-worker-algorithm
|
// https://w3c.github.io/ServiceWorker/#get-newest-worker-algorithm
|
||||||
pub fn get_newest_worker(&self) -> Option<DomRoot<ServiceWorker>> {
|
pub fn get_newest_worker(&self) -> Option<DomRoot<ServiceWorker>> {
|
||||||
if self.installing.as_ref().is_some() {
|
let installing = self.installing.borrow();
|
||||||
self.installing.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
let waiting = self.waiting.borrow();
|
||||||
} else if self.waiting.as_ref().is_some() {
|
let active = self.active.borrow();
|
||||||
self.waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
installing
|
||||||
} else {
|
.as_ref()
|
||||||
self.active.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
.map(|sw| DomRoot::from_ref(&**sw))
|
||||||
}
|
.or_else(|| waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
|
||||||
|
.or_else(|| active.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,17 +157,26 @@ pub fn longest_prefix_match(stored_scope: &ServoUrl, potential_match: &ServoUrl)
|
||||||
impl ServiceWorkerRegistrationMethods for ServiceWorkerRegistration {
|
impl ServiceWorkerRegistrationMethods for ServiceWorkerRegistration {
|
||||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-installing-attribute
|
// https://w3c.github.io/ServiceWorker/#service-worker-registration-installing-attribute
|
||||||
fn GetInstalling(&self) -> Option<DomRoot<ServiceWorker>> {
|
fn GetInstalling(&self) -> Option<DomRoot<ServiceWorker>> {
|
||||||
self.installing.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
self.installing
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.map(|sw| DomRoot::from_ref(&**sw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-active-attribute
|
// https://w3c.github.io/ServiceWorker/#service-worker-registration-active-attribute
|
||||||
fn GetActive(&self) -> Option<DomRoot<ServiceWorker>> {
|
fn GetActive(&self) -> Option<DomRoot<ServiceWorker>> {
|
||||||
self.active.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
self.active
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.map(|sw| DomRoot::from_ref(&**sw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-waiting-attribute
|
// https://w3c.github.io/ServiceWorker/#service-worker-registration-waiting-attribute
|
||||||
fn GetWaiting(&self) -> Option<DomRoot<ServiceWorker>> {
|
fn GetWaiting(&self) -> Option<DomRoot<ServiceWorker>> {
|
||||||
self.waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
self.waiting
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.map(|sw| DomRoot::from_ref(&**sw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-scope-attribute
|
// https://w3c.github.io/ServiceWorker/#service-worker-registration-scope-attribute
|
||||||
|
|
|
@ -92,8 +92,6 @@ pub mod script_thread;
|
||||||
#[warn(deprecated)]
|
#[warn(deprecated)]
|
||||||
pub mod serviceworker_manager;
|
pub mod serviceworker_manager;
|
||||||
#[warn(deprecated)]
|
#[warn(deprecated)]
|
||||||
mod serviceworkerjob;
|
|
||||||
#[warn(deprecated)]
|
|
||||||
mod stylesheet_loader;
|
mod stylesheet_loader;
|
||||||
#[warn(deprecated)]
|
#[warn(deprecated)]
|
||||||
mod stylesheet_set;
|
mod stylesheet_set;
|
||||||
|
|
|
@ -63,7 +63,6 @@ use crate::dom::node::{
|
||||||
use crate::dom::performanceentry::PerformanceEntry;
|
use crate::dom::performanceentry::PerformanceEntry;
|
||||||
use crate::dom::performancepainttiming::PerformancePaintTiming;
|
use crate::dom::performancepainttiming::PerformancePaintTiming;
|
||||||
use crate::dom::serviceworker::TrustedServiceWorkerAddress;
|
use crate::dom::serviceworker::TrustedServiceWorkerAddress;
|
||||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
|
||||||
use crate::dom::servoparser::{ParserContext, ServoParser};
|
use crate::dom::servoparser::{ParserContext, ServoParser};
|
||||||
use crate::dom::transitionevent::TransitionEvent;
|
use crate::dom::transitionevent::TransitionEvent;
|
||||||
use crate::dom::uievent::UIEvent;
|
use crate::dom::uievent::UIEvent;
|
||||||
|
@ -77,7 +76,6 @@ use crate::microtask::{Microtask, MicrotaskQueue};
|
||||||
use crate::realms::enter_realm;
|
use crate::realms::enter_realm;
|
||||||
use crate::script_runtime::{get_reports, new_rt_and_cx, JSContext, Runtime, ScriptPort};
|
use crate::script_runtime::{get_reports, new_rt_and_cx, JSContext, Runtime, ScriptPort};
|
||||||
use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
||||||
use crate::serviceworkerjob::{Job, JobQueue};
|
|
||||||
use crate::task_manager::TaskManager;
|
use crate::task_manager::TaskManager;
|
||||||
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
|
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
|
||||||
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
||||||
|
@ -287,8 +285,6 @@ pub enum MainThreadScriptMsg {
|
||||||
properties: Vec<Atom>,
|
properties: Vec<Atom>,
|
||||||
painter: Box<dyn Painter>,
|
painter: Box<dyn Painter>,
|
||||||
},
|
},
|
||||||
/// Dispatches a job queue.
|
|
||||||
DispatchJobQueue { scope_url: ServoUrl },
|
|
||||||
/// A task related to a not fully-active document has been throttled.
|
/// A task related to a not fully-active document has been throttled.
|
||||||
Inactive,
|
Inactive,
|
||||||
/// Wake-up call from the task queue.
|
/// Wake-up call from the task queue.
|
||||||
|
@ -533,10 +529,6 @@ pub struct ScriptThread {
|
||||||
incomplete_loads: DomRefCell<Vec<InProgressLoad>>,
|
incomplete_loads: DomRefCell<Vec<InProgressLoad>>,
|
||||||
/// A vector containing parser contexts which have not yet been fully processed
|
/// A vector containing parser contexts which have not yet been fully processed
|
||||||
incomplete_parser_contexts: RefCell<IncompleteParserContexts>,
|
incomplete_parser_contexts: RefCell<IncompleteParserContexts>,
|
||||||
/// A map to store service worker registrations for a given origin
|
|
||||||
registration_map: DomRefCell<HashMap<ServoUrl, Dom<ServiceWorkerRegistration>>>,
|
|
||||||
/// A job queue for Service Workers keyed by their scope url
|
|
||||||
job_queue_map: Rc<JobQueue>,
|
|
||||||
/// Image cache for this script thread.
|
/// Image cache for this script thread.
|
||||||
image_cache: Arc<dyn ImageCache>,
|
image_cache: Arc<dyn ImageCache>,
|
||||||
/// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if
|
/// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if
|
||||||
|
@ -927,15 +919,6 @@ impl ScriptThread {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
pub fn schedule_job(job: Job) {
|
|
||||||
SCRIPT_THREAD_ROOT.with(|root| {
|
|
||||||
let script_thread = unsafe { &*root.get().unwrap() };
|
|
||||||
let job_queue = &*script_thread.job_queue_map;
|
|
||||||
job_queue.schedule_job(job, &script_thread);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_event(msg: CommonScriptMsg) {
|
pub fn process_event(msg: CommonScriptMsg) {
|
||||||
SCRIPT_THREAD_ROOT.with(|root| {
|
SCRIPT_THREAD_ROOT.with(|root| {
|
||||||
if let Some(script_thread) = root.get() {
|
if let Some(script_thread) = root.get() {
|
||||||
|
@ -1317,8 +1300,6 @@ impl ScriptThread {
|
||||||
window_proxies: DomRefCell::new(HashMap::new()),
|
window_proxies: DomRefCell::new(HashMap::new()),
|
||||||
incomplete_loads: DomRefCell::new(vec![]),
|
incomplete_loads: DomRefCell::new(vec![]),
|
||||||
incomplete_parser_contexts: RefCell::new(vec![]),
|
incomplete_parser_contexts: RefCell::new(vec![]),
|
||||||
registration_map: DomRefCell::new(HashMap::new()),
|
|
||||||
job_queue_map: Rc::new(JobQueue::new()),
|
|
||||||
|
|
||||||
image_cache: state.image_cache.clone(),
|
image_cache: state.image_cache.clone(),
|
||||||
image_cache_channel: image_cache_channel,
|
image_cache_channel: image_cache_channel,
|
||||||
|
@ -1774,7 +1755,6 @@ impl ScriptThread {
|
||||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
||||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
||||||
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
||||||
MainThreadScriptMsg::DispatchJobQueue { .. } => None,
|
|
||||||
MainThreadScriptMsg::Inactive => None,
|
MainThreadScriptMsg::Inactive => None,
|
||||||
MainThreadScriptMsg::WakeUp => None,
|
MainThreadScriptMsg::WakeUp => None,
|
||||||
},
|
},
|
||||||
|
@ -2008,9 +1988,6 @@ impl ScriptThread {
|
||||||
properties,
|
properties,
|
||||||
painter,
|
painter,
|
||||||
} => self.handle_register_paint_worklet(pipeline_id, name, properties, painter),
|
} => self.handle_register_paint_worklet(pipeline_id, name, properties, painter),
|
||||||
MainThreadScriptMsg::DispatchJobQueue { scope_url } => {
|
|
||||||
self.job_queue_map.run_job(scope_url, self)
|
|
||||||
},
|
|
||||||
MainThreadScriptMsg::Inactive => {},
|
MainThreadScriptMsg::Inactive => {},
|
||||||
MainThreadScriptMsg::WakeUp => {},
|
MainThreadScriptMsg::WakeUp => {},
|
||||||
}
|
}
|
||||||
|
@ -2720,57 +2697,6 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_get_registration(
|
|
||||||
&self,
|
|
||||||
scope_url: &ServoUrl,
|
|
||||||
) -> Option<DomRoot<ServiceWorkerRegistration>> {
|
|
||||||
let maybe_registration_ref = self.registration_map.borrow();
|
|
||||||
maybe_registration_ref
|
|
||||||
.get(scope_url)
|
|
||||||
.map(|x| DomRoot::from_ref(&**x))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_serviceworker_registration(
|
|
||||||
&self,
|
|
||||||
scope: &ServoUrl,
|
|
||||||
registration: &ServiceWorkerRegistration,
|
|
||||||
pipeline_id: PipelineId,
|
|
||||||
) {
|
|
||||||
{
|
|
||||||
let ref mut reg_ref = *self.registration_map.borrow_mut();
|
|
||||||
// according to spec we should replace if an older registration exists for
|
|
||||||
// same scope otherwise just insert the new one
|
|
||||||
let _ = reg_ref.remove(scope);
|
|
||||||
reg_ref.insert(scope.clone(), Dom::from_ref(registration));
|
|
||||||
}
|
|
||||||
|
|
||||||
// send ScopeThings to sw-manager
|
|
||||||
let ref maybe_registration_ref = *self.registration_map.borrow();
|
|
||||||
let maybe_registration = match maybe_registration_ref.get(scope) {
|
|
||||||
Some(r) => r,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let window = match self.documents.borrow().find_window(pipeline_id) {
|
|
||||||
Some(window) => window,
|
|
||||||
None => return warn!("Registration failed for {}", scope),
|
|
||||||
};
|
|
||||||
|
|
||||||
let script_url = maybe_registration.get_installed().get_script_url();
|
|
||||||
let scope_things =
|
|
||||||
ServiceWorkerRegistration::create_scope_things(window.upcast(), script_url);
|
|
||||||
let _ = self.script_sender.send((
|
|
||||||
pipeline_id,
|
|
||||||
ScriptMsg::RegisterServiceWorker(scope_things, scope.clone()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn schedule_job_queue(&self, scope_url: ServoUrl) {
|
|
||||||
let _ = self
|
|
||||||
.chan
|
|
||||||
.0
|
|
||||||
.send(MainThreadScriptMsg::DispatchJobQueue { scope_url });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dom_manipulation_task_source(
|
pub fn dom_manipulation_task_source(
|
||||||
&self,
|
&self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
|
|
@ -13,10 +13,12 @@ use crate::dom::serviceworkerregistration::longest_prefix_match;
|
||||||
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
|
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
|
use msg::constellation_msg::PipelineNamespace;
|
||||||
|
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||||
use net_traits::{CoreResourceMsg, CustomResponseMediator};
|
use net_traits::{CoreResourceMsg, CustomResponseMediator};
|
||||||
use script_traits::{
|
use script_traits::{
|
||||||
DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerManagerFactory,
|
DOMMessage, Job, JobError, JobResult, JobResultValue, JobType, SWManagerMsg, SWManagerSenders,
|
||||||
ServiceWorkerMsg,
|
ScopeThings, ServiceWorkerManagerFactory, ServiceWorkerMsg,
|
||||||
};
|
};
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
|
@ -29,11 +31,112 @@ enum Message {
|
||||||
FromConstellation(ServiceWorkerMsg),
|
FromConstellation(ServiceWorkerMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-service-worker>
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct ServiceWorker {
|
||||||
|
/// A unique identifer.
|
||||||
|
pub id: ServiceWorkerId,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-script-url>
|
||||||
|
pub script_url: ServoUrl,
|
||||||
|
/// A sender to the running service worker scope.
|
||||||
|
pub sender: Sender<ServiceWorkerScriptMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceWorker {
|
||||||
|
fn new(
|
||||||
|
script_url: ServoUrl,
|
||||||
|
sender: Sender<ServiceWorkerScriptMsg>,
|
||||||
|
id: ServiceWorkerId,
|
||||||
|
) -> ServiceWorker {
|
||||||
|
ServiceWorker {
|
||||||
|
id,
|
||||||
|
script_url,
|
||||||
|
sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forward a DOM message to the running service worker scope.
|
||||||
|
fn forward_dom_message(&self, msg: DOMMessage) {
|
||||||
|
let DOMMessage { origin, data } = msg;
|
||||||
|
let _ = self.sender.send(ServiceWorkerScriptMsg::CommonWorker(
|
||||||
|
WorkerScriptMsg::DOMMessage { origin, data },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a message to the running service worker scope.
|
||||||
|
fn send_message(&self, msg: ServiceWorkerScriptMsg) {
|
||||||
|
let _ = self.sender.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When updating a registration, which worker are we targetting?
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum RegistrationUpdateTarget {
|
||||||
|
Installing,
|
||||||
|
Waiting,
|
||||||
|
Active,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#service-worker-registration-concept
|
||||||
|
struct ServiceWorkerRegistration {
|
||||||
|
/// A unique identifer.
|
||||||
|
id: ServiceWorkerRegistrationId,
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#dfn-active-worker
|
||||||
|
active_worker: Option<ServiceWorker>,
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#dfn-waiting-worker
|
||||||
|
waiting_worker: Option<ServiceWorker>,
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#dfn-installing-worker
|
||||||
|
installing_worker: Option<ServiceWorker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceWorkerRegistration {
|
||||||
|
pub fn new() -> ServiceWorkerRegistration {
|
||||||
|
ServiceWorkerRegistration {
|
||||||
|
id: ServiceWorkerRegistrationId::new(),
|
||||||
|
active_worker: None,
|
||||||
|
waiting_worker: None,
|
||||||
|
installing_worker: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#get-newest-worker>
|
||||||
|
fn get_newest_worker(&self) -> Option<ServiceWorker> {
|
||||||
|
if let Some(worker) = self.active_worker.as_ref() {
|
||||||
|
return Some(worker.clone());
|
||||||
|
}
|
||||||
|
if let Some(worker) = self.waiting_worker.as_ref() {
|
||||||
|
return Some(worker.clone());
|
||||||
|
}
|
||||||
|
if let Some(worker) = self.installing_worker.as_ref() {
|
||||||
|
return Some(worker.clone());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#update-registration-state>
|
||||||
|
fn update_registration_state(
|
||||||
|
&mut self,
|
||||||
|
target: RegistrationUpdateTarget,
|
||||||
|
worker: ServiceWorker,
|
||||||
|
) {
|
||||||
|
match target {
|
||||||
|
RegistrationUpdateTarget::Active => {
|
||||||
|
self.active_worker = Some(worker);
|
||||||
|
},
|
||||||
|
RegistrationUpdateTarget::Waiting => {
|
||||||
|
self.waiting_worker = Some(worker);
|
||||||
|
},
|
||||||
|
RegistrationUpdateTarget::Installing => {
|
||||||
|
self.installing_worker = Some(worker);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A structure managing all registrations and workers for a given origin.
|
||||||
pub struct ServiceWorkerManager {
|
pub struct ServiceWorkerManager {
|
||||||
// map of registered service worker descriptors
|
/// https://w3c.github.io/ServiceWorker/#dfn-scope-to-registration-map
|
||||||
registered_workers: HashMap<ServoUrl, ScopeThings>,
|
registrations: HashMap<ServoUrl, ServiceWorkerRegistration>,
|
||||||
// map of active service worker descriptors
|
|
||||||
active_workers: HashMap<ServoUrl, Sender<ServiceWorkerScriptMsg>>,
|
|
||||||
// Will be useful to implement posting a message to a client.
|
// Will be useful to implement posting a message to a client.
|
||||||
// See https://github.com/servo/servo/issues/24660
|
// See https://github.com/servo/servo/issues/24660
|
||||||
_constellation_sender: IpcSender<SWManagerMsg>,
|
_constellation_sender: IpcSender<SWManagerMsg>,
|
||||||
|
@ -52,9 +155,11 @@ impl ServiceWorkerManager {
|
||||||
resource_port: Receiver<CustomResponseMediator>,
|
resource_port: Receiver<CustomResponseMediator>,
|
||||||
constellation_sender: IpcSender<SWManagerMsg>,
|
constellation_sender: IpcSender<SWManagerMsg>,
|
||||||
) -> ServiceWorkerManager {
|
) -> ServiceWorkerManager {
|
||||||
|
// Install a pipeline-namespace in the current thread.
|
||||||
|
PipelineNamespace::auto_install();
|
||||||
|
|
||||||
ServiceWorkerManager {
|
ServiceWorkerManager {
|
||||||
registered_workers: HashMap::new(),
|
registrations: HashMap::new(),
|
||||||
active_workers: HashMap::new(),
|
|
||||||
own_sender: own_sender,
|
own_sender: own_sender,
|
||||||
own_port: from_constellation_receiver,
|
own_port: from_constellation_receiver,
|
||||||
resource_receiver: resource_port,
|
resource_receiver: resource_port,
|
||||||
|
@ -63,7 +168,7 @@ impl ServiceWorkerManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_matching_scope(&self, load_url: &ServoUrl) -> Option<ServoUrl> {
|
pub fn get_matching_scope(&self, load_url: &ServoUrl) -> Option<ServoUrl> {
|
||||||
for scope in self.registered_workers.keys() {
|
for scope in self.registrations.keys() {
|
||||||
if longest_prefix_match(&scope, load_url) {
|
if longest_prefix_match(&scope, load_url) {
|
||||||
return Some(scope.clone());
|
return Some(scope.clone());
|
||||||
}
|
}
|
||||||
|
@ -71,31 +176,6 @@ impl ServiceWorkerManager {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wakeup_serviceworker(
|
|
||||||
&mut self,
|
|
||||||
scope_url: ServoUrl,
|
|
||||||
) -> Option<Sender<ServiceWorkerScriptMsg>> {
|
|
||||||
let scope_things = self.registered_workers.get(&scope_url);
|
|
||||||
if let Some(scope_things) = scope_things {
|
|
||||||
let (sender, receiver) = unbounded();
|
|
||||||
let (_devtools_sender, devtools_receiver) = ipc::channel().unwrap();
|
|
||||||
ServiceWorkerGlobalScope::run_serviceworker_scope(
|
|
||||||
scope_things.clone(),
|
|
||||||
sender.clone(),
|
|
||||||
receiver,
|
|
||||||
devtools_receiver,
|
|
||||||
self.own_sender.clone(),
|
|
||||||
scope_url.clone(),
|
|
||||||
);
|
|
||||||
// We store the activated worker
|
|
||||||
self.active_workers.insert(scope_url, sender.clone());
|
|
||||||
return Some(sender);
|
|
||||||
} else {
|
|
||||||
warn!("Unable to activate service worker");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_message(&mut self) {
|
fn handle_message(&mut self) {
|
||||||
while let Ok(message) = self.receive_message() {
|
while let Ok(message) = self.receive_message() {
|
||||||
let should_continue = match message {
|
let should_continue = match message {
|
||||||
|
@ -108,65 +188,18 @@ impl ServiceWorkerManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
|
|
||||||
let DOMMessage { origin, data } = msg;
|
|
||||||
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
|
|
||||||
WorkerScriptMsg::DOMMessage { origin, data },
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool {
|
|
||||||
match msg {
|
|
||||||
ServiceWorkerMsg::RegisterServiceWorker(scope_things, scope) => {
|
|
||||||
if self.registered_workers.contains_key(&scope) {
|
|
||||||
warn!("ScopeThings for {:?} already stored in SW-Manager", scope);
|
|
||||||
} else {
|
|
||||||
self.registered_workers.insert(scope, scope_things);
|
|
||||||
}
|
|
||||||
true
|
|
||||||
},
|
|
||||||
ServiceWorkerMsg::Timeout(scope) => {
|
|
||||||
if self.active_workers.contains_key(&scope) {
|
|
||||||
let _ = self.active_workers.remove(&scope);
|
|
||||||
} else {
|
|
||||||
warn!("ServiceWorker for {:?} is not active", scope);
|
|
||||||
}
|
|
||||||
true
|
|
||||||
},
|
|
||||||
ServiceWorkerMsg::ForwardDOMMessage(msg, scope_url) => {
|
|
||||||
if self.active_workers.contains_key(&scope_url) {
|
|
||||||
if let Some(ref sender) = self.active_workers.get(&scope_url) {
|
|
||||||
self.forward_message(msg, &sender);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(ref sender) = self.wakeup_serviceworker(scope_url) {
|
|
||||||
self.forward_message(msg, &sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
},
|
|
||||||
ServiceWorkerMsg::Exit => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_message_from_resource(&mut self, mediator: CustomResponseMediator) -> bool {
|
fn handle_message_from_resource(&mut self, mediator: CustomResponseMediator) -> bool {
|
||||||
if serviceworker_enabled() {
|
if serviceworker_enabled() {
|
||||||
if let Some(scope) = self.get_matching_scope(&mediator.load_url) {
|
if let Some(scope) = self.get_matching_scope(&mediator.load_url) {
|
||||||
if self.active_workers.contains_key(&scope) {
|
if let Some(registration) = self.registrations.get(&scope) {
|
||||||
if let Some(sender) = self.active_workers.get(&scope) {
|
if let Some(ref worker) = registration.active_worker {
|
||||||
let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator));
|
worker.send_message(ServiceWorkerScriptMsg::Response(mediator));
|
||||||
}
|
return true;
|
||||||
} else {
|
}
|
||||||
if let Some(sender) = self.wakeup_serviceworker(scope) {
|
}
|
||||||
let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let _ = mediator.response_chan.send(None);
|
let _ = mediator.response_chan.send(None);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let _ = mediator.response_chan.send(None);
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +209,173 @@ impl ServiceWorkerManager {
|
||||||
recv(self.resource_receiver) -> msg => msg.map(Message::FromResource),
|
recv(self.resource_receiver) -> msg => msg.map(Message::FromResource),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool {
|
||||||
|
match msg {
|
||||||
|
ServiceWorkerMsg::Timeout(scope) => {},
|
||||||
|
ServiceWorkerMsg::ForwardDOMMessage(msg, scope_url) => {
|
||||||
|
if let Some(registration) = self.registrations.get_mut(&scope_url) {
|
||||||
|
if let Some(ref worker) = registration.active_worker {
|
||||||
|
worker.forward_dom_message(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ServiceWorkerMsg::ScheduleJob(job) => match job.job_type {
|
||||||
|
JobType::Register => {
|
||||||
|
self.handle_register_job(job);
|
||||||
|
},
|
||||||
|
JobType::Update => {
|
||||||
|
self.handle_update_job(job);
|
||||||
|
},
|
||||||
|
JobType::Unregister => {
|
||||||
|
// TODO: https://w3c.github.io/ServiceWorker/#unregister-algorithm
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServiceWorkerMsg::Exit => return false,
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#register-algorithm>
|
||||||
|
fn handle_register_job(&mut self, mut job: Job) {
|
||||||
|
if !job.script_url.is_origin_trustworthy() {
|
||||||
|
// Step 1.1
|
||||||
|
let _ = job
|
||||||
|
.client
|
||||||
|
.send(JobResult::RejectPromise(JobError::SecurityError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if job.script_url.origin() != job.referrer.origin() ||
|
||||||
|
job.scope_url.origin() != job.referrer.origin()
|
||||||
|
{
|
||||||
|
// Step 2.1
|
||||||
|
let _ = job
|
||||||
|
.client
|
||||||
|
.send(JobResult::RejectPromise(JobError::SecurityError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Get registration.
|
||||||
|
if let Some(registration) = self.registrations.get(&job.scope_url) {
|
||||||
|
// Step 5, we have a registation.
|
||||||
|
|
||||||
|
// Step 5.1, get newest worker
|
||||||
|
let newest_worker = registration.get_newest_worker();
|
||||||
|
|
||||||
|
// step 5.2
|
||||||
|
if newest_worker.is_some() {
|
||||||
|
// TODO: the various checks of job versus worker.
|
||||||
|
|
||||||
|
// Step 2.1: Run resolve job.
|
||||||
|
let client = job.client.clone();
|
||||||
|
let _ = client.send(JobResult::ResolvePromise(
|
||||||
|
job,
|
||||||
|
JobResultValue::Registration {
|
||||||
|
id: registration.id,
|
||||||
|
installing_worker: registration
|
||||||
|
.installing_worker
|
||||||
|
.as_ref()
|
||||||
|
.map(|worker| worker.id),
|
||||||
|
waiting_worker: registration
|
||||||
|
.waiting_worker
|
||||||
|
.as_ref()
|
||||||
|
.map(|worker| worker.id),
|
||||||
|
active_worker: registration.active_worker.as_ref().map(|worker| worker.id),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Step 6: we do not have a registration.
|
||||||
|
|
||||||
|
// Step 6.1: Run Set Registration.
|
||||||
|
let new_registration = ServiceWorkerRegistration::new();
|
||||||
|
self.registrations
|
||||||
|
.insert(job.scope_url.clone(), new_registration);
|
||||||
|
|
||||||
|
// Step 7: Schedule update
|
||||||
|
job.job_type = JobType::Update;
|
||||||
|
let _ = self.own_sender.send(ServiceWorkerMsg::ScheduleJob(job));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#update>
|
||||||
|
fn handle_update_job(&mut self, job: Job) {
|
||||||
|
// Step 1: Get registation
|
||||||
|
if let Some(registration) = self.registrations.get_mut(&job.scope_url) {
|
||||||
|
// Step 3.
|
||||||
|
let newest_worker = registration.get_newest_worker();
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
if let Some(worker) = newest_worker {
|
||||||
|
if worker.script_url != job.script_url {
|
||||||
|
let _ = job
|
||||||
|
.client
|
||||||
|
.send(JobResult::RejectPromise(JobError::TypeError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope_things = job
|
||||||
|
.scope_things
|
||||||
|
.clone()
|
||||||
|
.expect("Update job should have scope things.");
|
||||||
|
|
||||||
|
// Very roughly steps 5 to 18.
|
||||||
|
// TODO: implement all steps precisely.
|
||||||
|
let new_worker =
|
||||||
|
update_serviceworker(self.own_sender.clone(), job.scope_url.clone(), scope_things);
|
||||||
|
|
||||||
|
// Step 19, run Install.
|
||||||
|
|
||||||
|
// Install: Step 4, run Update Registration State.
|
||||||
|
registration
|
||||||
|
.update_registration_state(RegistrationUpdateTarget::Installing, new_worker);
|
||||||
|
|
||||||
|
// Install: Step 7, run Resolve Job Promise.
|
||||||
|
let client = job.client.clone();
|
||||||
|
let _ = client.send(JobResult::ResolvePromise(
|
||||||
|
job,
|
||||||
|
JobResultValue::Registration {
|
||||||
|
id: registration.id,
|
||||||
|
installing_worker: registration
|
||||||
|
.installing_worker
|
||||||
|
.as_ref()
|
||||||
|
.map(|worker| worker.id),
|
||||||
|
waiting_worker: registration.waiting_worker.as_ref().map(|worker| worker.id),
|
||||||
|
active_worker: registration.active_worker.as_ref().map(|worker| worker.id),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// Step 2
|
||||||
|
let _ = job
|
||||||
|
.client
|
||||||
|
.send(JobResult::RejectPromise(JobError::TypeError));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#update-algorithm>
|
||||||
|
fn update_serviceworker(
|
||||||
|
own_sender: IpcSender<ServiceWorkerMsg>,
|
||||||
|
scope_url: ServoUrl,
|
||||||
|
scope_things: ScopeThings,
|
||||||
|
) -> ServiceWorker {
|
||||||
|
let (sender, receiver) = unbounded();
|
||||||
|
let (_devtools_sender, devtools_receiver) = ipc::channel().unwrap();
|
||||||
|
let worker_id = ServiceWorkerId::new();
|
||||||
|
|
||||||
|
ServiceWorkerGlobalScope::run_serviceworker_scope(
|
||||||
|
scope_things.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
receiver,
|
||||||
|
devtools_receiver,
|
||||||
|
own_sender,
|
||||||
|
scope_url.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
ServiceWorker::new(scope_things.script_url, sender, worker_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceWorkerManagerFactory for ServiceWorkerManager {
|
impl ServiceWorkerManagerFactory for ServiceWorkerManager {
|
||||||
|
|
|
@ -1,342 +0,0 @@
|
||||||
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
//! A Job is an abstraction of async operation in service worker lifecycle propagation.
|
|
||||||
//! Each Job is uniquely identified by its scope_url, and is keyed accordingly under
|
|
||||||
//! the script thread. The script thread contains a JobQueue, which stores all scheduled Jobs
|
|
||||||
//! by multiple service worker clients in a Vec.
|
|
||||||
|
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
|
||||||
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
|
|
||||||
use crate::dom::bindings::error::Error;
|
|
||||||
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
|
||||||
use crate::dom::bindings::reflector::DomObject;
|
|
||||||
use crate::dom::bindings::root::Dom;
|
|
||||||
use crate::dom::client::Client;
|
|
||||||
use crate::dom::promise::Promise;
|
|
||||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
|
||||||
use crate::script_thread::ScriptThread;
|
|
||||||
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
|
||||||
use crate::task_source::TaskSource;
|
|
||||||
use servo_url::ServoUrl;
|
|
||||||
use std::cmp::PartialEq;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, JSTraceable, PartialEq)]
|
|
||||||
pub enum JobType {
|
|
||||||
Register,
|
|
||||||
Unregister,
|
|
||||||
Update,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum SettleType {
|
|
||||||
Resolve(Trusted<ServiceWorkerRegistration>),
|
|
||||||
Reject(Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unrooted_must_root_lint::must_root]
|
|
||||||
#[derive(JSTraceable)]
|
|
||||||
pub struct Job {
|
|
||||||
pub job_type: JobType,
|
|
||||||
pub scope_url: ServoUrl,
|
|
||||||
pub script_url: ServoUrl,
|
|
||||||
pub promise: Rc<Promise>,
|
|
||||||
pub equivalent_jobs: Vec<Job>,
|
|
||||||
pub worker_type: WorkerType,
|
|
||||||
// client can be a window client, worker client so `Client` will be an enum in future
|
|
||||||
pub client: Dom<Client>,
|
|
||||||
pub referrer: ServoUrl,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Job {
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
// https://w3c.github.io/ServiceWorker/#create-job-algorithm
|
|
||||||
pub fn create_job(
|
|
||||||
job_type: JobType,
|
|
||||||
scope_url: ServoUrl,
|
|
||||||
script_url: ServoUrl,
|
|
||||||
promise: Rc<Promise>,
|
|
||||||
worker_type: WorkerType,
|
|
||||||
client: &Client,
|
|
||||||
) -> Job {
|
|
||||||
Job {
|
|
||||||
job_type: job_type,
|
|
||||||
scope_url: scope_url,
|
|
||||||
script_url: script_url,
|
|
||||||
promise: promise,
|
|
||||||
equivalent_jobs: vec![],
|
|
||||||
worker_type,
|
|
||||||
client: Dom::from_ref(client),
|
|
||||||
referrer: client.creation_url(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
pub fn append_equivalent_job(&mut self, job: Job) {
|
|
||||||
self.equivalent_jobs.push(job);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Job {
|
|
||||||
// Equality criteria as described in https://w3c.github.io/ServiceWorker/#dfn-job-equivalent
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
let same_job = self.job_type == other.job_type;
|
|
||||||
if same_job {
|
|
||||||
match self.job_type {
|
|
||||||
JobType::Register | JobType::Update => {
|
|
||||||
self.scope_url == other.scope_url && self.script_url == other.script_url
|
|
||||||
},
|
|
||||||
JobType::Unregister => self.scope_url == other.scope_url,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unrooted_must_root_lint::must_root]
|
|
||||||
#[derive(JSTraceable)]
|
|
||||||
pub struct JobQueue(pub DomRefCell<HashMap<ServoUrl, Vec<Job>>>);
|
|
||||||
|
|
||||||
impl JobQueue {
|
|
||||||
pub fn new() -> JobQueue {
|
|
||||||
JobQueue(DomRefCell::new(HashMap::new()))
|
|
||||||
}
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
// https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
|
|
||||||
pub fn schedule_job(&self, job: Job, script_thread: &ScriptThread) {
|
|
||||||
debug!("scheduling {:?} job", job.job_type);
|
|
||||||
let mut queue_ref = self.0.borrow_mut();
|
|
||||||
let job_queue = queue_ref.entry(job.scope_url.clone()).or_insert(vec![]);
|
|
||||||
// Step 1
|
|
||||||
if job_queue.is_empty() {
|
|
||||||
let scope_url = job.scope_url.clone();
|
|
||||||
job_queue.push(job);
|
|
||||||
let _ = script_thread.schedule_job_queue(scope_url);
|
|
||||||
debug!("queued task to run newly-queued job");
|
|
||||||
} else {
|
|
||||||
// Step 2
|
|
||||||
let mut last_job = job_queue.pop().unwrap();
|
|
||||||
if job == last_job && !last_job.promise.is_fulfilled() {
|
|
||||||
last_job.append_equivalent_job(job);
|
|
||||||
job_queue.push(last_job);
|
|
||||||
debug!("appended equivalent job");
|
|
||||||
} else {
|
|
||||||
// restore the popped last_job
|
|
||||||
job_queue.push(last_job);
|
|
||||||
// and push this new job to job queue
|
|
||||||
job_queue.push(job);
|
|
||||||
debug!("pushed onto job queue job");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
// https://w3c.github.io/ServiceWorker/#run-job-algorithm
|
|
||||||
pub fn run_job(&self, scope_url: ServoUrl, script_thread: &ScriptThread) {
|
|
||||||
debug!("running a job");
|
|
||||||
let url = {
|
|
||||||
let queue_ref = self.0.borrow();
|
|
||||||
let front_job = {
|
|
||||||
let job_vec = queue_ref.get(&scope_url);
|
|
||||||
job_vec.unwrap().first().unwrap()
|
|
||||||
};
|
|
||||||
let front_scope_url = front_job.scope_url.clone();
|
|
||||||
match front_job.job_type {
|
|
||||||
JobType::Register => self.run_register(front_job, scope_url, script_thread),
|
|
||||||
JobType::Update => self.update(front_job, script_thread),
|
|
||||||
JobType::Unregister => unreachable!(),
|
|
||||||
};
|
|
||||||
front_scope_url
|
|
||||||
};
|
|
||||||
self.finish_job(url, script_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
// https://w3c.github.io/ServiceWorker/#register-algorithm
|
|
||||||
fn run_register(&self, job: &Job, scope_url: ServoUrl, script_thread: &ScriptThread) {
|
|
||||||
debug!("running register job");
|
|
||||||
let global = &*job.client.global();
|
|
||||||
let pipeline_id = global.pipeline_id();
|
|
||||||
// Step 1-3
|
|
||||||
if !job.script_url.is_origin_trustworthy() {
|
|
||||||
// Step 1.1
|
|
||||||
reject_job_promise(
|
|
||||||
job,
|
|
||||||
Error::Type("Invalid script ServoURL".to_owned()),
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 1.2 (see run_job)
|
|
||||||
return;
|
|
||||||
} else if job.script_url.origin() != job.referrer.origin() ||
|
|
||||||
job.scope_url.origin() != job.referrer.origin()
|
|
||||||
{
|
|
||||||
// Step 2.1/3.1
|
|
||||||
reject_job_promise(
|
|
||||||
job,
|
|
||||||
Error::Security,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 2.2/3.2 (see run_job)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4-5
|
|
||||||
if let Some(reg) = script_thread.handle_get_registration(&job.scope_url) {
|
|
||||||
// Step 5.1
|
|
||||||
if reg.get_uninstalling() {
|
|
||||||
reg.set_uninstalling(false);
|
|
||||||
}
|
|
||||||
// Step 5.3
|
|
||||||
if let Some(ref newest_worker) = reg.get_newest_worker() {
|
|
||||||
if (&*newest_worker).get_script_url() == job.script_url {
|
|
||||||
// Step 5.3.1
|
|
||||||
resolve_job_promise(
|
|
||||||
job,
|
|
||||||
&*reg,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 5.3.2 (see run_job)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Step 6.1
|
|
||||||
let new_reg = ServiceWorkerRegistration::new(&*global, &job.script_url, scope_url);
|
|
||||||
script_thread.handle_serviceworker_registration(&job.scope_url, &*new_reg, pipeline_id);
|
|
||||||
}
|
|
||||||
// Step 7
|
|
||||||
self.update(job, script_thread)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
// https://w3c.github.io/ServiceWorker/#finish-job-algorithm
|
|
||||||
pub fn finish_job(&self, scope_url: ServoUrl, script_thread: &ScriptThread) {
|
|
||||||
debug!("finishing previous job");
|
|
||||||
let run_job = if let Some(job_vec) = (*self.0.borrow_mut()).get_mut(&scope_url) {
|
|
||||||
assert_eq!(job_vec.first().as_ref().unwrap().scope_url, scope_url);
|
|
||||||
let _ = job_vec.remove(0);
|
|
||||||
!job_vec.is_empty()
|
|
||||||
} else {
|
|
||||||
warn!("non-existent job vector for Servourl: {:?}", scope_url);
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if run_job {
|
|
||||||
debug!("further jobs in queue after finishing");
|
|
||||||
self.run_job(scope_url, script_thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#update-algorithm
|
|
||||||
fn update(&self, job: &Job, script_thread: &ScriptThread) {
|
|
||||||
debug!("running update job");
|
|
||||||
|
|
||||||
let global = &*job.client.global();
|
|
||||||
let pipeline_id = global.pipeline_id();
|
|
||||||
// Step 1
|
|
||||||
let reg = match script_thread.handle_get_registration(&job.scope_url) {
|
|
||||||
Some(reg) => reg,
|
|
||||||
None => {
|
|
||||||
let err_type = Error::Type("No registration to update".to_owned());
|
|
||||||
// Step 2.1
|
|
||||||
reject_job_promise(
|
|
||||||
job,
|
|
||||||
err_type,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 2.2 (see run_job)
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Step 2
|
|
||||||
if reg.get_uninstalling() {
|
|
||||||
let err_type = Error::Type("Update called on an uninstalling registration".to_owned());
|
|
||||||
// Step 2.1
|
|
||||||
reject_job_promise(
|
|
||||||
job,
|
|
||||||
err_type,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 2.2 (see run_job)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Step 3
|
|
||||||
let newest_worker = reg.get_newest_worker();
|
|
||||||
let newest_worker_url = newest_worker.as_ref().map(|w| w.get_script_url());
|
|
||||||
// Step 4
|
|
||||||
if newest_worker_url.as_ref() == Some(&job.script_url) && job.job_type == JobType::Update {
|
|
||||||
let err_type = Error::Type("Invalid script ServoURL".to_owned());
|
|
||||||
// Step 4.1
|
|
||||||
reject_job_promise(
|
|
||||||
job,
|
|
||||||
err_type,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 4.2 (see run_job)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Step 8
|
|
||||||
if let Some(newest_worker) = newest_worker {
|
|
||||||
job.client.set_controller(&*newest_worker);
|
|
||||||
// Step 8.1
|
|
||||||
resolve_job_promise(
|
|
||||||
job,
|
|
||||||
&*reg,
|
|
||||||
&script_thread.dom_manipulation_task_source(pipeline_id),
|
|
||||||
);
|
|
||||||
// Step 8.2 present in run_job
|
|
||||||
}
|
|
||||||
// TODO Step 9 (create new service worker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn settle_job_promise(promise: &Promise, settle: SettleType) {
|
|
||||||
match settle {
|
|
||||||
SettleType::Resolve(reg) => promise.resolve_native(&*reg.root()),
|
|
||||||
SettleType::Reject(err) => promise.reject_error(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
|
||||||
fn queue_settle_promise_for_job(
|
|
||||||
job: &Job,
|
|
||||||
settle: SettleType,
|
|
||||||
task_source: &DOMManipulationTaskSource,
|
|
||||||
) {
|
|
||||||
let global = job.client.global();
|
|
||||||
let promise = TrustedPromise::new(job.promise.clone());
|
|
||||||
// FIXME(nox): Why are errors silenced here?
|
|
||||||
let _ = task_source.queue(
|
|
||||||
task!(settle_promise_for_job: move || {
|
|
||||||
let promise = promise.root();
|
|
||||||
settle_job_promise(&promise, settle)
|
|
||||||
}),
|
|
||||||
&*global,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://w3c.github.io/ServiceWorker/#reject-job-promise-algorithm
|
|
||||||
// https://w3c.github.io/ServiceWorker/#resolve-job-promise-algorithm
|
|
||||||
fn queue_settle_promise(job: &Job, settle: SettleType, task_source: &DOMManipulationTaskSource) {
|
|
||||||
// Step 1
|
|
||||||
queue_settle_promise_for_job(job, settle.clone(), task_source);
|
|
||||||
// Step 2
|
|
||||||
for job in &job.equivalent_jobs {
|
|
||||||
queue_settle_promise_for_job(job, settle.clone(), task_source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reject_job_promise(job: &Job, err: Error, task_source: &DOMManipulationTaskSource) {
|
|
||||||
queue_settle_promise(job, SettleType::Reject(err), task_source)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_job_promise(
|
|
||||||
job: &Job,
|
|
||||||
reg: &ServiceWorkerRegistration,
|
|
||||||
task_source: &DOMManipulationTaskSource,
|
|
||||||
) {
|
|
||||||
queue_settle_promise(job, SettleType::Resolve(Trusted::new(reg)), task_source)
|
|
||||||
}
|
|
|
@ -76,8 +76,8 @@ use webrender_api::{
|
||||||
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags, HitTestResult};
|
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags, HitTestResult};
|
||||||
|
|
||||||
pub use crate::script_msg::{
|
pub use crate::script_msg::{
|
||||||
DOMMessage, HistoryEntryReplacement, SWManagerMsg, SWManagerSenders, ScopeThings,
|
DOMMessage, HistoryEntryReplacement, Job, JobError, JobResult, JobResultValue, JobType,
|
||||||
ServiceWorkerMsg,
|
SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg,
|
||||||
};
|
};
|
||||||
pub use crate::script_msg::{
|
pub use crate::script_msg::{
|
||||||
EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg,
|
EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg,
|
||||||
|
|
|
@ -27,6 +27,7 @@ use msg::constellation_msg::{
|
||||||
TopLevelBrowsingContextId,
|
TopLevelBrowsingContextId,
|
||||||
};
|
};
|
||||||
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
||||||
|
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||||
use net_traits::request::RequestBuilder;
|
use net_traits::request::RequestBuilder;
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::CoreResourceMsg;
|
use net_traits::CoreResourceMsg;
|
||||||
|
@ -262,8 +263,8 @@ pub enum ScriptMsg {
|
||||||
/// Send messages from postMessage calls from serviceworker
|
/// Send messages from postMessage calls from serviceworker
|
||||||
/// to constellation for storing in service worker manager
|
/// to constellation for storing in service worker manager
|
||||||
ForwardDOMMessage(DOMMessage, ServoUrl),
|
ForwardDOMMessage(DOMMessage, ServoUrl),
|
||||||
/// Store the data required to activate a service worker for the given scope
|
/// https://w3c.github.io/ServiceWorker/#schedule-job-algorithm.
|
||||||
RegisterServiceWorker(ScopeThings, ServoUrl),
|
ScheduleJob(Job),
|
||||||
/// Get Window Informations size and position
|
/// Get Window Informations size and position
|
||||||
GetClientWindow(IpcSender<(DeviceIntSize, DeviceIntPoint)>),
|
GetClientWindow(IpcSender<(DeviceIntSize, DeviceIntPoint)>),
|
||||||
/// Get the screen size (pixel)
|
/// Get the screen size (pixel)
|
||||||
|
@ -331,7 +332,7 @@ impl fmt::Debug for ScriptMsg {
|
||||||
DiscardTopLevelBrowsingContext => "DiscardTopLevelBrowsingContext",
|
DiscardTopLevelBrowsingContext => "DiscardTopLevelBrowsingContext",
|
||||||
PipelineExited => "PipelineExited",
|
PipelineExited => "PipelineExited",
|
||||||
ForwardDOMMessage(..) => "ForwardDOMMessage",
|
ForwardDOMMessage(..) => "ForwardDOMMessage",
|
||||||
RegisterServiceWorker(..) => "RegisterServiceWorker",
|
ScheduleJob(..) => "ScheduleJob",
|
||||||
GetClientWindow(..) => "GetClientWindow",
|
GetClientWindow(..) => "GetClientWindow",
|
||||||
GetScreenSize(..) => "GetScreenSize",
|
GetScreenSize(..) => "GetScreenSize",
|
||||||
GetScreenAvailSize(..) => "GetScreenAvailSize",
|
GetScreenAvailSize(..) => "GetScreenAvailSize",
|
||||||
|
@ -382,16 +383,118 @@ pub struct SWManagerSenders {
|
||||||
/// Messages sent to Service Worker Manager thread
|
/// Messages sent to Service Worker Manager thread
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum ServiceWorkerMsg {
|
pub enum ServiceWorkerMsg {
|
||||||
/// Message to register the service worker
|
|
||||||
RegisterServiceWorker(ScopeThings, ServoUrl),
|
|
||||||
/// Timeout message sent by active service workers
|
/// Timeout message sent by active service workers
|
||||||
Timeout(ServoUrl),
|
Timeout(ServoUrl),
|
||||||
/// Message sent by constellation to forward to a running service worker
|
/// Message sent by constellation to forward to a running service worker
|
||||||
ForwardDOMMessage(DOMMessage, ServoUrl),
|
ForwardDOMMessage(DOMMessage, ServoUrl),
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
|
||||||
|
ScheduleJob(Job),
|
||||||
/// Exit the service worker manager
|
/// Exit the service worker manager
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#dfn-job-type
|
||||||
|
pub enum JobType {
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#register>
|
||||||
|
Register,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#unregister-algorithm>
|
||||||
|
Unregister,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#update-algorithm
|
||||||
|
Update,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
/// The kind of error the job promise should be rejected with.
|
||||||
|
pub enum JobError {
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#reject-job-promise
|
||||||
|
TypeError,
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#reject-job-promise
|
||||||
|
SecurityError,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
/// Messages sent from Job algorithms steps running in the SW manager,
|
||||||
|
/// in order to resolve or reject the job promise.
|
||||||
|
pub enum JobResult {
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#reject-job-promise
|
||||||
|
RejectPromise(JobError),
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#resolve-job-promise
|
||||||
|
ResolvePromise(Job, JobResultValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
/// Jobs are resolved with the help of various values.
|
||||||
|
pub enum JobResultValue {
|
||||||
|
/// Data representing a serviceworker registration.
|
||||||
|
Registration {
|
||||||
|
/// The Id of the registration.
|
||||||
|
id: ServiceWorkerRegistrationId,
|
||||||
|
/// The installing worker, if any.
|
||||||
|
installing_worker: Option<ServiceWorkerId>,
|
||||||
|
/// The waiting worker, if any.
|
||||||
|
waiting_worker: Option<ServiceWorkerId>,
|
||||||
|
/// The active worker, if any.
|
||||||
|
active_worker: Option<ServiceWorkerId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#dfn-job
|
||||||
|
pub struct Job {
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-job-type>
|
||||||
|
pub job_type: JobType,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-job-scope-url>
|
||||||
|
pub scope_url: ServoUrl,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-job-script-url>
|
||||||
|
pub script_url: ServoUrl,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#dfn-job-client>
|
||||||
|
pub client: IpcSender<JobResult>,
|
||||||
|
/// <https://w3c.github.io/ServiceWorker/#job-referrer>
|
||||||
|
pub referrer: ServoUrl,
|
||||||
|
/// Various data needed to process job.
|
||||||
|
pub scope_things: Option<ScopeThings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Job {
|
||||||
|
/// https://w3c.github.io/ServiceWorker/#create-job-algorithm
|
||||||
|
pub fn create_job(
|
||||||
|
job_type: JobType,
|
||||||
|
scope_url: ServoUrl,
|
||||||
|
script_url: ServoUrl,
|
||||||
|
client: IpcSender<JobResult>,
|
||||||
|
referrer: ServoUrl,
|
||||||
|
scope_things: Option<ScopeThings>,
|
||||||
|
) -> Job {
|
||||||
|
Job {
|
||||||
|
job_type,
|
||||||
|
scope_url,
|
||||||
|
script_url,
|
||||||
|
client,
|
||||||
|
referrer,
|
||||||
|
scope_things,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Job {
|
||||||
|
/// Equality criteria as described in https://w3c.github.io/ServiceWorker/#dfn-job-equivalent
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// TODO: match on job type, take worker type and `update_via_cache_mode` into account.
|
||||||
|
let same_job = self.job_type == other.job_type;
|
||||||
|
if same_job {
|
||||||
|
match self.job_type {
|
||||||
|
JobType::Register | JobType::Update => {
|
||||||
|
self.scope_url == other.scope_url && self.script_url == other.script_url
|
||||||
|
},
|
||||||
|
JobType::Unregister => self.scope_url == other.scope_url,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Messages outgoing from the Service Worker Manager thread to constellation
|
/// Messages outgoing from the Service Worker Manager thread to constellation
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum SWManagerMsg {
|
pub enum SWManagerMsg {
|
||||||
|
|
|
@ -1,7 +1,19 @@
|
||||||
[service-worker-registration.https.html]
|
[service-worker-registration.https.html]
|
||||||
|
[Test: Asserts Installing Service Worker and its Registration]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Test: Installing Service Worker ScriptURL property]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
[Test: Throws Error when Invalid Scope]
|
[Test: Throws Error when Invalid Scope]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[Test: Asserts Active Service Worker and its Registration]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Test: Active Service Worker ScriptURL property]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
[Test: Asserts ServiceWorkerContainer in Navigator]
|
[Test: Asserts ServiceWorkerContainer in Navigator]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -14298,7 +14298,7 @@
|
||||||
],
|
],
|
||||||
"service-workers": {
|
"service-workers": {
|
||||||
"service-worker-registration.https.html": [
|
"service-worker-registration.https.html": [
|
||||||
"949992e45de6858c336936b4f1ea4bca76db1d91",
|
"04d74fb5c582a3fef8dc589868f0b8c3e402eab2",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[service-worker-registration.https.html]
|
||||||
|
type: testharness
|
||||||
|
[Test: Asserts Active Service Worker and its Registration]
|
||||||
|
expected: FAIL
|
||||||
|
[Test: Active Service Worker ScriptURL property]
|
||||||
|
expected: FAIL
|
|
@ -20,6 +20,12 @@ promise_test(function() {
|
||||||
});
|
});
|
||||||
}, "Test: Active Service Worker ScriptURL property");
|
}, "Test: Active Service Worker ScriptURL property");
|
||||||
|
|
||||||
|
promise_test(function() {
|
||||||
|
return register_sw('resources/sw.js').then(function(sw_reg) {
|
||||||
|
assert_equals(sw_reg.installing.scriptURL, location.href.replace("service-worker-registration.https.html", "resources/sw.js"));
|
||||||
|
});
|
||||||
|
}, "Test: Installing Service Worker ScriptURL property");
|
||||||
|
|
||||||
promise_test(function() {
|
promise_test(function() {
|
||||||
return register_sw('sw.js').then(function(sw_reg) {
|
return register_sw('sw.js').then(function(sw_reg) {
|
||||||
assert_class_string(sw_reg, "ServiceWorkerRegistration");
|
assert_class_string(sw_reg, "ServiceWorkerRegistration");
|
||||||
|
@ -28,6 +34,13 @@ promise_test(function() {
|
||||||
});
|
});
|
||||||
}, "Test: Asserts Active Service Worker and its Registration");
|
}, "Test: Asserts Active Service Worker and its Registration");
|
||||||
|
|
||||||
|
promise_test(function() {
|
||||||
|
return register_sw('sw.js').then(function(sw_reg) {
|
||||||
|
assert_class_string(sw_reg, "ServiceWorkerRegistration");
|
||||||
|
assert_class_string(sw_reg.installing, "ServiceWorker");
|
||||||
|
});
|
||||||
|
}, "Test: Asserts Installing Service Worker and its Registration");
|
||||||
|
|
||||||
promise_test(function() {
|
promise_test(function() {
|
||||||
return register_sw('resources/sw.js', './').then(function(sw_reg) {
|
return register_sw('resources/sw.js', './').then(function(sw_reg) {
|
||||||
assert_equals(sw_reg.scope, location.href.replace("service-worker-registration.https.html", ""));
|
assert_equals(sw_reg.scope, location.href.replace("service-worker-registration.https.html", ""));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue