script/dom/
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    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
67    pub(crate) fn new(
68        global: &GlobalScope,
69        scope: ServoUrl,
70        registration_id: ServiceWorkerRegistrationId,
71        can_gc: CanGc,
72    ) -> DomRoot<ServiceWorkerRegistration> {
73        reflect_dom_object(
74            Box::new(ServiceWorkerRegistration::new_inherited(
75                scope,
76                registration_id,
77            )),
78            global,
79            can_gc,
80        )
81    }
82
83    /// Does this registration have an active worker?
84    pub(crate) fn is_active(&self) -> bool {
85        self.active.borrow().is_some()
86    }
87
88    pub(crate) fn set_installing(&self, worker: &ServiceWorker) {
89        *self.installing.borrow_mut() = Some(Dom::from_ref(worker));
90    }
91
92    pub(crate) fn get_navigation_preload_header_value(&self) -> Option<ByteString> {
93        self.navigation_preload_header_value.borrow().clone()
94    }
95
96    pub(crate) fn set_navigation_preload_header_value(&self, value: ByteString) {
97        let mut header_value = self.navigation_preload_header_value.borrow_mut();
98        *header_value = Some(value);
99    }
100
101    pub(crate) fn get_navigation_preload_enabled(&self) -> bool {
102        self.navigation_preload_enabled.get()
103    }
104
105    pub(crate) fn set_navigation_preload_enabled(&self, flag: bool) {
106        self.navigation_preload_enabled.set(flag)
107    }
108
109    pub(crate) fn get_uninstalling(&self) -> bool {
110        self.uninstalling.get()
111    }
112
113    pub(crate) fn set_uninstalling(&self, flag: bool) {
114        self.uninstalling.set(flag)
115    }
116
117    pub(crate) fn create_scope_things(global: &GlobalScope, script_url: ServoUrl) -> ScopeThings {
118        let worker_load_origin = WorkerScriptLoadOrigin {
119            referrer_url: match global.get_referrer() {
120                Referrer::Client(url) => Some(url),
121                Referrer::ReferrerUrl(url) => Some(url),
122                _ => None,
123            },
124            referrer_policy: global.get_referrer_policy(),
125            pipeline_id: global.pipeline_id(),
126        };
127
128        let worker_id = WorkerId(Uuid::new_v4());
129        let devtools_chan = global.devtools_chan().cloned();
130        let init = prepare_workerscope_init(global, None, None);
131        ScopeThings {
132            script_url,
133            init,
134            worker_load_origin,
135            devtools_chan,
136            worker_id,
137        }
138    }
139
140    // https://w3c.github.io/ServiceWorker/#get-newest-worker-algorithm
141    pub(crate) fn get_newest_worker(&self) -> Option<DomRoot<ServiceWorker>> {
142        let installing = self.installing.borrow();
143        let waiting = self.waiting.borrow();
144        let active = self.active.borrow();
145        installing
146            .as_ref()
147            .map(|sw| DomRoot::from_ref(&**sw))
148            .or_else(|| waiting.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
149            .or_else(|| active.as_ref().map(|sw| DomRoot::from_ref(&**sw)))
150    }
151}
152
153pub(crate) fn longest_prefix_match(stored_scope: &ServoUrl, potential_match: &ServoUrl) -> bool {
154    if stored_scope.origin() != potential_match.origin() {
155        return false;
156    }
157    let scope_chars = stored_scope.path().chars();
158    let matching_chars = potential_match.path().chars();
159    if scope_chars.count() > matching_chars.count() {
160        return false;
161    }
162
163    stored_scope
164        .path()
165        .chars()
166        .zip(potential_match.path().chars())
167        .all(|(scope, matched)| scope == matched)
168}
169
170impl ServiceWorkerRegistrationMethods<crate::DomTypeHolder> for ServiceWorkerRegistration {
171    // https://w3c.github.io/ServiceWorker/#service-worker-registration-installing-attribute
172    fn GetInstalling(&self) -> Option<DomRoot<ServiceWorker>> {
173        self.installing
174            .borrow()
175            .as_ref()
176            .map(|sw| DomRoot::from_ref(&**sw))
177    }
178
179    // https://w3c.github.io/ServiceWorker/#service-worker-registration-active-attribute
180    fn GetActive(&self) -> Option<DomRoot<ServiceWorker>> {
181        self.active
182            .borrow()
183            .as_ref()
184            .map(|sw| DomRoot::from_ref(&**sw))
185    }
186
187    // https://w3c.github.io/ServiceWorker/#service-worker-registration-waiting-attribute
188    fn GetWaiting(&self) -> Option<DomRoot<ServiceWorker>> {
189        self.waiting
190            .borrow()
191            .as_ref()
192            .map(|sw| DomRoot::from_ref(&**sw))
193    }
194
195    // https://w3c.github.io/ServiceWorker/#service-worker-registration-scope-attribute
196    fn Scope(&self) -> USVString {
197        USVString(self.scope.as_str().to_owned())
198    }
199
200    // https://w3c.github.io/ServiceWorker/#service-worker-registration-updateviacache
201    fn UpdateViaCache(&self) -> ServiceWorkerUpdateViaCache {
202        self.update_via_cache
203    }
204
205    // https://w3c.github.io/ServiceWorker/#service-worker-registration-navigationpreload
206    fn NavigationPreload(&self) -> DomRoot<NavigationPreloadManager> {
207        self.navigation_preload
208            .or_init(|| NavigationPreloadManager::new(&self.global(), self, CanGc::note()))
209    }
210}