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