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