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