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 script_bindings::reflector::{DomObject, reflect_dom_object_with_cx};
15use serde::{Deserialize, Serialize};
16use servo_base::generic_channel::GenericSend;
17use storage_traits::indexeddb::{
18 AsyncOperation, AsyncReadOnlyOperation, BackendError, BackendResult, IndexedDBKeyType,
19 IndexedDBRecord, IndexedDBThreadMsg, IndexedDBTxnMode, PutItemResult, SyncOperation,
20};
21use stylo_atoms::Atom;
22
23use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
24 IDBRequestMethods, IDBRequestReadyState,
25};
26use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
27use crate::dom::bindings::error::{Error, Fallible, create_dom_exception};
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::refcounted::Trusted;
30use crate::dom::bindings::reflector::DomGlobal;
31use crate::dom::bindings::root::{DomRoot, MutNullableDom};
32use crate::dom::bindings::structuredclone;
33use crate::dom::domexception::DOMException;
34use crate::dom::event::{Event, EventBubbles, EventCancelable};
35use crate::dom::eventtarget::EventTarget;
36use crate::dom::globalscope::GlobalScope;
37use crate::dom::indexeddb::idbcursor::{IterationParam, iterate_cursor};
38use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
39use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
40use crate::dom::indexeddb::idbtransaction::IDBTransaction;
41use crate::indexeddb::key_type_to_jsval;
42use crate::realms::enter_auto_realm;
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(cx: &mut JSContext, 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: String::from(transaction.get_db_name()),
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(cx);
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(cx, &global, data, answer.handle_mut())
182 });
183 if let Err(e) = result {
184 warn!("Error reading structuredclone data");
185 Self::handle_async_request_error(&global, cx, request, e, self.request_id);
186 return;
187 };
188 },
189 IdbResult::Values(serialized_values) => {
190 rooted!(&in(cx) let mut values = vec![JSVal::default(); serialized_values.len()]);
191 for (i, serialized_data) in serialized_values.into_iter().enumerate() {
192 let result = postcard::from_bytes(&serialized_data)
193 .map_err(|_| Error::Data(None))
194 .and_then(|data| {
195 structuredclone::read(cx, &global, data, values.handle_mut_at(i))
196 });
197 if let Err(e) = result {
198 warn!("Error reading structuredclone data");
199 Self::handle_async_request_error(
200 &global,
201 cx,
202 request,
203 e,
204 self.request_id,
205 );
206 return;
207 };
208 }
209 values.safe_to_jsval(cx, answer.handle_mut());
210 },
211 IdbResult::Count(count) => {
212 answer.handle_mut().set(DoubleValue(count as f64));
213 },
214 IdbResult::Iterate(records) => {
215 let param = self.iteration_param.as_ref().expect(
216 "iteration_param must be provided by IDBRequest::execute_async for Iterate",
217 );
218 let cursor = match iterate_cursor(&global, cx, param, records) {
219 Ok(cursor) => cursor,
220 Err(e) => {
221 warn!("Error reading structuredclone data");
222 Self::handle_async_request_error(
223 &global,
224 cx,
225 request,
226 e,
227 self.request_id,
228 );
229 return;
230 },
231 };
232 if let Some(cursor) = cursor {
233 match cursor.downcast::<IDBCursorWithValue>() {
234 Some(cursor_with_value) => {
235 answer.handle_mut().set(ObjectValue(
236 *cursor_with_value.reflector().get_jsobject(),
237 ));
238 },
239 None => {
240 answer
241 .handle_mut()
242 .set(ObjectValue(*cursor.reflector().get_jsobject()));
243 },
244 }
245 }
246 },
247 IdbResult::None => {
248 },
250 IdbResult::Error(error) => {
251 Self::handle_async_request_error(&global, cx, request, error, self.request_id);
253 return;
254 },
255 }
256
257 request.set_result(answer.handle());
259
260 request.set_error(cx, None);
262
263 let event = Event::new(
268 cx,
269 &global,
270 Atom::from("success"),
271 EventBubbles::DoesNotBubble,
272 EventCancelable::NotCancelable,
273 );
274
275 let did_listeners_throw = Cell::new(false);
277 if transaction.is_inactive() {
279 transaction.set_active_flag(true);
280 }
281 event
283 .upcast::<Event>()
284 .fire_with_legacy_output_did_listeners_throw(
285 cx,
286 request.upcast(),
287 &did_listeners_throw,
288 );
289 if transaction.is_active() {
291 transaction.set_active_flag(false);
293 if did_listeners_throw.get() {
296 transaction.initiate_abort(cx, Error::Abort(None));
297 transaction.request_backend_abort();
298 }
299 }
300 transaction.request_finished();
301
302 Self::send_request_handled(cx, &transaction, self.request_id);
303 } else {
304 Self::handle_async_request_error(
307 &global,
308 cx,
309 request,
310 Error::Data(None),
311 self.request_id,
312 );
313 }
314 }
315
316 fn handle_async_request_error(
319 global: &GlobalScope,
320 cx: &mut JSContext,
321 request: DomRoot<IDBRequest>,
322 error: Error,
323 request_id: u64,
324 ) {
325 let transaction = request
326 .transaction
327 .get()
328 .expect("Request has no transaction");
329 rooted!(&in(cx) let undefined = UndefinedValue());
331 request.set_result(undefined.handle());
332
333 request.set_error(cx, Some(error.clone()));
335
336 let event = Event::new(
341 cx,
342 global,
343 Atom::from("error"),
344 EventBubbles::Bubbles,
345 EventCancelable::Cancelable,
346 );
347
348 if transaction.is_committing() {
351 transaction.initiate_abort(cx, error.clone());
352 transaction.request_backend_abort();
353 }
354 let did_listeners_throw = Cell::new(false);
356 if transaction.is_inactive() {
358 transaction.set_active_flag(true);
359 }
360 let default_not_prevented = event
362 .upcast::<Event>()
363 .fire_with_legacy_output_did_listeners_throw(
364 cx,
365 request.upcast(),
366 &did_listeners_throw,
367 );
368 if transaction.is_active() {
370 transaction.set_active_flag(false);
372 if did_listeners_throw.get() {
379 transaction.initiate_abort(cx, Error::Abort(None));
380 transaction.request_backend_abort();
381 } else if default_not_prevented {
382 transaction.initiate_abort(cx, error);
385 transaction.request_backend_abort();
386 }
387 }
388 transaction.request_finished();
389 Self::send_request_handled(cx, &transaction, request_id);
390 }
391}
392
393#[dom_struct]
394pub struct IDBRequest {
395 eventtarget: EventTarget,
396 #[ignore_malloc_size_of = "mozjs"]
397 result: Heap<JSVal>,
398 error: MutNullableDom<DOMException>,
399 source: MutNullableDom<IDBObjectStore>,
400 transaction: MutNullableDom<IDBTransaction>,
401 ready_state: Cell<IDBRequestReadyState>,
402}
403
404impl IDBRequest {
405 pub fn new_inherited() -> IDBRequest {
406 IDBRequest {
407 eventtarget: EventTarget::new_inherited(),
408
409 result: Heap::default(),
410 error: Default::default(),
411 source: Default::default(),
412 transaction: Default::default(),
413 ready_state: Cell::new(IDBRequestReadyState::Pending),
414 }
415 }
416
417 pub fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<IDBRequest> {
418 reflect_dom_object_with_cx(Box::new(IDBRequest::new_inherited()), global, cx)
419 }
420
421 pub fn set_source(&self, source: Option<&IDBObjectStore>) {
422 self.source.set(source);
423 }
424
425 pub fn set_ready_state_done(&self) {
426 self.ready_state.set(IDBRequestReadyState::Done);
427 }
428
429 pub fn set_result(&self, result: HandleValue) {
430 self.result.set(result.get());
431 }
432
433 pub fn set_error(&self, cx: &mut JSContext, error: Option<Error>) {
434 if let Some(error) = error {
435 if let Ok(exception) = create_dom_exception(cx, &self.global(), error) {
436 self.error.set(Some(&exception));
437 }
438 } else {
439 self.error.set(None);
440 }
441 }
442
443 pub fn set_transaction(&self, transaction: &IDBTransaction) {
444 self.transaction.set(Some(transaction));
445 }
446
447 pub fn clear_transaction(&self) {
448 self.transaction.set(None);
449 }
450
451 fn is_done(&self) -> bool {
452 self.ready_state.get() == IDBRequestReadyState::Done
453 }
454
455 pub(crate) fn transaction(&self) -> Option<DomRoot<IDBTransaction>> {
456 self.transaction.get()
457 }
458
459 pub fn execute_async<T, F>(
461 cx: &mut JSContext,
462 source: &IDBObjectStore,
463 operation_fn: F,
464 request: Option<DomRoot<IDBRequest>>,
465 iteration_param: Option<IterationParam>,
466 ) -> Fallible<DomRoot<IDBRequest>>
467 where
468 T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
469 F: FnOnce(GenericCallback<BackendResult<T>>) -> AsyncOperation,
470 {
471 let transaction = source.transaction();
473 let global = transaction.global();
474 if !transaction.is_active() || !transaction.is_usable() {
476 return Err(Error::TransactionInactive(None));
477 }
478
479 let request_id = transaction.allocate_request_id();
480
481 let request = request.unwrap_or_else(|| {
483 let new_request = IDBRequest::new(cx, &global);
484 new_request.set_source(Some(source));
485 new_request.set_transaction(&transaction);
486 new_request
487 });
488
489 transaction.add_request(&request);
491
492 let transaction_mode = match transaction.get_mode() {
495 IDBTransactionMode::Readonly => IndexedDBTxnMode::Readonly,
496 IDBTransactionMode::Readwrite => IndexedDBTxnMode::Readwrite,
497 IDBTransactionMode::Versionchange => IndexedDBTxnMode::Versionchange,
498 };
499
500 let response_listener = RequestListener {
501 request: Trusted::new(&request),
502 iteration_param: iteration_param.clone(),
503 request_id,
504 };
505
506 let task_source = global
507 .task_manager()
508 .database_access_task_source()
509 .to_sendable();
510
511 let closure = move |message: Result<BackendResult<T>, ipc_channel::IpcError>| {
512 let response_listener = response_listener.clone();
513 task_source.queue(task!(request_callback: move |cx| {
514 response_listener.handle_async_request_finished(
515 cx,
516 message.expect("Could not unwrap message").inspect_err(|e| {
517 if let BackendError::DbErr(e) = e {
518 error!("Error in IndexedDB operation: {}", e);
519 }
520 }).map(|t| t.into()),
521 );
522 }));
523 };
524 let callback = GenericCallback::new(global.time_profiler_chan().clone(), closure)
525 .expect("Could not create callback");
526 let operation = operation_fn(callback);
527
528 if matches!(
529 operation,
530 AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate { .. })
531 ) {
532 assert!(
533 iteration_param.is_some(),
534 "iteration_param must be provided for Iterate"
535 );
536 } else {
537 assert!(
538 iteration_param.is_none(),
539 "iteration_param should not be provided for operation other than Iterate"
540 );
541 }
542
543 transaction
546 .global()
547 .storage_threads()
548 .send(IndexedDBThreadMsg::Async(
549 global.origin().immutable().clone(),
550 String::from(transaction.get_db_name()),
551 String::from(source.get_name()),
552 transaction.get_serial_number(),
553 request_id,
554 transaction_mode,
555 operation,
556 ))
557 .unwrap();
558
559 Ok(request)
561 }
562}
563
564impl IDBRequestMethods<crate::DomTypeHolder> for IDBRequest {
565 fn GetResult(
567 &self,
568 _cx: &mut JSContext,
569 mut val: js::rust::MutableHandle<'_, js::jsapi::Value>,
570 ) -> Fallible<()> {
571 if !self.is_done() {
573 return Err(Error::InvalidState(Some(
574 "Cannot get result on a request that is still pending.".into(),
575 )));
576 }
577
578 val.set(self.result.get());
580 Ok(())
581 }
582
583 fn GetError(&self) -> Fallible<Option<DomRoot<DOMException>>> {
585 if !self.is_done() {
587 return Err(Error::InvalidState(Some(
588 "Cannot get error on a request that is still pending.".into(),
589 )));
590 }
591
592 Ok(self.error.get())
594 }
595
596 fn GetSource(&self) -> Option<DomRoot<IDBObjectStore>> {
598 self.source.get()
599 }
600
601 fn GetTransaction(&self) -> Option<DomRoot<IDBTransaction>> {
603 self.transaction.get()
604 }
605
606 fn ReadyState(&self) -> IDBRequestReadyState {
608 self.ready_state.get()
609 }
610
611 event_handler!(success, GetOnsuccess, SetOnsuccess);
613
614 event_handler!(error, GetOnerror, SetOnerror);
616}