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;
25
26#[dom_struct]
28pub(crate) struct CacheStorage {
29 reflector_: Reflector,
30
31 #[no_trace]
32 #[ignore_malloc_size_of = "GenericCallback"]
33 callback: RefCell<Option<GenericCallback<CacheStorageThreadResponse>>>,
34
35 #[conditional_malloc_size_of]
37 pending_promises: RefCell<VecDeque<Rc<Promise>>>,
38}
39
40impl CacheStorage {
41 fn new_inherited() -> CacheStorage {
42 CacheStorage {
43 reflector_: Reflector::new(),
44 callback: Default::default(),
45 pending_promises: Default::default(),
46 }
47 }
48
49 pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<CacheStorage> {
50 reflect_dom_object_with_cx(Box::new(CacheStorage::new_inherited()), global, cx)
51 }
52
53 fn get_or_setup_callback(&self) -> GenericCallback<CacheStorageThreadResponse> {
55 if let Some(cb) = self.callback.borrow().as_ref() {
56 return cb.clone();
57 }
58
59 let global = self.global();
60 let response_listener = Trusted::new(self);
61
62 let task_source = global
63 .task_manager()
64 .database_access_task_source()
65 .to_sendable();
66 let callback = GenericCallback::new(move |message| {
67 let response_listener = response_listener.clone();
68 let response = match message {
69 Ok(inner) => Some(inner),
70 Err(err) => {
71 error!("Error in CacheStorage callback {:?}.", err);
72 None
73 },
74 };
75 task_source.queue(task!(set_request_result_to_database: move |cx| {
76 let cache_storage = response_listener.root();
77 cache_storage.handle_response(cx, response)
78 }));
79 })
80 .expect("Could not create CacheStorage callback");
81
82 *self.callback.borrow_mut() = Some(callback.clone());
83
84 callback
85 }
86
87 fn handle_response(&self, cx: &mut JSContext, response: Option<CacheStorageThreadResponse>) {
88 let response = match response {
89 Some(response) => response,
90 None => {
91 if self.pending_promises.borrow_mut().pop_front().is_none() {
92 error!("No pending promise for CacheStorage response.");
93 }
94 return;
95 },
96 };
97 match response {
98 CacheStorageThreadResponse::HasCacheResult(result) => {
102 let promise = self.pending_promises.borrow_mut().pop_front();
103 if let Some(promise) = promise {
104 match result {
105 Ok(has_cache) => {
106 promise.resolve_native(cx, &has_cache);
111 },
112 Err(err) => promise.reject_error(cx, Error::Operation(Some(err))),
113 }
114 } else {
115 error!("No pending promise for HasCacheResult response.");
116 }
117 },
118 }
119 }
120}
121
122fn relevant_name_to_cache_map(
124 global: &GlobalScope,
125 origin: ImmutableOrigin,
126) -> Result<StorageProxyMap, Error> {
127 let handle = global.storage_threads().client_storage_handle();
132 let message = handle
133 .obtain_a_storage_bottle_map(
134 StorageType::Local,
135 global.webview_id(),
136 StorageIdentifier::Caches,
137 origin,
138 )
139 .recv();
140 let Ok(response) = message else {
141 return Err(Error::Operation(None));
142 };
143 let Ok(proxy_map) = response else {
144 return Err(Error::Operation(None));
145 };
146 Ok(proxy_map)
147}
148
149impl CacheStorageMethods<crate::DomTypeHolder> for CacheStorage {
150 fn Has(&self, cx: &mut JSContext, cache_name: DOMString) -> Rc<Promise> {
152 let global = self.global();
153
154 let promise = Promise::new(cx, &global);
156
157 let callback = self.get_or_setup_callback();
159 let origin = global.origin().immutable().clone();
160 let proxy_map = match relevant_name_to_cache_map(&global, origin.clone()) {
161 Ok(proxy_map) => proxy_map,
162 Err(err) => {
163 promise.reject_error(cx, err);
164 return promise;
165 },
166 };
167 if global
168 .storage_threads()
169 .send(CacheStorageThreadMessage::HasCache {
170 cache_name: cache_name.to_string(),
171 callback,
172 proxy: proxy_map,
173 origin,
174 })
175 .is_err()
176 {
177 promise.reject_error(cx, Error::Operation(None));
178 return promise;
179 }
180
181 self.pending_promises
182 .borrow_mut()
183 .push_back(promise.clone());
184
185 promise
186 }
187}