aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/constellation/constellation.rs35
-rw-r--r--components/msg/constellation_msg.rs82
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/client.rs1
-rw-r--r--components/script/dom/globalscope.rs78
-rw-r--r--components/script/dom/navigationpreloadmanager.rs8
-rw-r--r--components/script/dom/serviceworker.rs17
-rw-r--r--components/script/dom/serviceworkercontainer.rs194
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs63
-rw-r--r--components/script/dom/serviceworkerregistration.rs68
-rw-r--r--components/script/dom/workerglobalscope.rs28
-rw-r--r--components/script/lib.rs2
-rw-r--r--components/script/script_thread.rs74
-rw-r--r--components/script/serviceworker_manager.rs368
-rw-r--r--components/script/serviceworkerjob.rs342
-rw-r--r--components/script_traits/lib.rs4
-rw-r--r--components/script_traits/script_msg.rs113
-rw-r--r--tests/wpt/mozilla/meta-layout-2020/mozilla/service-workers/service-worker-registration.https.html.ini12
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json2
-rw-r--r--tests/wpt/mozilla/meta/mozilla/service-workers/service-worker-registration.https.html.ini6
-rw-r--r--tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.https.html13
21 files changed, 882 insertions, 632 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index e6f8d990ceb..528d3d37fd0 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -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(
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 8a599405df8..064595d6c5e 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -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,
@@ -424,6 +438,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);
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 7534541a06d..ba174193cdf 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -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);
diff --git a/components/script/dom/client.rs b/components/script/dom/client.rs
index 53c692d5591..2768ee7c9ba 100644
--- a/components/script/dom/client.rs
+++ b/components/script/dom/client.rs
@@ -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));
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index e578f31c190..8e067e75de1 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -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(&registration_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) =
diff --git a/components/script/dom/navigationpreloadmanager.rs b/components/script/dom/navigationpreloadmanager.rs
index 7586f0a040f..d45fa9ac5f8 100644
--- a/components/script/dom/navigationpreloadmanager.rs
+++ b/components/script/dom/navigationpreloadmanager.rs
@@ -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()
diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs
index fce07ab0809..bcbdd158a40 100644
--- a/components/script/dom/serviceworker.rs
+++ b/components/script/dom/serviceworker.rs
@@ -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,
)
diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs
index 8145729d857..b65dcd59b7d 100644
--- a/components/script/dom/serviceworkercontainer.rs
+++ b/components/script/dom/serviceworkercontainer.rs
@@ -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,28 +58,50 @@ 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
+
+ // A: Step 4-5
+ let scope = match options.scope {
+ Some(ref scope) => {
+ let &USVString(ref inner_scope) = scope;
+ match api_base_url.join(inner_scope) {
+ Ok(url) => url,
+ Err(_) => {
+ promise.reject_error(Error::Type("Invalid scope URL".to_owned()));
+ return promise;
+ },
+ }
+ },
+ None => script_url.join("./").unwrap(),
+ };
+
+ // A: Step 6 -> invoke B.
+
+ // B: Step 3
match script_url.scheme() {
"https" | "http" => {},
_ => {
@@ -79,7 +109,7 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
return promise;
},
}
- // B: Step 3
+ // B: Step 4
if script_url.path().to_ascii_lowercase().contains("%2f") ||
script_url.path().to_ascii_lowercase().contains("%5c")
{
@@ -88,20 +118,7 @@ impl ServiceWorkerContainerMethods for ServiceWorkerContainer {
));
return promise;
}
- // B: Step 4-5
- let scope = match options.scope {
- Some(ref scope) => {
- let &USVString(ref inner_scope) = scope;
- match api_base_url.join(inner_scope) {
- Ok(url) => url,
- Err(_) => {
- promise.reject_error(Error::Type("Invalid scope URL".to_owned()));
- return promise;
- },
- }
- },
- None => script_url.join("./").unwrap(),
- };
+
// 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.
+ },
+ }
+ }
+}
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 9699262a874..a605e744ead 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -268,15 +268,12 @@ impl ServiceWorkerGlobalScope {
} = scope_things;
let serialized_worker_url = script_url.to_string();
- let origin = GlobalScope::current()
- .expect("No current global object")
- .origin()
- .immutable()
- .clone();
+ let origin = scope_url.origin();
thread::Builder::new()
.name(format!("ServiceWorker for {}", serialized_worker_url))
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
+ let runtime = new_rt_and_cx(None);
let roots = RootCollection::new();
let _stack_roots = ThreadLocalStackRoots::new(&roots);
@@ -298,34 +295,19 @@ impl ServiceWorkerGlobalScope {
.referrer_policy(referrer_policy)
.origin(origin);
- let (url, source) = match load_whole_resource(
- request,
- &init.resource_threads.sender(),
- &GlobalScope::current().expect("No current global object"),
- ) {
- Err(_) => {
- println!("error loading script {}", serialized_worker_url);
- return;
- },
- Ok((metadata, bytes)) => {
- (metadata.final_url, String::from_utf8(bytes).unwrap())
- },
- };
-
- let runtime = new_rt_and_cx(None);
-
- let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
- ROUTER
- .route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan);
-
// Service workers are time limited
// https://w3c.github.io/ServiceWorker/#service-worker-lifetime
let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64;
let time_out_port = after(Duration::new(sw_lifetime_timeout, 0));
+ let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
+ ROUTER
+ .route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan);
+
+ let resource_threads_sender = init.resource_threads.sender();
let global = ServiceWorkerGlobalScope::new(
init,
- url,
+ script_url,
devtools_mpsc_port,
runtime,
own_sender,
@@ -334,6 +316,19 @@ impl ServiceWorkerGlobalScope {
swmanager_sender,
scope_url,
);
+
+ let (_url, source) =
+ match load_whole_resource(request, &resource_threads_sender, &*global.upcast())
+ {
+ Err(_) => {
+ println!("error loading script {}", serialized_worker_url);
+ return;
+ },
+ Ok((metadata, bytes)) => {
+ (metadata.final_url, String::from_utf8(bytes).unwrap())
+ },
+ };
+
let scope = global.upcast::<WorkerGlobalScope>();
unsafe {
@@ -356,7 +351,7 @@ impl ServiceWorkerGlobalScope {
// until the event loop is destroyed,
// which happens after the closing flag is set to true,
// or until the worker has run beyond its allocated time.
- while !scope.is_closing() || !global.has_timed_out() {
+ while !scope.is_closing() && !global.has_timed_out() {
run_worker_event_loop(&*global, None);
}
},
@@ -364,6 +359,7 @@ impl ServiceWorkerGlobalScope {
scope.script_chan(),
CommonScriptMsg::CollectReports,
);
+ scope.clear_js_runtime();
})
.expect("Thread spawning failed");
}
@@ -390,15 +386,10 @@ impl ServiceWorkerGlobalScope {
}
fn has_timed_out(&self) -> bool {
- // Note: this should be included in the `select` inside `run_worker_event_loop`,
- // otherwise a block on the select can prevent the timeout.
- if self.time_out_port.try_recv().is_ok() {
- let _ = self
- .swmanager_sender
- .send(ServiceWorkerMsg::Timeout(self.scope_url.clone()));
- return true;
- }
- false
+ // TODO: https://w3c.github.io/ServiceWorker/#service-worker-lifetime
+ // Since we don't have a shutdown mechanism yet, see #26548
+ // immediately stop the event-loop after executing the initial script.
+ true
}
fn handle_script_event(&self, msg: ServiceWorkerScriptMsg) {
diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs
index 9bb578e0d59..0c1734b41cc 100644
--- a/components/script/dom/serviceworkerregistration.rs
+++ b/components/script/dom/serviceworkerregistration.rs
@@ -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
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 6e9490cb2c7..8d54c3f7da4 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -100,7 +100,7 @@ pub struct WorkerGlobalScope {
#[ignore_malloc_size_of = "Arc"]
closing: Option<Arc<AtomicBool>>,
#[ignore_malloc_size_of = "Defined in js"]
- runtime: Runtime,
+ runtime: DomRefCell<Option<Runtime>>,
location: MutNullableDom<WorkerLocation>,
navigator: MutNullableDom<WorkerNavigator>,
@@ -151,7 +151,7 @@ impl WorkerGlobalScope {
worker_type,
worker_url: DomRefCell::new(worker_url),
closing,
- runtime,
+ runtime: DomRefCell::new(Some(runtime)),
location: Default::default(),
navigator: Default::default(),
from_devtools_sender: init.from_devtools_sender,
@@ -161,8 +161,17 @@ impl WorkerGlobalScope {
}
}
+ pub fn clear_js_runtime(&self) {
+ let runtime = self.runtime.borrow_mut().take();
+ drop(runtime);
+ }
+
pub fn runtime_handle(&self) -> ParentRuntime {
- self.runtime.prepare_for_new_child()
+ self.runtime
+ .borrow()
+ .as_ref()
+ .unwrap()
+ .prepare_for_new_child()
}
pub fn from_devtools_sender(&self) -> Option<IpcSender<DevtoolScriptControlMsg>> {
@@ -175,7 +184,7 @@ impl WorkerGlobalScope {
#[allow(unsafe_code)]
pub fn get_cx(&self) -> JSContext {
- unsafe { JSContext::from_ptr(self.runtime.cx()) }
+ unsafe { JSContext::from_ptr(self.runtime.borrow().as_ref().unwrap().cx()) }
}
pub fn is_closing(&self) -> bool {
@@ -235,7 +244,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
};
}
- rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
+ rooted!(in(self.runtime.borrow().as_ref().unwrap().cx()) let mut rval = UndefinedValue());
for url in urls {
let global_scope = self.upcast::<GlobalScope>();
let request = NetRequestInit::new(url.clone())
@@ -256,7 +265,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
Ok((metadata, bytes)) => (metadata.final_url, String::from_utf8(bytes).unwrap()),
};
- let result = self.runtime.evaluate_script(
+ let result = self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(),
&source,
url.as_str(),
@@ -401,8 +410,9 @@ impl WorkerGlobalScope {
#[allow(unsafe_code)]
pub fn execute_script(&self, source: DOMString) {
let _aes = AutoEntryScript::new(self.upcast());
- rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
- match self.runtime.evaluate_script(
+ let cx = self.runtime.borrow().as_ref().unwrap().cx();
+ rooted!(in(cx) let mut rval = UndefinedValue());
+ match self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(),
&source,
self.worker_url.borrow().as_str(),
@@ -419,7 +429,7 @@ impl WorkerGlobalScope {
println!("evaluate_script failed");
unsafe {
let ar = enter_realm(&*self);
- report_pending_exception(self.runtime.cx(), true, InRealm::Entered(&ar));
+ report_pending_exception(cx, true, InRealm::Entered(&ar));
}
}
},
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 886a76e9c95..be9e650e81d 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -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;
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 41dc5406b4a..2107cd070cd 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -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,
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 73ead01fe17..7c0484144d9 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -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,76 +188,198 @@ 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_resource(&mut self, mediator: CustomResponseMediator) -> bool {
+ if serviceworker_enabled() {
+ if let Some(scope) = self.get_matching_scope(&mediator.load_url) {
+ if let Some(registration) = self.registrations.get(&scope) {
+ if let Some(ref worker) = registration.active_worker {
+ worker.send_message(ServiceWorkerScriptMsg::Response(mediator));
+ return true;
+ }
+ }
+ }
+ }
+ let _ = mediator.response_chan.send(None);
+ true
+ }
+
+ fn receive_message(&mut self) -> Result<Message, RecvError> {
+ select! {
+ recv(self.own_port) -> msg => msg.map(Message::FromConstellation),
+ recv(self.resource_receiver) -> msg => msg.map(Message::FromResource),
+ }
}
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::Timeout(_scope) => {
+ // TODO: https://w3c.github.io/ServiceWorker/#terminate-service-worker
},
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);
+ if let Some(registration) = self.registrations.get_mut(&scope_url) {
+ if let Some(ref worker) = registration.active_worker {
+ worker.forward_dom_message(msg);
}
}
- true
},
- ServiceWorkerMsg::Exit => false,
+ 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
}
- 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));
- }
- }
- } else {
- let _ = mediator.response_chan.send(None);
+ /// <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 {
- let _ = mediator.response_chan.send(None);
+ // 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));
}
- true
}
- fn receive_message(&mut self) -> Result<Message, RecvError> {
- select! {
- recv(self.own_port) -> msg => msg.map(Message::FromConstellation),
- recv(self.resource_receiver) -> msg => msg.map(Message::FromResource),
+ /// <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 {
fn create(sw_senders: SWManagerSenders, origin: ImmutableOrigin) {
let (resource_chan, resource_port) = ipc::channel().unwrap();
diff --git a/components/script/serviceworkerjob.rs b/components/script/serviceworkerjob.rs
deleted file mode 100644
index eeced0918f7..00000000000
--- a/components/script/serviceworkerjob.rs
+++ /dev/null
@@ -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)
-}
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index e13f1958e1b..d0309c72864 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -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,
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index b5687d6217e..291d6f4798e 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -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 {
diff --git a/tests/wpt/mozilla/meta-layout-2020/mozilla/service-workers/service-worker-registration.https.html.ini b/tests/wpt/mozilla/meta-layout-2020/mozilla/service-workers/service-worker-registration.https.html.ini
index 577ca67b292..222aa07e5c3 100644
--- a/tests/wpt/mozilla/meta-layout-2020/mozilla/service-workers/service-worker-registration.https.html.ini
+++ b/tests/wpt/mozilla/meta-layout-2020/mozilla/service-workers/service-worker-registration.https.html.ini
@@ -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
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index d62a360aad8..832db1e23ac 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -14305,7 +14305,7 @@
],
"service-workers": {
"service-worker-registration.https.html": [
- "949992e45de6858c336936b4f1ea4bca76db1d91",
+ "04d74fb5c582a3fef8dc589868f0b8c3e402eab2",
[
null,
{}
diff --git a/tests/wpt/mozilla/meta/mozilla/service-workers/service-worker-registration.https.html.ini b/tests/wpt/mozilla/meta/mozilla/service-workers/service-worker-registration.https.html.ini
new file mode 100644
index 00000000000..0492835f43e
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/service-workers/service-worker-registration.https.html.ini
@@ -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
diff --git a/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.https.html b/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.https.html
index 949992e45de..04d74fb5c58 100644
--- a/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.https.html
+++ b/tests/wpt/mozilla/tests/mozilla/service-workers/service-worker-registration.https.html
@@ -21,6 +21,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");
assert_class_string(sw_reg.active, "ServiceWorker");
@@ -29,6 +35,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", ""));
});