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