1use std::cell::Cell;
6
7use base::generic_channel::GenericSend;
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::conversions::ToJSValConvertible;
11use js::jsapi::Heap;
12use js::jsval::{DoubleValue, JSVal, ObjectValue, UndefinedValue};
13use js::rust::HandleValue;
14use profile_traits::generic_callback::GenericCallback;
15use serde::{Deserialize, Serialize};
16use storage_traits::indexeddb::{
17 AsyncOperation, AsyncReadOnlyOperation, BackendError, BackendResult, IndexedDBKeyType,
18 IndexedDBRecord, IndexedDBThreadMsg, IndexedDBTxnMode, PutItemResult,
19};
20use stylo_atoms::Atom;
21
22use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
23 IDBRequestMethods, IDBRequestReadyState,
24};
25use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
26use crate::dom::bindings::error::{Error, Fallible, create_dom_exception};
27use crate::dom::bindings::inheritance::Castable;
28use crate::dom::bindings::refcounted::Trusted;
29use crate::dom::bindings::reflector::{DomGlobal, DomObject, reflect_dom_object};
30use crate::dom::bindings::root::{DomRoot, MutNullableDom};
31use crate::dom::bindings::structuredclone;
32use crate::dom::domexception::DOMException;
33use crate::dom::event::{Event, EventBubbles, EventCancelable};
34use crate::dom::eventtarget::EventTarget;
35use crate::dom::globalscope::GlobalScope;
36use crate::dom::indexeddb::idbcursor::{IterationParam, iterate_cursor};
37use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
38use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
39use crate::dom::indexeddb::idbtransaction::IDBTransaction;
40use crate::indexeddb::key_type_to_jsval;
41use crate::realms::enter_auto_realm;
42use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
43
44#[derive(Clone)]
45struct RequestListener {
46 request: Trusted<IDBRequest>,
47 iteration_param: Option<IterationParam>,
48}
49
50pub enum IdbResult {
51 Key(IndexedDBKeyType),
52 Keys(Vec<IndexedDBKeyType>),
53 Value(Vec<u8>),
54 Values(Vec<Vec<u8>>),
55 Count(u64),
56 Iterate(Vec<IndexedDBRecord>),
57 Error(Error),
58 None,
59}
60
61impl From<IndexedDBKeyType> for IdbResult {
62 fn from(value: IndexedDBKeyType) -> Self {
63 IdbResult::Key(value)
64 }
65}
66
67impl From<Vec<IndexedDBKeyType>> for IdbResult {
68 fn from(value: Vec<IndexedDBKeyType>) -> Self {
69 IdbResult::Keys(value)
70 }
71}
72
73impl From<Vec<u8>> for IdbResult {
74 fn from(value: Vec<u8>) -> Self {
75 IdbResult::Value(value)
76 }
77}
78
79impl From<Vec<Vec<u8>>> for IdbResult {
80 fn from(value: Vec<Vec<u8>>) -> Self {
81 IdbResult::Values(value)
82 }
83}
84
85impl From<PutItemResult> for IdbResult {
86 fn from(value: PutItemResult) -> Self {
87 match value {
88 PutItemResult::Success => Self::None,
89 PutItemResult::CannotOverwrite => Self::Error(Error::Constraint(None)),
90 }
91 }
92}
93
94impl From<Vec<IndexedDBRecord>> for IdbResult {
95 fn from(value: Vec<IndexedDBRecord>) -> Self {
96 Self::Iterate(value)
97 }
98}
99
100impl From<()> for IdbResult {
101 fn from(_value: ()) -> Self {
102 Self::None
103 }
104}
105
106impl<T> From<Option<T>> for IdbResult
107where
108 T: Into<IdbResult>,
109{
110 fn from(value: Option<T>) -> Self {
111 match value {
112 Some(value) => value.into(),
113 None => IdbResult::None,
114 }
115 }
116}
117
118impl From<u64> for IdbResult {
119 fn from(value: u64) -> Self {
120 IdbResult::Count(value)
121 }
122}
123
124impl RequestListener {
125 fn handle_async_request_finished(&self, cx: &mut JSContext, result: BackendResult<IdbResult>) {
128 let request = self.request.root();
129 let global = request.global();
130
131 request.set_ready_state_done();
133
134 let mut realm = enter_auto_realm(cx, &*request);
135 let cx: &mut JSContext = &mut realm;
136 rooted!(&in(cx) let mut answer = UndefinedValue());
137
138 if let Ok(data) = result {
139 match data {
140 IdbResult::Key(key) => key_type_to_jsval(cx, &key, answer.handle_mut()),
141 IdbResult::Keys(keys) => {
142 rooted!(&in(cx) let mut array = vec![JSVal::default(); keys.len()]);
143 for (i, key) in keys.into_iter().enumerate() {
144 key_type_to_jsval(cx, &key, array.handle_mut_at(i));
145 }
146 array.safe_to_jsval(cx, answer.handle_mut());
147 },
148 IdbResult::Value(serialized_data) => {
149 let result = postcard::from_bytes(&serialized_data)
150 .map_err(|_| Error::Data(None))
151 .and_then(|data| {
152 structuredclone::read(
153 &global,
154 data,
155 answer.handle_mut(),
156 CanGc::from_cx(cx),
157 )
158 });
159 if let Err(e) = result {
160 warn!("Error reading structuredclone data");
161 Self::handle_async_request_error(&global, cx, request, e);
162 return;
163 };
164 },
165 IdbResult::Values(serialized_values) => {
166 rooted!(&in(cx) let mut values = vec![JSVal::default(); serialized_values.len()]);
167 for (i, serialized_data) in serialized_values.into_iter().enumerate() {
168 let result = postcard::from_bytes(&serialized_data)
169 .map_err(|_| Error::Data(None))
170 .and_then(|data| {
171 structuredclone::read(
172 &global,
173 data,
174 values.handle_mut_at(i),
175 CanGc::from_cx(cx),
176 )
177 });
178 if let Err(e) = result {
179 warn!("Error reading structuredclone data");
180 Self::handle_async_request_error(&global, cx, request, e);
181 return;
182 };
183 }
184 values.safe_to_jsval(cx, answer.handle_mut());
185 },
186 IdbResult::Count(count) => {
187 answer.handle_mut().set(DoubleValue(count as f64));
188 },
189 IdbResult::Iterate(records) => {
190 let param = self.iteration_param.as_ref().expect(
191 "iteration_param must be provided by IDBRequest::execute_async for Iterate",
192 );
193 let cursor = match iterate_cursor(&global, cx, param, records) {
194 Ok(cursor) => cursor,
195 Err(e) => {
196 warn!("Error reading structuredclone data");
197 Self::handle_async_request_error(&global, cx, request, e);
198 return;
199 },
200 };
201 if let Some(cursor) = cursor {
202 match cursor.downcast::<IDBCursorWithValue>() {
203 Some(cursor_with_value) => {
204 answer.handle_mut().set(ObjectValue(
205 *cursor_with_value.reflector().get_jsobject(),
206 ));
207 },
208 None => {
209 answer
210 .handle_mut()
211 .set(ObjectValue(*cursor.reflector().get_jsobject()));
212 },
213 }
214 }
215 },
216 IdbResult::None => {
217 },
219 IdbResult::Error(error) => {
220 Self::handle_async_request_error(&global, cx, request, error);
222 return;
223 },
224 }
225
226 request.set_result(answer.handle());
228
229 request.set_error(None, CanGc::from_cx(cx));
231
232 let transaction = request
235 .transaction
236 .get()
237 .expect("Request unexpectedly has no transaction");
238
239 let event = Event::new(
240 &global,
241 Atom::from("success"),
242 EventBubbles::DoesNotBubble,
243 EventCancelable::NotCancelable,
244 CanGc::from_cx(cx),
245 );
246
247 transaction.set_active_flag(true);
248 event
249 .upcast::<Event>()
250 .fire(request.upcast(), CanGc::from_cx(cx));
251 transaction.set_active_flag(false);
252 transaction.request_finished();
254 } else {
255 Self::handle_async_request_error(&global, cx, request, Error::Data(None));
258 }
259 }
260
261 fn handle_async_request_error(
264 global: &GlobalScope,
265 cx: &mut JSContext,
266 request: DomRoot<IDBRequest>,
267 error: Error,
268 ) {
269 rooted!(&in(cx) let undefined = UndefinedValue());
271 request.set_result(undefined.handle());
272
273 request.set_error(Some(error), CanGc::from_cx(cx));
275
276 let transaction = request
279 .transaction
280 .get()
281 .expect("Request has no transaction");
282
283 let event = Event::new(
284 global,
285 Atom::from("error"),
286 EventBubbles::Bubbles,
287 EventCancelable::Cancelable,
288 CanGc::from_cx(cx),
289 );
290
291 transaction.set_active_flag(true);
293 event
294 .upcast::<Event>()
295 .fire(request.upcast(), CanGc::from_cx(cx));
296 transaction.set_active_flag(false);
297 transaction.request_finished();
299 }
300}
301
302#[dom_struct]
303pub struct IDBRequest {
304 eventtarget: EventTarget,
305 #[ignore_malloc_size_of = "mozjs"]
306 result: Heap<JSVal>,
307 error: MutNullableDom<DOMException>,
308 source: MutNullableDom<IDBObjectStore>,
309 transaction: MutNullableDom<IDBTransaction>,
310 ready_state: Cell<IDBRequestReadyState>,
311}
312
313impl IDBRequest {
314 pub fn new_inherited() -> IDBRequest {
315 IDBRequest {
316 eventtarget: EventTarget::new_inherited(),
317
318 result: Heap::default(),
319 error: Default::default(),
320 source: Default::default(),
321 transaction: Default::default(),
322 ready_state: Cell::new(IDBRequestReadyState::Pending),
323 }
324 }
325
326 pub fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<IDBRequest> {
327 reflect_dom_object(Box::new(IDBRequest::new_inherited()), global, can_gc)
328 }
329
330 pub fn set_source(&self, source: Option<&IDBObjectStore>) {
331 self.source.set(source);
332 }
333
334 pub fn set_ready_state_done(&self) {
335 self.ready_state.set(IDBRequestReadyState::Done);
336 }
337
338 pub fn set_result(&self, result: HandleValue) {
339 self.result.set(result.get());
340 }
341
342 pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
343 if let Some(error) = error {
344 if let Ok(exception) = create_dom_exception(&self.global(), error, can_gc) {
345 self.error.set(Some(&exception));
346 }
347 } else {
348 self.error.set(None);
349 }
350 }
351
352 pub fn set_transaction(&self, transaction: &IDBTransaction) {
353 self.transaction.set(Some(transaction));
354 }
355
356 pub fn execute_async<T, F>(
358 source: &IDBObjectStore,
359 operation_fn: F,
360 request: Option<DomRoot<IDBRequest>>,
361 iteration_param: Option<IterationParam>,
362 can_gc: CanGc,
363 ) -> Fallible<DomRoot<IDBRequest>>
364 where
365 T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
366 F: FnOnce(GenericCallback<BackendResult<T>>) -> AsyncOperation,
367 {
368 let transaction = source.transaction();
370 let global = transaction.global();
371
372 if !transaction.is_active() {
374 return Err(Error::TransactionInactive(None));
375 }
376
377 let request = request.unwrap_or_else(|| {
379 let new_request = IDBRequest::new(&global, can_gc);
380 new_request.set_source(Some(source));
381 new_request.set_transaction(&transaction);
382 new_request
383 });
384
385 transaction.add_request(&request);
387
388 let transaction_mode = match transaction.get_mode() {
391 IDBTransactionMode::Readonly => IndexedDBTxnMode::Readonly,
392 IDBTransactionMode::Readwrite => IndexedDBTxnMode::Readwrite,
393 IDBTransactionMode::Versionchange => IndexedDBTxnMode::Versionchange,
394 };
395
396 let response_listener = RequestListener {
397 request: Trusted::new(&request),
398 iteration_param: iteration_param.clone(),
399 };
400
401 let task_source = global
402 .task_manager()
403 .database_access_task_source()
404 .to_sendable();
405
406 let closure = move |message: Result<BackendResult<T>, ipc_channel::Error>| {
407 let response_listener = response_listener.clone();
408 task_source.queue(task!(request_callback: move |cx| {
409 response_listener.handle_async_request_finished(
410 cx,
411 message.expect("Could not unwrap message").inspect_err(|e| {
412 if let BackendError::DbErr(e) = e {
413 error!("Error in IndexedDB operation: {}", e);
414 }
415 }).map(|t| t.into()));
416 }));
417 };
418 let callback = GenericCallback::new(global.time_profiler_chan().clone(), closure)
419 .expect("Could not create callback");
420 let operation = operation_fn(callback);
421
422 if matches!(
423 operation,
424 AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate { .. })
425 ) {
426 assert!(
427 iteration_param.is_some(),
428 "iteration_param must be provided for Iterate"
429 );
430 } else {
431 assert!(
432 iteration_param.is_none(),
433 "iteration_param should not be provided for operation other than Iterate"
434 );
435 }
436
437 transaction
438 .global()
439 .storage_threads()
440 .send(IndexedDBThreadMsg::Async(
441 global.origin().immutable().clone(),
442 transaction.get_db_name().to_string(),
443 source.get_name().to_string(),
444 transaction.get_serial_number(),
445 transaction_mode,
446 operation,
447 ))
448 .unwrap();
449
450 Ok(request)
452 }
453}
454
455impl IDBRequestMethods<crate::DomTypeHolder> for IDBRequest {
456 fn Result(&self, _cx: SafeJSContext, mut val: js::rust::MutableHandle<'_, js::jsapi::Value>) {
458 val.set(self.result.get());
459 }
460
461 fn GetError(&self) -> Option<DomRoot<DOMException>> {
463 self.error.get()
464 }
465
466 fn GetSource(&self) -> Option<DomRoot<IDBObjectStore>> {
468 self.source.get()
469 }
470
471 fn GetTransaction(&self) -> Option<DomRoot<IDBTransaction>> {
473 self.transaction.get()
474 }
475
476 fn ReadyState(&self) -> IDBRequestReadyState {
478 self.ready_state.get()
479 }
480
481 event_handler!(success, GetOnsuccess, SetOnsuccess);
483
484 event_handler!(error, GetOnerror, SetOnerror);
486}