script/dom/
storagemanager.rs1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use servo_base::generic_channel::GenericCallback;
9
10use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
11 PermissionName, PermissionState,
12};
13use crate::dom::bindings::codegen::Bindings::StorageManagerBinding::{
14 StorageEstimate, StorageManagerMethods,
15};
16use crate::dom::bindings::error::Error;
17use crate::dom::bindings::refcounted::TrustedPromise;
18use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::permissions::request_permission_to_use;
22use crate::dom::promise::Promise;
23use crate::realms::InRealm;
24use crate::script_runtime::CanGc;
25use crate::task_source::SendableTaskSource;
26
27#[dom_struct]
28pub(crate) struct StorageManager {
29 reflector_: Reflector,
30}
31
32impl StorageManager {
33 fn new_inherited() -> StorageManager {
34 StorageManager {
35 reflector_: Reflector::new(),
36 }
37 }
38
39 pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<StorageManager> {
40 reflect_dom_object(Box::new(StorageManager::new_inherited()), global, can_gc)
41 }
42
43 fn origin_cannot_obtain_local_storage_shelf(&self) -> bool {
44 !self.global().origin().is_tuple()
45 }
46
47 fn type_error_from_string(message: String) -> Error {
48 let message = std::ffi::CString::new(message)
49 .unwrap_or_else(|_| c"Storage operation failed".to_owned());
50 Error::Type(message)
51 }
52}
53
54struct StorageManagerBooleanResponseHandler {
55 trusted_promise: Option<TrustedPromise>,
56 task_source: SendableTaskSource,
57}
58
59impl StorageManagerBooleanResponseHandler {
60 fn new(trusted_promise: TrustedPromise, task_source: SendableTaskSource) -> Self {
61 Self {
62 trusted_promise: Some(trusted_promise),
63 task_source,
64 }
65 }
66
67 fn handle(&mut self, result: Result<bool, String>) {
68 let Some(trusted_promise) = self.trusted_promise.take() else {
69 error!("StorageManager callback called twice.");
70 return;
71 };
72
73 self.task_source
74 .queue(task!(storage_manager_boolean_response: move |cx| {
75 let promise = trusted_promise.root();
76 match result {
77 Ok(value) => promise.resolve_native(&value, CanGc::from_cx(cx)),
78 Err(message) => promise.reject_error(
79 StorageManager::type_error_from_string(message),
80 CanGc::from_cx(cx),
81 ),
82 }
83 }));
84 }
85}
86
87struct StorageManagerEstimateResponseHandler {
88 trusted_promise: Option<TrustedPromise>,
89 task_source: SendableTaskSource,
90}
91
92impl StorageManagerEstimateResponseHandler {
93 fn new(trusted_promise: TrustedPromise, task_source: SendableTaskSource) -> Self {
94 Self {
95 trusted_promise: Some(trusted_promise),
96 task_source,
97 }
98 }
99
100 fn handle(&mut self, result: Result<(u64, u64), String>) {
101 let Some(trusted_promise) = self.trusted_promise.take() else {
102 error!("StorageManager callback called twice.");
103 return;
104 };
105
106 self.task_source
107 .queue(task!(storage_manager_estimate_response: move |cx| {
108 let promise = trusted_promise.root();
109 match result {
110 Ok((usage, quota)) => {
111 let mut estimate = StorageEstimate::empty();
112 estimate.usage = Some(usage);
113 estimate.quota = Some(quota);
114 promise.resolve_native(&estimate, CanGc::from_cx(cx));
115 },
116 Err(message) => {
117 promise.reject_error(
118 StorageManager::type_error_from_string(message),
119 CanGc::from_cx(cx),
120 );
121 },
122 }
123 }));
124 }
125}
126
127impl StorageManagerMethods<crate::DomTypeHolder> for StorageManager {
128 fn Persisted(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
130 let promise = Promise::new_in_current_realm(comp, can_gc);
132 let global = self.global();
134
135 if self.origin_cannot_obtain_local_storage_shelf() {
139 promise.reject_error(
140 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
141 can_gc,
142 );
143 return promise;
144 }
145
146 let mut handler = StorageManagerBooleanResponseHandler::new(
152 TrustedPromise::new(promise.clone()),
153 global.task_manager().storage_task_source().to_sendable(),
154 );
155 let callback = GenericCallback::new(move |message| {
156 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
157 })
158 .expect("Could not create StorageManager persisted callback");
159
160 if global
161 .storage_threads()
162 .persisted(global.origin().immutable().clone(), callback.clone())
163 .is_err()
164 {
165 if let Err(error) = callback.send(Err("Failed to queue storage task".to_owned())) {
166 error!("Failed to deliver StorageManager persisted error: {error}");
167 }
168 }
169
170 promise
172 }
173
174 fn Persist(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
176 let promise = Promise::new_in_current_realm(comp, can_gc);
178 let global = self.global();
180
181 if self.origin_cannot_obtain_local_storage_shelf() {
185 promise.reject_error(
186 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
187 can_gc,
188 );
189 return promise;
190 }
191
192 let permission = request_permission_to_use(PermissionName::Persistent_storage, &global);
196
197 let mut handler = StorageManagerBooleanResponseHandler::new(
205 TrustedPromise::new(promise.clone()),
206 global.task_manager().storage_task_source().to_sendable(),
207 );
208 let callback = GenericCallback::new(move |message| {
209 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
210 })
211 .expect("Could not create StorageManager persist callback");
212
213 if global
214 .storage_threads()
215 .persist(
216 global.origin().immutable().clone(),
217 permission == PermissionState::Granted,
218 callback.clone(),
219 )
220 .is_err()
221 {
222 if let Err(error) = callback.send(Err("Failed to queue storage task".to_owned())) {
223 error!("Failed to deliver StorageManager persist error: {error}");
224 }
225 }
226
227 promise
229 }
230
231 fn Estimate(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
233 let promise = Promise::new_in_current_realm(comp, can_gc);
235 let global = self.global();
237
238 if self.origin_cannot_obtain_local_storage_shelf() {
242 promise.reject_error(
243 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
244 can_gc,
245 );
246 return promise;
247 }
248
249 let mut handler = StorageManagerEstimateResponseHandler::new(
258 TrustedPromise::new(promise.clone()),
259 global.task_manager().storage_task_source().to_sendable(),
260 );
261 let callback = GenericCallback::new(move |message| {
262 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
263 })
264 .expect("Could not create StorageManager estimate callback");
265
266 if global
267 .storage_threads()
268 .estimate(global.origin().immutable().clone(), callback.clone())
269 .is_err()
270 {
271 if let Err(error) = callback.send(Err("Failed to queue storage task".to_owned())) {
272 error!("Failed to deliver StorageManager estimate error: {error}");
273 }
274 }
275
276 promise
278 }
279}