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