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