script/dom/workers/
serviceworkerregistration.rs1use std::cell::Cell;
6
7use devtools_traits::WorkerId;
8use dom_struct::dom_struct;
9use net_traits::request::Referrer;
10use servo_base::id::ServiceWorkerRegistrationId;
11use servo_constellation_traits::{ScopeThings, WorkerScriptLoadOrigin};
12use servo_url::ServoUrl;
13use uuid::Uuid;
14
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::ServiceWorkerRegistrationBinding::{
17 ServiceWorkerRegistrationMethods, ServiceWorkerUpdateViaCache,
18};
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
21use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
22use crate::dom::bindings::str::{ByteString, USVString};
23use crate::dom::eventtarget::EventTarget;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::navigationpreloadmanager::NavigationPreloadManager;
26use crate::dom::serviceworker::ServiceWorker;
27use crate::dom::window::Window;
28use crate::dom::workerglobalscope::prepare_workerscope_init;
29use crate::script_runtime::CanGc;
30
31#[dom_struct]
32pub(crate) struct ServiceWorkerRegistration {
33 eventtarget: EventTarget,
34 active: DomRefCell<Option<Dom<ServiceWorker>>>,
35 installing: DomRefCell<Option<Dom<ServiceWorker>>>,
36 waiting: DomRefCell<Option<Dom<ServiceWorker>>>,
37 navigation_preload: MutNullableDom<NavigationPreloadManager>,
38 #[no_trace]
39 scope: ServoUrl,
40 navigation_preload_enabled: Cell<bool>,
41 navigation_preload_header_value: DomRefCell<Option<ByteString>>,
42 update_via_cache: ServiceWorkerUpdateViaCache,
43 uninstalling: Cell<bool>,
44 #[no_trace]
45 registration_id: ServiceWorkerRegistrationId,
46}
47
48impl ServiceWorkerRegistration {
49 fn new_inherited(
50 scope: ServoUrl,
51 registration_id: ServiceWorkerRegistrationId,
52 ) -> ServiceWorkerRegistration {
53 ServiceWorkerRegistration {
54 eventtarget: EventTarget::new_inherited(),
55 active: DomRefCell::new(None),
56 installing: DomRefCell::new(None),
57 waiting: DomRefCell::new(None),
58 navigation_preload: MutNullableDom::new(None),
59 scope,
60 navigation_preload_enabled: Cell::new(false),
61 navigation_preload_header_value: DomRefCell::new(None),
62 update_via_cache: ServiceWorkerUpdateViaCache::Imports,
63 uninstalling: Cell::new(false),
64 registration_id,
65 }
66 }
67
68 pub(crate) fn new(
69 global: &GlobalScope,
70 scope: ServoUrl,
71 registration_id: ServiceWorkerRegistrationId,
72 can_gc: CanGc,
73 ) -> DomRoot<ServiceWorkerRegistration> {
74 reflect_dom_object(
75 Box::new(ServiceWorkerRegistration::new_inherited(
76 scope,
77 registration_id,
78 )),
79 global,
80 can_gc,
81 )
82 }
83
84 pub(crate) fn is_active(&self) -> bool {
86 self.active.borrow().is_some()
87 }
88
89 pub(crate) fn set_installing(&self, worker: &ServiceWorker) {
90 *self.installing.borrow_mut() = Some(Dom::from_ref(worker));
91 }
92
93 pub(crate) fn get_navigation_preload_header_value(&self) -> Option<ByteString> {
94 self.navigation_preload_header_value.borrow().clone()
95 }
96
97 pub(crate) fn set_navigation_preload_header_value(&self, value: ByteString) {
98 let mut header_value = self.navigation_preload_header_value.borrow_mut();
99 *header_value = Some(value);
100 }
101
102 pub(crate) fn get_navigation_preload_enabled(&self) -> bool {
103 self.navigation_preload_enabled.get()
104 }
105
106 pub(crate) fn set_navigation_preload_enabled(&self, flag: bool) {
107 self.navigation_preload_enabled.set(flag)
108 }
109
110 pub(crate) fn get_uninstalling(&self) -> bool {
111 self.uninstalling.get()
112 }
113
114 pub(crate) fn set_uninstalling(&self, flag: bool) {
115 self.uninstalling.set(flag)
116 }
117
118 pub(crate) fn create_scope_things(global: &GlobalScope, script_url: ServoUrl) -> ScopeThings {
119 let worker_load_origin = WorkerScriptLoadOrigin {
120 referrer_url: match global.get_referrer() {
121 Referrer::Client(url) => Some(url),
122 Referrer::ReferrerUrl(url) => Some(url),
123 _ => None,
124 },
125 referrer_policy: global.get_referrer_policy(),
126 pipeline_id: global.pipeline_id(),
127 };
128
129 let webgl_chan = global
130 .downcast::<Window>()
131 .and_then(|window| window.webgl_chan_value());
132 let worker_id = WorkerId(Uuid::new_v4());
133 let devtools_chan = global.devtools_chan().cloned();
134 let init = prepare_workerscope_init(global, None, Some(worker_id), webgl_chan);
135 let browsing_context_id = global
136 .downcast::<Window>()
137 .map(|w: &Window| w.window_proxy().browsing_context_id())
138 .expect("Service worker must be registered from a Window global");
139 let webview_id = global
140 .webview_id()
141 .expect("Service worker must have a WebViewId");
142 ScopeThings {
143 script_url,
144 init,
145 worker_load_origin,
146 devtools_chan,
147 worker_id,
148 browsing_context_id,
149 webview_id,
150 }
151 }
152
153 pub(crate) fn get_newest_worker(&self) -> Option<DomRoot<ServiceWorker>> {
155 let installing = self.installing.borrow();
156 let waiting = self.waiting.borrow();
157 let active = self.active.borrow();
158 installing
159 .as_ref()
160 .map(|sw| DomRoot::from_ref(&**sw))
161 .or_else(|| waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
162 .or_else(|| active.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
163 }
164}
165
166pub(crate) fn longest_prefix_match(stored_scope: &ServoUrl, potential_match: &ServoUrl) -> bool {
167 if stored_scope.origin() != potential_match.origin() {
168 return false;
169 }
170 let scope_chars = stored_scope.path().chars();
171 let matching_chars = potential_match.path().chars();
172 if scope_chars.count() > matching_chars.count() {
173 return false;
174 }
175
176 stored_scope
177 .path()
178 .chars()
179 .zip(potential_match.path().chars())
180 .all(|(scope, matched)| scope == matched)
181}
182
183impl ServiceWorkerRegistrationMethods<crate::DomTypeHolder> for ServiceWorkerRegistration {
184 fn GetInstalling(&self) -> Option<DomRoot<ServiceWorker>> {
186 self.installing
187 .borrow()
188 .as_ref()
189 .map(|sw| DomRoot::from_ref(&**sw))
190 }
191
192 fn GetActive(&self) -> Option<DomRoot<ServiceWorker>> {
194 self.active
195 .borrow()
196 .as_ref()
197 .map(|sw| DomRoot::from_ref(&**sw))
198 }
199
200 fn GetWaiting(&self) -> Option<DomRoot<ServiceWorker>> {
202 self.waiting
203 .borrow()
204 .as_ref()
205 .map(|sw| DomRoot::from_ref(&**sw))
206 }
207
208 fn Scope(&self) -> USVString {
210 USVString(self.scope.as_str().to_owned())
211 }
212
213 fn UpdateViaCache(&self) -> ServiceWorkerUpdateViaCache {
215 self.update_via_cache
216 }
217
218 fn NavigationPreload(&self) -> DomRoot<NavigationPreloadManager> {
220 self.navigation_preload.or_init(|| {
221 NavigationPreloadManager::new(&self.global(), self, CanGc::deprecated_note())
222 })
223 }
224}