script/dom/serviceworker/
cache.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_with_cx};
11use script_bindings::root::DomRoot;
12use servo_base::generic_channel::{GenericCallback, GenericSend};
13use servo_url::ServoUrl;
14use storage_traits::cache_storage::{CacheStorageThreadMessage, CacheStorageThreadResponse};
15
16use crate::dom::bindings::codegen::Bindings::CacheBinding::CacheMethods;
17use crate::dom::bindings::codegen::GenericBindings::CacheBinding::CacheQueryOptions;
18use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::refcounted::Trusted;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::fetch::request::Request;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::{Promise, RootedPromise, TracedPromise};
26
27#[dom_struct]
29pub(crate) struct Cache {
30 reflector_: Reflector,
31
32 name: DOMString,
35
36 #[no_trace]
37 #[ignore_malloc_size_of = "GenericCallback"]
38 callback: RefCell<Option<GenericCallback<CacheStorageThreadResponse>>>,
39
40 pending_promises: RefCell<VecDeque<TracedPromise>>,
42}
43
44impl Cache {
45 fn new_inherited(name: DOMString) -> Cache {
46 Cache {
47 reflector_: Reflector::new(),
48 name,
49 callback: Default::default(),
50 pending_promises: Default::default(),
51 }
52 }
53
54 pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope, name: DOMString) -> DomRoot<Cache> {
55 reflect_dom_object_with_cx(Box::new(Cache::new_inherited(name)), global, cx)
56 }
57
58 pub(crate) fn get_or_setup_callback(&self) -> GenericCallback<CacheStorageThreadResponse> {
60 if let Some(cb) = self.callback.borrow().as_ref() {
61 return cb.clone();
62 }
63
64 let global = self.global();
65 let response_listener = Trusted::new(self);
66
67 let task_source = global
68 .task_manager()
69 .dom_manipulation_task_source()
70 .to_sendable();
71 let callback = GenericCallback::new(move |message| {
72 let response_listener = response_listener.clone();
73 let response = match message {
74 Ok(inner) => Some(inner),
75 Err(err) => {
76 error!("Error in Cache callback {:?}.", err);
77 None
78 },
79 };
80 task_source.queue(task!(set_request_result_to_database: move |cx| {
81 let cache = response_listener.root();
82 cache.handle_response(cx, response)
83 }));
84 })
85 .expect("Could not create Cache callback");
86
87 *self.callback.borrow_mut() = Some(callback.clone());
88
89 callback
90 }
91
92 fn handle_response(&self, cx: &mut JSContext, response: Option<CacheStorageThreadResponse>) {
93 let response = match response {
94 Some(response) => response,
95 None => {
96 let Some(promise) = self
97 .pending_promises
98 .borrow_mut()
99 .pop_front()
100 .map(|promise| promise.root(cx))
101 else {
102 error!("No pending promise for Cache response.");
103 return;
104 };
105 promise.reject_error(
106 cx,
107 Error::Operation(Some("No response from Cache backend.".to_string())),
108 );
109 return;
110 },
111 };
112 match response {
113 CacheStorageThreadResponse::KeysResult(results) => {
115 let mut request_list: Vec<DomRoot<Request>> = Vec::new();
122
123 let Some(promise) = self
124 .pending_promises
125 .borrow_mut()
126 .pop_front()
127 .map(|promise| promise.root(cx))
128 else {
129 debug_assert!(false, "No pending promise for Cache KeysResult response.");
130 return;
131 };
132
133 for url in results.unwrap_or_default() {
137 let Ok(url) = ServoUrl::parse(&url) else {
138 promise.reject_error(cx, Error::Type(c"Invalid URL".to_owned()));
139 return;
140 };
141 let request = Request::new(cx, &self.global(), None, url);
142 request_list.push(request);
144 }
145
146 promise.resolve_native(cx, &request_list);
148 },
149 CacheStorageThreadResponse::DeleteCacheResult(_result) => debug_assert!(
150 false,
151 "Unexpected DeleteCacheResult response in Cache handle_response."
152 ),
153 CacheStorageThreadResponse::HasCacheResult(_result) => debug_assert!(
154 false,
155 "Unexpected HasCacheResult response in Cache handle_response."
156 ),
157 CacheStorageThreadResponse::OpenCacheResult { .. } => debug_assert!(
158 false,
159 "Unexpected OpenCacheResult response in Cache handle_response."
160 ),
161 }
162 }
163}
164
165impl CacheMethods<crate::DomTypeHolder> for Cache {
166 fn Keys(
168 &self,
169 cx: &mut JSContext,
170 request: Option<RequestOrUSVString>,
171 _options: &CacheQueryOptions,
172 ) -> RootedPromise {
173 let mut r: Option<DomRoot<Request>> = None;
175
176 let global = self.global();
179
180 let promise = Promise::new_rooted(cx, &global);
183
184 if let Some(request) = request {
186 if let RequestOrUSVString::Request(request) = request {
188 r = Some(request);
190 } else if let RequestOrUSVString::USVString(request_string) = request {
192 let Ok(url) = ServoUrl::parse(&request_string) else {
196 promise.reject_error(cx, Error::Type(c"Invalid URL".to_owned()));
199 return promise;
200 };
201 let request = Request::new(cx, &global, None, url);
202 r = Some(request);
203 }
204 }
205
206 let _ = r;
208
209 let callback = self.get_or_setup_callback();
211 if global
212 .storage_threads()
213 .send(CacheStorageThreadMessage::Keys {
214 cache_name: self.name.to_string(),
215 callback,
216 origin: global.origin().immutable().clone(),
217 })
218 .is_err()
219 {
220 promise.reject_error(
221 cx,
222 Error::Operation(Some("Could not run the parallel steps.".to_string())),
223 );
224 return promise;
225 }
226
227 self.pending_promises
228 .borrow_mut()
229 .push_back(promise.to_traced());
230
231 promise
232 }
233}