script/dom/workers/
serviceworkerregistration.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    /// Does this registration have an active worker?
85    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    // https://w3c.github.io/ServiceWorker/#get-newest-worker-algorithm
154    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    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-installing-attribute>
185    fn GetInstalling(&self) -> Option<DomRoot<ServiceWorker>> {
186        self.installing
187            .borrow()
188            .as_ref()
189            .map(|sw| DomRoot::from_ref(&**sw))
190    }
191
192    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-active-attribute>
193    fn GetActive(&self) -> Option<DomRoot<ServiceWorker>> {
194        self.active
195            .borrow()
196            .as_ref()
197            .map(|sw| DomRoot::from_ref(&**sw))
198    }
199
200    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-waiting-attribute>
201    fn GetWaiting(&self) -> Option<DomRoot<ServiceWorker>> {
202        self.waiting
203            .borrow()
204            .as_ref()
205            .map(|sw| DomRoot::from_ref(&**sw))
206    }
207
208    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-scope-attribute>
209    fn Scope(&self) -> USVString {
210        USVString(self.scope.as_str().to_owned())
211    }
212
213    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-updateviacache>
214    fn UpdateViaCache(&self) -> ServiceWorkerUpdateViaCache {
215        self.update_via_cache
216    }
217
218    /// <https://w3c.github.io/ServiceWorker/#service-worker-registration-navigationpreload>
219    fn NavigationPreload(&self) -> DomRoot<NavigationPreloadManager> {
220        self.navigation_preload.or_init(|| {
221            NavigationPreloadManager::new(&self.global(), self, CanGc::deprecated_note())
222        })
223    }
224}