1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::conversions::ToJSValConvertible;
10use js::jsapi::Heap;
11use js::jsval::{DoubleValue, JSVal, ObjectValue, UndefinedValue};
12use js::rust::HandleValue;
13use profile_traits::generic_callback::GenericCallback;
14use serde::{Deserialize, Serialize};
15use servo_base::generic_channel::GenericSend;
16use storage_traits::indexeddb::{
17 AsyncOperation, AsyncReadOnlyOperation, BackendError, BackendResult, IndexedDBKeyType,
18 IndexedDBRecord, IndexedDBThreadMsg, IndexedDBTxnMode, PutItemResult, SyncOperation,
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 request_id: u64,
49}
50
51pub enum IdbResult {
52 Key(IndexedDBKeyType),
53 Keys(Vec<IndexedDBKeyType>),
54 Value(Vec<u8>),
55 Values(Vec<Vec<u8>>),
56 Count(u64),
57 Iterate(Vec<IndexedDBRecord>),
58 Error(Error),
59 None,
60}
61
62impl From<IndexedDBKeyType> for IdbResult {
63 fn from(value: IndexedDBKeyType) -> Self {
64 IdbResult::Key(value)
65 }
66}
67
68impl From<Vec<IndexedDBKeyType>> for IdbResult {
69 fn from(value: Vec<IndexedDBKeyType>) -> Self {
70 IdbResult::Keys(value)
71 }
72}
73
74impl From<Vec<u8>> for IdbResult {
75 fn from(value: Vec<u8>) -> Self {
76 IdbResult::Value(value)
77 }
78}
79
80impl From<Vec<Vec<u8>>> for IdbResult {
81 fn from(value: Vec<Vec<u8>>) -> Self {
82 IdbResult::Values(value)
83 }
84}
85
86impl From<PutItemResult> for IdbResult {
87 fn from(value: PutItemResult) -> Self {
88 match value {
89 PutItemResult::Key(key) => Self::Key(key),
90 PutItemResult::CannotOverwrite => Self::Error(Error::Constraint(None)),
91 }
92 }
93}
94
95impl From<Vec<IndexedDBRecord>> for IdbResult {
96 fn from(value: Vec<IndexedDBRecord>) -> Self {
97 Self::Iterate(value)
98 }
99}
100
101impl From<()> for IdbResult {
102 fn from(_value: ()) -> Self {
103 Self::None
104 }
105}
106
107impl<T> From<Option<T>> for IdbResult
108where
109 T: Into<IdbResult>,
110{
111 fn from(value: Option<T>) -> Self {
112 match value {
113 Some(value) => value.into(),
114 None => IdbResult::None,
115 }
116 }
117}
118
119impl From<u64> for IdbResult {
120 fn from(value: u64) -> Self {
121 IdbResult::Count(value)
122 }
123}
124
125impl RequestListener {
126 fn send_request_handled(transaction: &IDBTransaction, request_id: u64) {
127 let global = transaction.global();
128 let send_result = global.storage_threads().send(IndexedDBThreadMsg::Sync(
133 SyncOperation::RequestHandled {
134 origin: global.origin().immutable().clone(),
135 db_name: transaction.get_db_name().to_string(),
136 txn: transaction.get_serial_number(),
137 request_id,
138 },
139 ));
140 if send_result.is_err() {
141 error!("Failed to send SyncOperation::RequestHandled");
142 }
143 transaction.mark_request_handled(request_id);
144
145 transaction.maybe_commit();
148 }
149
150 fn handle_async_request_finished(&self, cx: &mut JSContext, result: BackendResult<IdbResult>) {
153 let request = self.request.root();
154 let global = request.global();
155
156 let transaction = request
157 .transaction
158 .get()
159 .expect("Request unexpectedly has no transaction");
160 request.set_ready_state_done();
162
163 let mut realm = enter_auto_realm(cx, &*request);
164 let cx: &mut JSContext = &mut realm;
165 rooted!(&in(cx) let mut answer = UndefinedValue());
166
167 if let Ok(data) = result {
168 match data {
169 IdbResult::Key(key) => key_type_to_jsval(cx, &key, answer.handle_mut()),
170 IdbResult::Keys(keys) => {
171 rooted!(&in(cx) let mut array = vec![JSVal::default(); keys.len()]);
172 for (i, key) in keys.into_iter().enumerate() {
173 key_type_to_jsval(cx, &key, array.handle_mut_at(i));
174 }
175 array.safe_to_jsval(cx, answer.handle_mut());
176 },
177 IdbResult::Value(serialized_data) => {
178 let result = postcard::from_bytes(&serialized_data)
179 .map_err(|_| Error::Data(None))
180 .and_then(|data| {
181 structuredclone::read(
182 &global,
183 data,
184 answer.handle_mut(),
185 CanGc::from_cx(cx),
186 )
187 });
188 if let Err(e) = result {
189 warn!("Error reading structuredclone data");
190 Self::handle_async_request_error(&global, cx, request, e, self.request_id);
191 return;
192 };
193 },
194 IdbResult::Values(serialized_values) => {
195 rooted!(&in(cx) let mut values = vec![JSVal::default(); serialized_values.len()]);
196 for (i, serialized_data) in serialized_values.into_iter().enumerate() {
197 let result = postcard::from_bytes(&serialized_data)
198 .map_err(|_| Error::Data(None))
199 .and_then(|data| {
200 structuredclone::read(
201 &global,
202 data,
203 values.handle_mut_at(i),
204 CanGc::from_cx(cx),
205 )
206 });
207 if let Err(e) = result {
208 warn!("Error reading structuredclone data");
209 Self::handle_async_request_error(
210 &global,
211 cx,
212 request,
213 e,
214 self.request_id,
215 );
216 return;
217 };
218 }
219 values.safe_to_jsval(cx, answer.handle_mut());
220 },
221 IdbResult::Count(count) => {
222 answer.handle_mut().set(DoubleValue(count as f64));
223 },
224 IdbResult::Iterate(records) => {
225 let param = self.iteration_param.as_ref().expect(
226 "iteration_param must be provided by IDBRequest::execute_async for Iterate",
227 );
228 let cursor = match iterate_cursor(&global, cx, param, records) {
229 Ok(cursor) => cursor,
230 Err(e) => {
231 warn!("Error reading structuredclone data");
232 Self::handle_async_request_error(
233 &global,
234 cx,
235 request,
236 e,
237 self.request_id,
238 );
239 return;
240 },
241 };
242 if let Some(cursor) = cursor {
243 match cursor.downcast::<IDBCursorWithValue>() {
244 Some(cursor_with_value) => {
245 answer.handle_mut().set(ObjectValue(
246 *cursor_with_value.reflector().get_jsobject(),
247 ));
248 },
249 None => {
250 answer
251 .handle_mut()
252 .set(ObjectValue(*cursor.reflector().get_jsobject()));
253 },
254 }
255 }
256 },
257 IdbResult::None => {
258 },
260 IdbResult::Error(error) => {
261 Self::handle_async_request_error(&global, cx, request, error, self.request_id);
263 return;
264 },
265 }
266
267 request.set_result(answer.handle());
269
270 request.set_error(None, CanGc::from_cx(cx));
272
273 let event = Event::new(
278 &global,
279 Atom::from("success"),
280 EventBubbles::DoesNotBubble,
281 EventCancelable::NotCancelable,
282 CanGc::from_cx(cx),
283 );
284
285 let did_listeners_throw = Cell::new(false);
287 if transaction.is_inactive() {
289 transaction.set_active_flag(true);
290 }
291 event
293 .upcast::<Event>()
294 .fire_with_legacy_output_did_listeners_throw(
295 request.upcast(),
296 &did_listeners_throw,
297 CanGc::from_cx(cx),
298 );
299 if transaction.is_active() {
301 transaction.set_active_flag(false);
303 if did_listeners_throw.get() {
306 transaction.initiate_abort(Error::Abort(None), CanGc::from_cx(cx));
307 transaction.request_backend_abort();
308 }
309 }
310 transaction.request_finished();
311
312 Self::send_request_handled(&transaction, self.request_id);
313 } else {
314 Self::handle_async_request_error(
317 &global,
318 cx,
319 request,
320 Error::Data(None),
321 self.request_id,
322 );
323 }
324 }
325
326 fn handle_async_request_error(
329 global: &GlobalScope,
330 cx: &mut JSContext,
331 request: DomRoot<IDBRequest>,
332 error: Error,
333 request_id: u64,
334 ) {
335 let transaction = request
336 .transaction
337 .get()
338 .expect("Request has no transaction");
339 rooted!(&in(cx) let undefined = UndefinedValue());
341 request.set_result(undefined.handle());
342
343 request.set_error(Some(error.clone()), CanGc::from_cx(cx));
345
346 let event = Event::new(
351 global,
352 Atom::from("error"),
353 EventBubbles::Bubbles,
354 EventCancelable::Cancelable,
355 CanGc::from_cx(cx),
356 );
357
358 if transaction.is_committing() {
361 transaction.initiate_abort(error.clone(), CanGc::from_cx(cx));
362 transaction.request_backend_abort();
363 }
364 let did_listeners_throw = Cell::new(false);
366 if transaction.is_inactive() {
368 transaction.set_active_flag(true);
369 }
370 let default_not_prevented = event
372 .upcast::<Event>()
373 .fire_with_legacy_output_did_listeners_throw(
374 request.upcast(),
375 &did_listeners_throw,
376 CanGc::from_cx(cx),
377 );
378 if transaction.is_active() {
380 transaction.set_active_flag(false);
382 if did_listeners_throw.get() {
389 transaction.initiate_abort(Error::Abort(None), CanGc::from_cx(cx));
390 transaction.request_backend_abort();
391 } else if default_not_prevented {
392 transaction.initiate_abort(error, CanGc::from_cx(cx));
395 transaction.request_backend_abort();
396 }
397 }
398 transaction.request_finished();
399 Self::send_request_handled(&transaction, request_id);
400 }
401}
402
403#[dom_struct]
404pub struct IDBRequest {
405 eventtarget: EventTarget,
406 #[ignore_malloc_size_of = "mozjs"]
407 result: Heap<JSVal>,
408 error: MutNullableDom<DOMException>,
409 source: MutNullableDom<IDBObjectStore>,
410 transaction: MutNullableDom<IDBTransaction>,
411 ready_state: Cell<IDBRequestReadyState>,
412}
413
414impl IDBRequest {
415 pub fn new_inherited() -> IDBRequest {
416 IDBRequest {
417 eventtarget: EventTarget::new_inherited(),
418
419 result: Heap::default(),
420 error: Default::default(),
421 source: Default::default(),
422 transaction: Default::default(),
423 ready_state: Cell::new(IDBRequestReadyState::Pending),
424 }
425 }
426
427 pub fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<IDBRequest> {
428 reflect_dom_object(Box::new(IDBRequest::new_inherited()), global, can_gc)
429 }
430
431 pub fn set_source(&self, source: Option<&IDBObjectStore>) {
432 self.source.set(source);
433 }
434
435 pub fn set_ready_state_done(&self) {
436 self.ready_state.set(IDBRequestReadyState::Done);
437 }
438
439 pub fn set_result(&self, result: HandleValue) {
440 self.result.set(result.get());
441 }
442
443 pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
444 if let Some(error) = error {
445 if let Ok(exception) = create_dom_exception(&self.global(), error, can_gc) {
446 self.error.set(Some(&exception));
447 }
448 } else {
449 self.error.set(None);
450 }
451 }
452
453 pub fn set_transaction(&self, transaction: &IDBTransaction) {
454 self.transaction.set(Some(transaction));
455 }
456
457 pub fn clear_transaction(&self) {
458 self.transaction.set(None);
459 }
460
461 fn is_done(&self) -> bool {
462 self.ready_state.get() == IDBRequestReadyState::Done
463 }
464
465 pub(crate) fn transaction(&self) -> Option<DomRoot<IDBTransaction>> {
466 self.transaction.get()
467 }
468
469 pub fn execute_async<T, F>(
471 source: &IDBObjectStore,
472 operation_fn: F,
473 request: Option<DomRoot<IDBRequest>>,
474 iteration_param: Option<IterationParam>,
475 can_gc: CanGc,
476 ) -> Fallible<DomRoot<IDBRequest>>
477 where
478 T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
479 F: FnOnce(GenericCallback<BackendResult<T>>) -> AsyncOperation,
480 {
481 let transaction = source.transaction();
483 let global = transaction.global();
484 if !transaction.is_active() || !transaction.is_usable() {
486 return Err(Error::TransactionInactive(None));
487 }
488
489 let request_id = transaction.allocate_request_id();
490
491 let request = request.unwrap_or_else(|| {
493 let new_request = IDBRequest::new(&global, can_gc);
494 new_request.set_source(Some(source));
495 new_request.set_transaction(&transaction);
496 new_request
497 });
498
499 transaction.add_request(&request);
501
502 let transaction_mode = match transaction.get_mode() {
505 IDBTransactionMode::Readonly => IndexedDBTxnMode::Readonly,
506 IDBTransactionMode::Readwrite => IndexedDBTxnMode::Readwrite,
507 IDBTransactionMode::Versionchange => IndexedDBTxnMode::Versionchange,
508 };
509
510 let response_listener = RequestListener {
511 request: Trusted::new(&request),
512 iteration_param: iteration_param.clone(),
513 request_id,
514 };
515
516 let task_source = global
517 .task_manager()
518 .database_access_task_source()
519 .to_sendable();
520
521 let closure = move |message: Result<BackendResult<T>, ipc_channel::IpcError>| {
522 let response_listener = response_listener.clone();
523 task_source.queue(task!(request_callback: move |cx| {
524 response_listener.handle_async_request_finished(
525 cx,
526 message.expect("Could not unwrap message").inspect_err(|e| {
527 if let BackendError::DbErr(e) = e {
528 error!("Error in IndexedDB operation: {}", e);
529 }
530 }).map(|t| t.into()),
531 );
532 }));
533 };
534 let callback = GenericCallback::new(global.time_profiler_chan().clone(), closure)
535 .expect("Could not create callback");
536 let operation = operation_fn(callback);
537
538 if matches!(
539 operation,
540 AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate { .. })
541 ) {
542 assert!(
543 iteration_param.is_some(),
544 "iteration_param must be provided for Iterate"
545 );
546 } else {
547 assert!(
548 iteration_param.is_none(),
549 "iteration_param should not be provided for operation other than Iterate"
550 );
551 }
552
553 transaction
556 .global()
557 .storage_threads()
558 .send(IndexedDBThreadMsg::Async(
559 global.origin().immutable().clone(),
560 transaction.get_db_name().to_string(),
561 source.get_name().to_string(),
562 transaction.get_serial_number(),
563 request_id,
564 transaction_mode,
565 operation,
566 ))
567 .unwrap();
568
569 Ok(request)
571 }
572}
573
574impl IDBRequestMethods<crate::DomTypeHolder> for IDBRequest {
575 fn GetResult(
577 &self,
578 _cx: SafeJSContext,
579 mut val: js::rust::MutableHandle<'_, js::jsapi::Value>,
580 ) -> Fallible<()> {
581 if !self.is_done() {
583 return Err(Error::InvalidState(Some(
584 "Cannot get result on a request that is still pending.".into(),
585 )));
586 }
587
588 val.set(self.result.get());
590 Ok(())
591 }
592
593 fn GetError(&self) -> Fallible<Option<DomRoot<DOMException>>> {
595 if !self.is_done() {
597 return Err(Error::InvalidState(Some(
598 "Cannot get error on a request that is still pending.".into(),
599 )));
600 }
601
602 Ok(self.error.get())
604 }
605
606 fn GetSource(&self) -> Option<DomRoot<IDBObjectStore>> {
608 self.source.get()
609 }
610
611 fn GetTransaction(&self) -> Option<DomRoot<IDBTransaction>> {
613 self.transaction.get()
614 }
615
616 fn ReadyState(&self) -> IDBRequestReadyState {
618 self.ready_state.get()
619 }
620
621 event_handler!(success, GetOnsuccess, SetOnsuccess);
623
624 event_handler!(error, GetOnerror, SetOnerror);
626}