script/dom/storage/
storagemanager.rs1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::realm::CurrentRealm;
8use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
9use servo_base::generic_channel::GenericCallback;
10
11use crate::dom::RootedPromise;
12use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
13 PermissionName, PermissionState,
14};
15use crate::dom::bindings::codegen::Bindings::StorageManagerBinding::{
16 StorageEstimate, StorageManagerMethods,
17};
18use crate::dom::bindings::error::Error;
19use crate::dom::bindings::refcounted::TrustedPromise;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::permissions::request_permission_to_use;
24use crate::dom::promise::Promise;
25use crate::tasks::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(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<StorageManager> {
40 reflect_dom_object_with_cx(Box::new(StorageManager::new_inherited()), global, cx)
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(cx);
76 match result {
77 Ok(value) => promise.resolve_native(cx, &value),
78 Err(message) => promise.reject_error(cx, StorageManager::type_error_from_string(message)),
79 }
80 }));
81 }
82}
83
84struct StorageManagerEstimateResponseHandler {
85 trusted_promise: Option<TrustedPromise>,
86 task_source: SendableTaskSource,
87}
88
89impl StorageManagerEstimateResponseHandler {
90 fn new(trusted_promise: TrustedPromise, task_source: SendableTaskSource) -> Self {
91 Self {
92 trusted_promise: Some(trusted_promise),
93 task_source,
94 }
95 }
96
97 fn handle(&mut self, result: Result<(u64, u64), String>) {
98 let Some(trusted_promise) = self.trusted_promise.take() else {
99 error!("StorageManager callback called twice.");
100 return;
101 };
102
103 self.task_source
104 .queue(task!(storage_manager_estimate_response: move |cx| {
105 let promise = trusted_promise.root(cx);
106 match result {
107 Ok((usage, quota)) => {
108 let mut estimate = StorageEstimate::empty();
109 estimate.usage = Some(usage);
110 estimate.quota = Some(quota);
111 promise.resolve_native(cx, &estimate);
112 },
113 Err(message) => {
114 promise.reject_error(cx, StorageManager::type_error_from_string(message));
115 },
116 }
117 }));
118 }
119}
120
121impl StorageManagerMethods<crate::DomTypeHolder> for StorageManager {
122 fn Persisted(&self, cx: &mut CurrentRealm) -> RootedPromise {
124 let promise = Promise::new_in_realm_rooted(cx);
126 let global = self.global();
128
129 if self.origin_cannot_obtain_local_storage_shelf() {
133 promise.reject_error(
134 cx,
135 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
136 );
137 return promise;
138 }
139
140 let mut handler = StorageManagerBooleanResponseHandler::new(
146 TrustedPromise::from(&promise),
147 global.task_manager().storage_task_source().to_sendable(),
148 );
149 let callback = GenericCallback::new(move |message| {
150 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
151 })
152 .expect("Could not create StorageManager persisted callback");
153
154 if global
155 .storage_threads()
156 .persisted(global.origin().immutable().clone(), callback.clone())
157 .is_err() &&
158 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
159 {
160 error!("Failed to deliver StorageManager persisted error: {error}");
161 }
162
163 promise
165 }
166
167 fn Persist(&self, cx: &mut CurrentRealm) -> RootedPromise {
169 let promise = Promise::new_in_realm_rooted(cx);
171 let global = self.global();
173
174 if self.origin_cannot_obtain_local_storage_shelf() {
178 promise.reject_error(
179 cx,
180 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
181 );
182 return promise;
183 }
184
185 let permission = request_permission_to_use(PermissionName::Persistent_storage, &global);
189
190 let mut handler = StorageManagerBooleanResponseHandler::new(
198 TrustedPromise::from(&promise),
199 global.task_manager().storage_task_source().to_sendable(),
200 );
201 let callback = GenericCallback::new(move |message| {
202 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
203 })
204 .expect("Could not create StorageManager persist callback");
205
206 if global
207 .storage_threads()
208 .persist(
209 global.origin().immutable().clone(),
210 permission == PermissionState::Granted,
211 callback.clone(),
212 )
213 .is_err() &&
214 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
215 {
216 error!("Failed to deliver StorageManager persist error: {error}");
217 }
218
219 promise
221 }
222
223 fn Estimate(&self, cx: &mut CurrentRealm) -> RootedPromise {
225 let promise = Promise::new_in_realm_rooted(cx);
227 let global = self.global();
229
230 if self.origin_cannot_obtain_local_storage_shelf() {
234 promise.reject_error(
235 cx,
236 Error::Type(c"Storage is unavailable for opaque origins".to_owned()),
237 );
238 return promise;
239 }
240
241 let mut handler = StorageManagerEstimateResponseHandler::new(
250 TrustedPromise::from(&promise),
251 global.task_manager().storage_task_source().to_sendable(),
252 );
253 let callback = GenericCallback::new(move |message| {
254 handler.handle(message.unwrap_or_else(|error| Err(error.to_string())));
255 })
256 .expect("Could not create StorageManager estimate callback");
257
258 if global
259 .storage_threads()
260 .estimate(global.origin().immutable().clone(), callback.clone())
261 .is_err() &&
262 let Err(error) = callback.send(Err("Failed to queue storage task".to_owned()))
263 {
264 error!("Failed to deliver StorageManager estimate error: {error}");
265 }
266
267 promise
269 }
270}