script/dom/serviceworker/
cachestorage.rs1use std::cell::RefCell;
6use std::collections::VecDeque;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11use script_bindings::root::DomRoot;
12use servo_base::generic_channel::{GenericCallback, GenericSend};
13use servo_url::ImmutableOrigin;
14use storage_traits::cache_storage::{CacheStorageThreadMessage, CacheStorageThreadResponse};
15use storage_traits::client_storage::{StorageIdentifier, StorageProxyMap, StorageType};
16
17use crate::dom::bindings::codegen::Bindings::CacheStorageBinding::CacheStorageMethods;
18use crate::dom::bindings::error::Error;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::str::DOMString;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::serviceworker::cache::Cache;
24use crate::dom::{Promise, RootedPromise, TracedPromise};
25
26#[dom_struct]
28pub(crate) struct CacheStorage {
29 reflector_: Reflector,
30
31 #[no_trace]
32 callback: RefCell<Option<GenericCallback<CacheStorageThreadResponse>>>,
33
34 pending_promises: RefCell<VecDeque<TracedPromise>>,
36}
37
38impl CacheStorage {
39 fn new_inherited() -> CacheStorage {
40 CacheStorage {
41 reflector_: Reflector::new(),
42 callback: Default::default(),
43 pending_promises: Default::default(),
44 }
45 }
46
47 pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<CacheStorage> {
48 reflect_dom_object(cx, Box::new(CacheStorage::new_inherited()), global)
49 }
50
51 fn get_or_setup_callback(&self) -> GenericCallback<CacheStorageThreadResponse> {
53 if let Some(cb) = self.callback.borrow().as_ref() {
54 return cb.clone();
55 }
56
57 let global = self.global();
58 let response_listener = Trusted::new(self);
59
60 let task_source = global
61 .task_manager()
62 .dom_manipulation_task_source()
63 .to_sendable();
64 let callback = GenericCallback::new(move |message| {
65 let response_listener = response_listener.clone();
66 let response = match message {
67 Ok(inner) => Some(inner),
68 Err(err) => {
69 error!("Error in CacheStorage callback {:?}.", err);
70 None
71 },
72 };
73 task_source.queue(task!(set_request_result_to_database: move |cx| {
74 let cache_storage = response_listener.root();
75 cache_storage.handle_response(cx, response)
76 }));
77 })
78 .expect("Could not create CacheStorage callback");
79
80 *self.callback.borrow_mut() = Some(callback.clone());
81
82 callback
83 }
84
85 fn handle_response(&self, cx: &mut JSContext, response: Option<CacheStorageThreadResponse>) {
86 let response = match response {
87 Some(response) => response,
88 None => {
89 let Some(promise) = self
90 .pending_promises
91 .borrow_mut()
92 .pop_front()
93 .map(|promise| promise.root(cx))
94 else {
95 error!("No pending promise for CacheStorage response.");
96 return;
97 };
98 promise.reject_error(
99 cx,
100 Error::Operation(Some("No response from CacheStorage backend.".to_string())),
101 );
102 return;
103 },
104 };
105 match response {
106 CacheStorageThreadResponse::HasCacheResult(result) => {
110 let Some(promise) = self
111 .pending_promises
112 .borrow_mut()
113 .pop_front()
114 .map(|promise| promise.root(cx))
115 else {
116 debug_assert!(false, "No pending promise for HasCacheResult response.");
117 return;
118 };
119 let Ok(has_cache) = result else {
120 promise.reject_error(
121 cx,
122 Error::Operation(Some(
123 result
124 .err()
125 .unwrap_or_else(|| "HasCacheResult error".to_string()),
126 )),
127 );
128 return;
129 };
130 promise.resolve_native(cx, &has_cache);
135 },
136 CacheStorageThreadResponse::OpenCacheResult { result, cache_name } => {
139 let Some(promise) = self
140 .pending_promises
141 .borrow_mut()
142 .pop_front()
143 .map(|promise| promise.root(cx))
144 else {
145 debug_assert!(false, "No pending promise for OpenCacheResult response.");
146 return;
147 };
148 if result.is_err() {
149 promise.reject_error(
150 cx,
151 Error::Operation(Some(
152 result
153 .err()
154 .unwrap_or_else(|| "OpenCacheResult error".to_string()),
155 )),
156 );
157 return;
158 };
159 let cache = Cache::new(cx, &self.global(), DOMString::from(cache_name));
161 promise.resolve_native(cx, &cache);
162 },
163 CacheStorageThreadResponse::DeleteCacheResult(result) => {
165 let Some(promise) = self
166 .pending_promises
167 .borrow_mut()
168 .pop_front()
169 .map(|promise| promise.root(cx))
170 else {
171 debug_assert!(false, "No pending promise for DeleteCacheResult response.");
172 return;
173 };
174 let Ok(deleted) = result else {
175 promise.reject_error(
176 cx,
177 Error::Operation(Some(
178 result
179 .err()
180 .unwrap_or_else(|| "DeleteCacheResult error".to_string()),
181 )),
182 );
183 return;
184 };
185 promise.resolve_native(cx, &deleted);
186 },
187 CacheStorageThreadResponse::KeysResult(_) => debug_assert!(
188 false,
189 "Unexpected KeysResult response in CacheStorage handle_response."
190 ),
191 }
192 }
193}
194
195fn relevant_name_to_cache_map(
197 global: &GlobalScope,
198 origin: ImmutableOrigin,
199) -> Result<StorageProxyMap, Error> {
200 let handle = global.storage_threads().client_storage_handle();
205 let message = handle
206 .obtain_a_storage_bottle_map(
207 StorageType::Local,
208 global.webview_id(),
209 StorageIdentifier::Caches,
210 origin,
211 )
212 .recv();
213 let Ok(response) = message else {
214 return Err(Error::Operation(Some(
215 "Could not obtain a local storage bottle map.".to_string(),
216 )));
217 };
218 let Ok(proxy_map) = response else {
219 return Err(Error::Operation(Some(
220 "Could not obtain a local storage bottle map.".to_string(),
221 )));
222 };
223 Ok(proxy_map)
224}
225
226impl CacheStorageMethods<crate::DomTypeHolder> for CacheStorage {
227 fn Has(&self, cx: &mut JSContext, cache_name: DOMString) -> RootedPromise {
229 let global = self.global();
230
231 let promise = Promise::new(cx, &global);
233
234 let callback = self.get_or_setup_callback();
236 let origin = global.origin().immutable().clone();
237 let proxy_map = match relevant_name_to_cache_map(&global, origin.clone()) {
238 Ok(proxy_map) => proxy_map,
239 Err(err) => {
240 promise.reject_error(cx, err);
241 return promise;
242 },
243 };
244 if global
245 .storage_threads()
246 .send(CacheStorageThreadMessage::HasCache {
247 cache_name: cache_name.to_string(),
248 callback,
249 proxy: proxy_map,
250 origin,
251 })
252 .is_err()
253 {
254 promise.reject_error(
255 cx,
256 Error::Operation(Some("Could not run the parallel steps.".to_string())),
257 );
258 return promise;
259 }
260
261 self.pending_promises
262 .borrow_mut()
263 .push_back(promise.to_traced());
264
265 promise
266 }
267
268 fn Open(&self, cx: &mut JSContext, cache_name: DOMString) -> RootedPromise {
270 let global = self.global();
272 let promise = Promise::new(cx, &global);
273
274 let callback = self.get_or_setup_callback();
276 let origin = global.origin().immutable().clone();
277 let proxy_map = match relevant_name_to_cache_map(&global, origin.clone()) {
278 Ok(proxy_map) => proxy_map,
279 Err(err) => {
280 promise.reject_error(cx, err);
281 return promise;
282 },
283 };
284 if global
285 .storage_threads()
286 .send(CacheStorageThreadMessage::OpenCache {
287 cache_name: cache_name.to_string(),
288 callback,
289 proxy: proxy_map,
290 origin,
291 })
292 .is_err()
293 {
294 promise.reject_error(
295 cx,
296 Error::Operation(Some("Could not run the parallel steps.".to_string())),
297 );
298 return promise;
299 }
300
301 self.pending_promises
302 .borrow_mut()
303 .push_back(promise.to_traced());
304
305 promise
307 }
308
309 fn Delete(&self, cx: &mut JSContext, cache_name: DOMString) -> RootedPromise {
311 let global = self.global();
320 let promise = Promise::new(cx, &global);
321
322 let callback = self.get_or_setup_callback();
324 let origin = global.origin().immutable().clone();
325 let proxy_map = match relevant_name_to_cache_map(&global, origin.clone()) {
326 Ok(proxy_map) => proxy_map,
327 Err(err) => {
328 promise.reject_error(cx, err);
329 return promise;
330 },
331 };
332 if global
333 .storage_threads()
334 .send(CacheStorageThreadMessage::DeleteCache {
335 cache_name: cache_name.to_string(),
336 callback,
337 proxy: proxy_map,
338 origin,
339 })
340 .is_err()
341 {
342 promise.reject_error(
343 cx,
344 Error::Operation(Some("Could not run the parallel steps.".to_string())),
345 );
346 return promise;
347 }
348
349 self.pending_promises
350 .borrow_mut()
351 .push_back(promise.to_traced());
352
353 promise
355 }
356}