mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00: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,
|
||||
};
|
||||
use script_traits::{
|
||||
LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
|
||||
Job, LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory,
|
||||
ServiceWorkerManagerFactory,
|
||||
};
|
||||
use script_traits::{MediaSessionActionType, MouseEventType};
|
||||
use script_traits::{MessagePortMsg, PortMessageTask, StructuredSerializedData};
|
||||
use script_traits::{
|
||||
SWManagerMsg, SWManagerSenders, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg,
|
||||
};
|
||||
use script_traits::{SWManagerMsg, SWManagerSenders, UpdatePipelineIdReason, WebDriverCommandMsg};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_config::{opts, pref};
|
||||
use servo_rand::{random, Rng, ServoRng, SliceRandom};
|
||||
|
@ -1983,8 +1981,8 @@ where
|
|||
);
|
||||
}
|
||||
},
|
||||
FromScriptMsg::RegisterServiceWorker(scope_things, scope) => {
|
||||
self.handle_register_serviceworker(scope_things, scope);
|
||||
FromScriptMsg::ScheduleJob(job) => {
|
||||
self.handle_schedule_serviceworker_job(source_pipeline_id, job);
|
||||
},
|
||||
FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
|
||||
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.
|
||||
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::Vacant(entry) => {
|
||||
let (own_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||
|
@ -2653,7 +2668,7 @@ where
|
|||
own_sender: own_sender.clone(),
|
||||
receiver,
|
||||
};
|
||||
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, scope.origin());
|
||||
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin);
|
||||
|
||||
if opts::multiprocess() {
|
||||
if content.spawn_multiprocess().is_err() {
|
||||
|
@ -2665,7 +2680,7 @@ where
|
|||
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(
|
||||
|
|
|
@ -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 {
|
||||
BlobId {
|
||||
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)]
|
||||
pub struct BlobIndex(pub NonZeroU32);
|
||||
malloc_size_of_is_0!(BlobIndex);
|
||||
|
|
|
@ -86,6 +86,7 @@ use msg::constellation_msg::{
|
|||
BlobId, BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
||||
MessagePortRouterId, PipelineId, TopLevelBrowsingContextId,
|
||||
};
|
||||
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||
use net_traits::filemanager_thread::RelativePos;
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
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!(MessagePortRouterId);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(ServiceWorkerId);
|
||||
unsafe_no_jsmanaged_fields!(ServiceWorkerRegistrationId);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(BlobId);
|
||||
|
|
|
@ -47,6 +47,7 @@ impl Client {
|
|||
self.active_worker.get()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_controller(&self, worker: &ServiceWorker) {
|
||||
self.active_worker.set(Some(worker));
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
|||
use crate::dom::performance::Performance;
|
||||
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::serviceworker::ServiceWorker;
|
||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||
use crate::dom::window::Window;
|
||||
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
||||
use crate::dom::workletglobalscope::WorkletGlobalScope;
|
||||
|
@ -81,6 +83,7 @@ use js::rust::{HandleValue, MutableHandleValue};
|
|||
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
||||
use msg::constellation_msg::{
|
||||
BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
|
||||
ServiceWorkerId, ServiceWorkerRegistrationId,
|
||||
};
|
||||
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
||||
use net_traits::filemanager_thread::{
|
||||
|
@ -135,6 +138,13 @@ pub struct GlobalScope {
|
|||
/// The blobs managed by this global, if any.
|
||||
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: PipelineId,
|
||||
|
||||
|
@ -567,6 +577,8 @@ impl GlobalScope {
|
|||
blob_state: DomRefCell::new(BlobState::UnManaged),
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
crypto: Default::default(),
|
||||
registration_map: DomRefCell::new(HashMap::new()),
|
||||
worker_map: DomRefCell::new(HashMap::new()),
|
||||
pipeline_id,
|
||||
devtools_wants_updates: 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.
|
||||
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
|
||||
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);
|
||||
|
||||
// 2.
|
||||
if self.serviceworker_registration.active().is_none() {
|
||||
if self.serviceworker_registration.is_active() {
|
||||
promise.reject_native(&DOMException::new(
|
||||
&self.global(),
|
||||
DOMErrorName::InvalidStateError,
|
||||
|
@ -68,7 +68,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
|||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
||||
|
||||
// 2.
|
||||
if self.serviceworker_registration.active().is_none() {
|
||||
if self.serviceworker_registration.is_active() {
|
||||
promise.reject_native(&DOMException::new(
|
||||
&self.global(),
|
||||
DOMErrorName::InvalidStateError,
|
||||
|
@ -90,7 +90,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
|||
let promise = Promise::new_in_current_realm(&*self.global(), comp);
|
||||
|
||||
// 2.
|
||||
if self.serviceworker_registration.active().is_none() {
|
||||
if self.serviceworker_registration.is_active() {
|
||||
promise.reject_native(&DOMException::new(
|
||||
&self.global(),
|
||||
DOMErrorName::InvalidStateError,
|
||||
|
@ -114,7 +114,7 @@ impl NavigationPreloadManagerMethods for NavigationPreloadManager {
|
|||
let mut state = NavigationPreloadState::empty();
|
||||
|
||||
// 3.
|
||||
if let Some(_) = self.serviceworker_registration.active() {
|
||||
if self.serviceworker_registration.is_active() {
|
||||
if self
|
||||
.serviceworker_registration
|
||||
.get_navigation_preload_enabled()
|
||||
|
|
|
@ -23,6 +23,7 @@ use crate::task::TaskOnce;
|
|||
use dom_struct::dom_struct;
|
||||
use js::jsapi::{Heap, JSObject};
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use msg::constellation_msg::ServiceWorkerId;
|
||||
use script_traits::{DOMMessage, ScriptMsg};
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
|
@ -35,31 +36,35 @@ pub struct ServiceWorker {
|
|||
script_url: DomRefCell<String>,
|
||||
scope_url: ServoUrl,
|
||||
state: Cell<ServiceWorkerState>,
|
||||
skip_waiting: Cell<bool>,
|
||||
worker_id: ServiceWorkerId,
|
||||
}
|
||||
|
||||
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 {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
script_url: DomRefCell::new(String::from(script_url)),
|
||||
state: Cell::new(ServiceWorkerState::Installing),
|
||||
scope_url: scope_url,
|
||||
skip_waiting: Cell::new(skip_waiting),
|
||||
worker_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_serviceworker(
|
||||
pub fn new(
|
||||
global: &GlobalScope,
|
||||
script_url: ServoUrl,
|
||||
scope_url: ServoUrl,
|
||||
skip_waiting: bool,
|
||||
worker_id: ServiceWorkerId,
|
||||
) -> DomRoot<ServiceWorker> {
|
||||
reflect_dom_object(
|
||||
Box::new(ServiceWorker::new_inherited(
|
||||
script_url.as_str(),
|
||||
skip_waiting,
|
||||
scope_url,
|
||||
worker_id,
|
||||
)),
|
||||
global,
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::ServiceWorkerContainerMethods;
|
||||
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::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::str::USVString;
|
||||
|
@ -13,10 +14,17 @@ use crate::dom::eventtarget::EventTarget;
|
|||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::serviceworker::ServiceWorker;
|
||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||
use crate::realms::enter_realm;
|
||||
use crate::realms::InRealm;
|
||||
use crate::script_thread::ScriptThread;
|
||||
use crate::serviceworkerjob::{Job, JobType};
|
||||
use crate::task::TaskCanceller;
|
||||
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
||||
use crate::task_source::TaskSource;
|
||||
use crate::task_source::TaskSourceName;
|
||||
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::rc::Rc;
|
||||
|
||||
|
@ -50,45 +58,33 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
|||
self.client.get_controller()
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)] // Job is unrooted
|
||||
/// https://w3c.github.io/ServiceWorker/#navigator-service-worker-register and - A
|
||||
/// https://w3c.github.io/ServiceWorker/#start-register-algorithm - B
|
||||
/// https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-register - A
|
||||
/// and https://w3c.github.io/ServiceWorker/#start-register - B
|
||||
fn Register(
|
||||
&self,
|
||||
script_url: USVString,
|
||||
options: &RegistrationOptions,
|
||||
comp: InRealm,
|
||||
) -> Rc<Promise> {
|
||||
// A: Step 2.
|
||||
let global = self.client.global();
|
||||
|
||||
// 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 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) {
|
||||
Ok(url) => url,
|
||||
Err(_) => {
|
||||
// B: Step 1
|
||||
promise.reject_error(Error::Type("Invalid script URL".to_owned()));
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
// B: Step 2
|
||||
match script_url.scheme() {
|
||||
"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
|
||||
|
||||
// A: Step 4-5
|
||||
let scope = match options.scope {
|
||||
Some(ref scope) => {
|
||||
let &USVString(ref inner_scope) = scope;
|
||||
|
@ -102,6 +98,27 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
|||
},
|
||||
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
|
||||
match scope.scheme() {
|
||||
"https" | "http" => {},
|
||||
|
@ -120,17 +137,134 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
|
|||
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(
|
||||
JobType::Register,
|
||||
scope,
|
||||
script_url,
|
||||
promise.clone(),
|
||||
options.type_,
|
||||
&*self.client,
|
||||
job_result_sender,
|
||||
self.client.creation_url(),
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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/. */
|
||||
|
||||
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::ServiceWorkerUpdateViaCache;
|
||||
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 devtools_traits::WorkerId;
|
||||
use dom_struct::dom_struct;
|
||||
use msg::constellation_msg::ServiceWorkerRegistrationId;
|
||||
use script_traits::{ScopeThings, WorkerScriptLoadOrigin};
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
|
@ -24,58 +24,60 @@ use uuid::Uuid;
|
|||
#[dom_struct]
|
||||
pub struct ServiceWorkerRegistration {
|
||||
eventtarget: EventTarget,
|
||||
active: Option<Dom<ServiceWorker>>,
|
||||
installing: Option<Dom<ServiceWorker>>,
|
||||
waiting: Option<Dom<ServiceWorker>>,
|
||||
active: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||
installing: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||
waiting: DomRefCell<Option<Dom<ServiceWorker>>>,
|
||||
navigation_preload: MutNullableDom<NavigationPreloadManager>,
|
||||
scope: ServoUrl,
|
||||
navigation_preload_enabled: Cell<bool>,
|
||||
navigation_preload_header_value: DomRefCell<Option<ByteString>>,
|
||||
update_via_cache: ServiceWorkerUpdateViaCache,
|
||||
uninstalling: Cell<bool>,
|
||||
registration_id: ServiceWorkerRegistrationId,
|
||||
}
|
||||
|
||||
impl ServiceWorkerRegistration {
|
||||
fn new_inherited(active_sw: &ServiceWorker, scope: ServoUrl) -> ServiceWorkerRegistration {
|
||||
fn new_inherited(
|
||||
scope: ServoUrl,
|
||||
registration_id: ServiceWorkerRegistrationId,
|
||||
) -> ServiceWorkerRegistration {
|
||||
ServiceWorkerRegistration {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
active: Some(Dom::from_ref(active_sw)),
|
||||
installing: None,
|
||||
waiting: None,
|
||||
active: DomRefCell::new(None),
|
||||
installing: DomRefCell::new(None),
|
||||
waiting: DomRefCell::new(None),
|
||||
navigation_preload: MutNullableDom::new(None),
|
||||
scope: scope,
|
||||
navigation_preload_enabled: Cell::new(false),
|
||||
navigation_preload_header_value: DomRefCell::new(None),
|
||||
update_via_cache: ServiceWorkerUpdateViaCache::Imports,
|
||||
uninstalling: Cell::new(false),
|
||||
registration_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new(
|
||||
global: &GlobalScope,
|
||||
script_url: &ServoUrl,
|
||||
scope: ServoUrl,
|
||||
registration_id: ServiceWorkerRegistrationId,
|
||||
) -> 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(
|
||||
Box::new(ServiceWorkerRegistration::new_inherited(
|
||||
&*active_worker,
|
||||
scope,
|
||||
registration_id,
|
||||
)),
|
||||
global,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn active(&self) -> Option<&ServiceWorker> {
|
||||
self.active.as_ref().map(|sw| &**sw)
|
||||
/// Does this registration have an active worker?
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.active.borrow().is_some()
|
||||
}
|
||||
|
||||
pub fn get_installed(&self) -> &ServiceWorker {
|
||||
self.active.as_ref().unwrap()
|
||||
pub fn set_installing(&self, worker: &ServiceWorker) {
|
||||
*self.installing.borrow_mut() = Some(Dom::from_ref(worker));
|
||||
}
|
||||
|
||||
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
|
||||
pub fn get_newest_worker(&self) -> Option<DomRoot<ServiceWorker>> {
|
||||
if self.installing.as_ref().is_some() {
|
||||
self.installing.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
||||
} else if self.waiting.as_ref().is_some() {
|
||||
self.waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
||||
} else {
|
||||
self.active.as_ref().map(|sw| DomRoot::from_ref(&**sw))
|
||||
}
|
||||
let installing = self.installing.borrow();
|
||||
let waiting = self.waiting.borrow();
|
||||
let active = self.active.borrow();
|
||||
installing
|
||||
.as_ref()
|
||||
.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 {
|
||||
// https://w3c.github.io/ServiceWorker/#service-worker-registration-installing-attribute
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -92,8 +92,6 @@ pub mod script_thread;
|
|||
#[warn(deprecated)]
|
||||
pub mod serviceworker_manager;
|
||||
#[warn(deprecated)]
|
||||
mod serviceworkerjob;
|
||||
#[warn(deprecated)]
|
||||
mod stylesheet_loader;
|
||||
#[warn(deprecated)]
|
||||
mod stylesheet_set;
|
||||
|
|
|
@ -63,7 +63,6 @@ use crate::dom::node::{
|
|||
use crate::dom::performanceentry::PerformanceEntry;
|
||||
use crate::dom::performancepainttiming::PerformancePaintTiming;
|
||||
use crate::dom::serviceworker::TrustedServiceWorkerAddress;
|
||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||
use crate::dom::servoparser::{ParserContext, ServoParser};
|
||||
use crate::dom::transitionevent::TransitionEvent;
|
||||
use crate::dom::uievent::UIEvent;
|
||||
|
@ -77,7 +76,6 @@ use crate::microtask::{Microtask, MicrotaskQueue};
|
|||
use crate::realms::enter_realm;
|
||||
use crate::script_runtime::{get_reports, new_rt_and_cx, JSContext, Runtime, ScriptPort};
|
||||
use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
||||
use crate::serviceworkerjob::{Job, JobQueue};
|
||||
use crate::task_manager::TaskManager;
|
||||
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
|
||||
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
||||
|
@ -287,8 +285,6 @@ pub enum MainThreadScriptMsg {
|
|||
properties: Vec<Atom>,
|
||||
painter: Box<dyn Painter>,
|
||||
},
|
||||
/// Dispatches a job queue.
|
||||
DispatchJobQueue { scope_url: ServoUrl },
|
||||
/// A task related to a not fully-active document has been throttled.
|
||||
Inactive,
|
||||
/// Wake-up call from the task queue.
|
||||
|
@ -533,10 +529,6 @@ pub struct ScriptThread {
|
|||
incomplete_loads: DomRefCell<Vec<InProgressLoad>>,
|
||||
/// A vector containing parser contexts which have not yet been fully processed
|
||||
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: Arc<dyn ImageCache>,
|
||||
/// 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) {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
if let Some(script_thread) = root.get() {
|
||||
|
@ -1317,8 +1300,6 @@ impl ScriptThread {
|
|||
window_proxies: DomRefCell::new(HashMap::new()),
|
||||
incomplete_loads: DomRefCell::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_channel: image_cache_channel,
|
||||
|
@ -1774,7 +1755,6 @@ impl ScriptThread {
|
|||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
||||
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
||||
MainThreadScriptMsg::DispatchJobQueue { .. } => None,
|
||||
MainThreadScriptMsg::Inactive => None,
|
||||
MainThreadScriptMsg::WakeUp => None,
|
||||
},
|
||||
|
@ -2008,9 +1988,6 @@ impl ScriptThread {
|
|||
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::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(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
|
|
|
@ -13,10 +13,12 @@ use crate::dom::serviceworkerregistration::longest_prefix_match;
|
|||
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use msg::constellation_msg::PipelineNamespace;
|
||||
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||
use net_traits::{CoreResourceMsg, CustomResponseMediator};
|
||||
use script_traits::{
|
||||
DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerManagerFactory,
|
||||
ServiceWorkerMsg,
|
||||
DOMMessage, Job, JobError, JobResult, JobResultValue, JobType, SWManagerMsg, SWManagerSenders,
|
||||
ScopeThings, ServiceWorkerManagerFactory, ServiceWorkerMsg,
|
||||
};
|
||||
use servo_config::pref;
|
||||
use servo_url::ImmutableOrigin;
|
||||
|
@ -29,11 +31,112 @@ enum Message {
|
|||
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 {
|
||||
// map of registered service worker descriptors
|
||||
registered_workers: HashMap<ServoUrl, ScopeThings>,
|
||||
// map of active service worker descriptors
|
||||
active_workers: HashMap<ServoUrl, Sender<ServiceWorkerScriptMsg>>,
|
||||
/// https://w3c.github.io/ServiceWorker/#dfn-scope-to-registration-map
|
||||
registrations: HashMap<ServoUrl, ServiceWorkerRegistration>,
|
||||
// Will be useful to implement posting a message to a client.
|
||||
// See https://github.com/servo/servo/issues/24660
|
||||
_constellation_sender: IpcSender<SWManagerMsg>,
|
||||
|
@ -52,9 +155,11 @@ impl ServiceWorkerManager {
|
|||
resource_port: Receiver<CustomResponseMediator>,
|
||||
constellation_sender: IpcSender<SWManagerMsg>,
|
||||
) -> ServiceWorkerManager {
|
||||
// Install a pipeline-namespace in the current thread.
|
||||
PipelineNamespace::auto_install();
|
||||
|
||||
ServiceWorkerManager {
|
||||
registered_workers: HashMap::new(),
|
||||
active_workers: HashMap::new(),
|
||||
registrations: HashMap::new(),
|
||||
own_sender: own_sender,
|
||||
own_port: from_constellation_receiver,
|
||||
resource_receiver: resource_port,
|
||||
|
@ -63,7 +168,7 @@ impl ServiceWorkerManager {
|
|||
}
|
||||
|
||||
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) {
|
||||
return Some(scope.clone());
|
||||
}
|
||||
|
@ -71,31 +176,6 @@ impl ServiceWorkerManager {
|
|||
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) {
|
||||
while let Ok(message) = self.receive_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 {
|
||||
if serviceworker_enabled() {
|
||||
if let Some(scope) = self.get_matching_scope(&mediator.load_url) {
|
||||
if self.active_workers.contains_key(&scope) {
|
||||
if let Some(sender) = self.active_workers.get(&scope) {
|
||||
let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator));
|
||||
}
|
||||
} else {
|
||||
if let Some(sender) = self.wakeup_serviceworker(scope) {
|
||||
let _ = sender.send(ServiceWorkerScriptMsg::Response(mediator));
|
||||
if let Some(registration) = self.registrations.get(&scope) {
|
||||
if let Some(ref worker) = registration.active_worker {
|
||||
worker.send_message(ServiceWorkerScriptMsg::Response(mediator));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = mediator.response_chan.send(None);
|
||||
}
|
||||
} else {
|
||||
let _ = mediator.response_chan.send(None);
|
||||
}
|
||||
let _ = mediator.response_chan.send(None);
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -176,6 +209,173 @@ impl ServiceWorkerManager {
|
|||
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 {
|
||||
|
|
|
@ -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};
|
||||
|
||||
pub use crate::script_msg::{
|
||||
DOMMessage, HistoryEntryReplacement, SWManagerMsg, SWManagerSenders, ScopeThings,
|
||||
ServiceWorkerMsg,
|
||||
DOMMessage, HistoryEntryReplacement, Job, JobError, JobResult, JobResultValue, JobType,
|
||||
SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg,
|
||||
};
|
||||
pub use crate::script_msg::{
|
||||
EventResult, IFrameSize, IFrameSizeMsg, LayoutMsg, LogEntry, ScriptMsg,
|
||||
|
|
|
@ -27,6 +27,7 @@ use msg::constellation_msg::{
|
|||
TopLevelBrowsingContextId,
|
||||
};
|
||||
use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
||||
use msg::constellation_msg::{ServiceWorkerId, ServiceWorkerRegistrationId};
|
||||
use net_traits::request::RequestBuilder;
|
||||
use net_traits::storage_thread::StorageType;
|
||||
use net_traits::CoreResourceMsg;
|
||||
|
@ -262,8 +263,8 @@ pub enum ScriptMsg {
|
|||
/// Send messages from postMessage calls from serviceworker
|
||||
/// to constellation for storing in service worker manager
|
||||
ForwardDOMMessage(DOMMessage, ServoUrl),
|
||||
/// Store the data required to activate a service worker for the given scope
|
||||
RegisterServiceWorker(ScopeThings, ServoUrl),
|
||||
/// https://w3c.github.io/ServiceWorker/#schedule-job-algorithm.
|
||||
ScheduleJob(Job),
|
||||
/// Get Window Informations size and position
|
||||
GetClientWindow(IpcSender<(DeviceIntSize, DeviceIntPoint)>),
|
||||
/// Get the screen size (pixel)
|
||||
|
@ -331,7 +332,7 @@ impl fmt::Debug for ScriptMsg {
|
|||
DiscardTopLevelBrowsingContext => "DiscardTopLevelBrowsingContext",
|
||||
PipelineExited => "PipelineExited",
|
||||
ForwardDOMMessage(..) => "ForwardDOMMessage",
|
||||
RegisterServiceWorker(..) => "RegisterServiceWorker",
|
||||
ScheduleJob(..) => "ScheduleJob",
|
||||
GetClientWindow(..) => "GetClientWindow",
|
||||
GetScreenSize(..) => "GetScreenSize",
|
||||
GetScreenAvailSize(..) => "GetScreenAvailSize",
|
||||
|
@ -382,16 +383,118 @@ pub struct SWManagerSenders {
|
|||
/// Messages sent to Service Worker Manager thread
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum ServiceWorkerMsg {
|
||||
/// Message to register the service worker
|
||||
RegisterServiceWorker(ScopeThings, ServoUrl),
|
||||
/// Timeout message sent by active service workers
|
||||
Timeout(ServoUrl),
|
||||
/// Message sent by constellation to forward to a running service worker
|
||||
ForwardDOMMessage(DOMMessage, ServoUrl),
|
||||
/// https://w3c.github.io/ServiceWorker/#schedule-job-algorithm
|
||||
ScheduleJob(Job),
|
||||
/// Exit the service worker manager
|
||||
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
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum SWManagerMsg {
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
[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]
|
||||
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]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -14298,7 +14298,7 @@
|
|||
],
|
||||
"service-workers": {
|
||||
"service-worker-registration.https.html": [
|
||||
"949992e45de6858c336936b4f1ea4bca76db1d91",
|
||||
"04d74fb5c582a3fef8dc589868f0b8c3e402eab2",
|
||||
[
|
||||
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");
|
||||
|
||||
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() {
|
||||
return register_sw('sw.js').then(function(sw_reg) {
|
||||
assert_class_string(sw_reg, "ServiceWorkerRegistration");
|
||||
|
@ -28,6 +34,13 @@ promise_test(function() {
|
|||
});
|
||||
}, "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() {
|
||||
return register_sw('resources/sw.js', './').then(function(sw_reg) {
|
||||
assert_equals(sw_reg.scope, location.href.replace("service-worker-registration.https.html", ""));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue