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_with_cx(cx, &value),
79 Err(message) => promise.reject_error_with_cx(cx, StorageManager::type_error_from_string(message)),
80 }
81 }));
82 }
83}
84
85struct StorageManagerEstimateResponseHandler {
86 trusted_promise: Option<TrustedPromise>,
87 task_source: SendableTaskSource,
88}
89
90impl StorageManagerEstimateResponseHandler {
91 fn new(trusted_promise: TrustedPromise, task_source: SendableTaskSource) -> Self {
92 Self {
93 trusted_promise: Some(trusted_promise),
94 task_source,
95 }
96 }
97
98 fn handle(&mut self, result: Result<(u64, u64), String>) {
99 let Some(trusted_promise) = self.trusted_promise.take() else {
100 error!("StorageManager callback called twice.");
101 return;
102 };
103
104 self.task_source
105 .queue(task!(storage_manager_estimate_response: move |cx| {
106 let promise = trusted_promise.root();
107 match result {
108 Ok((usage, quota)) => {
109 let mut estimate = StorageEstimate::empty();
110 estimate.usage = Some(usage);
111 estimate.quota = Some(quota);
112 promise.resolve_native_with_cx(cx, &estimate);
113 },
114 Err(message) => {
115 promise.reject_error_with_cx(cx, StorageManager::type_error_from_string(message));
116 },
117 }
118 }));
119 }
120}
121
122impl StorageManagerMethods<crate::DomTypeHolder> for StorageManager {
123 fn Persisted(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
125 let promise = Promise::new_in_current_realm(comp, can_gc);
127 let global = self.global();
129
130 if self.origin_cannot_obtain_local_storage_shelf() {
134 promise.reject_error(
135 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
136 can_gc,
137 );
138 return promise;
139 }
140
141 let mut handler = StorageManagerBooleanResponseHandler::new(
147 TrustedPromise::new(promise.clone()),
148 global.task_manager().storage_task_source().to_sendable(),
149 );
150 let callback = GenericCallback::new(move |message| {
151 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
152 })
153 .expect("Could not create StorageManager persisted callback");
154
155 if global
156 .storage_threads()
157 .persisted(global.origin().immutable().clone(), callback.clone())
158 .is_err() &&
159 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
160 {
161 error!("Failed to deliver StorageManager persisted error: {error}");
162 }
163
164 promise
166 }
167
168 fn Persist(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
170 let promise = Promise::new_in_current_realm(comp, can_gc);
172 let global = self.global();
174
175 if self.origin_cannot_obtain_local_storage_shelf() {
179 promise.reject_error(
180 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
181 can_gc,
182 );
183 return promise;
184 }
185
186 let permission = request_permission_to_use(PermissionName::Persistent_storage, &global);
190
191 let mut handler = StorageManagerBooleanResponseHandler::new(
199 TrustedPromise::new(promise.clone()),
200 global.task_manager().storage_task_source().to_sendable(),
201 );
202 let callback = GenericCallback::new(move |message| {
203 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
204 })
205 .expect("Could not create StorageManager persist callback");
206
207 if global
208 .storage_threads()
209 .persist(
210 global.origin().immutable().clone(),
211 permission == PermissionState::Granted,
212 callback.clone(),
213 )
214 .is_err() &&
215 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
216 {
217 error!("Failed to deliver StorageManager persist error: {error}");
218 }
219
220 promise
222 }
223
224 fn Estimate(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
226 let promise = Promise::new_in_current_realm(comp, can_gc);
228 let global = self.global();
230
231 if self.origin_cannot_obtain_local_storage_shelf() {
235 promise.reject_error(
236 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
237 can_gc,
238 );
239 return promise;
240 }
241
242 let mut handler = StorageManagerEstimateResponseHandler::new(
251 TrustedPromise::new(promise.clone()),
252 global.task_manager().storage_task_source().to_sendable(),
253 );
254 let callback = GenericCallback::new(move |message| {
255 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
256 })
257 .expect("Could not create StorageManager estimate callback");
258
259 if global
260 .storage_threads()
261 .estimate(global.origin().immutable().clone(), callback.clone())
262 .is_err() &&
263 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
264 {
265 error!("Failed to deliver StorageManager estimate error: {error}");
266 }
267
268 promise
270 }
271}