1use std::cell::Cell;
6
7use base::IpcSend;
8use dom_struct::dom_struct;
9use ipc_channel::router::ROUTER;
10use js::jsapi::Heap;
11use js::jsval::{DoubleValue, JSVal, ObjectValue, UndefinedValue};
12use js::rust::HandleValue;
13use profile_traits::ipc::IpcReceiver;
14use script_bindings::conversions::SafeToJSValConvertible;
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_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, result: BackendResult<IdbResult>, can_gc: CanGc) {
128 let request = self.request.root();
129 let global = request.global();
130 let cx = GlobalScope::get_cx();
131
132 request.set_ready_state_done();
134
135 let _ac = enter_realm(&*request);
136 rooted!(in(*cx) let mut answer = UndefinedValue());
137
138 if let Ok(data) = result {
139 match data {
140 IdbResult::Key(key) => {
141 key_type_to_jsval(GlobalScope::get_cx(), &key, answer.handle_mut(), can_gc)
142 },
143 IdbResult::Keys(keys) => {
144 rooted_vec!(let mut array);
145 for key in keys.into_iter() {
146 rooted!(in(*cx) let mut val = UndefinedValue());
147 key_type_to_jsval(GlobalScope::get_cx(), &key, val.handle_mut(), can_gc);
148 array.push(Heap::boxed(val.get()));
149 }
150 array.safe_to_jsval(cx, answer.handle_mut(), can_gc);
151 },
152 IdbResult::Value(serialized_data) => {
153 let result = bincode::deserialize(&serialized_data)
154 .map_err(|_| Error::Data(None))
155 .and_then(|data| {
156 structuredclone::read(&global, data, answer.handle_mut(), can_gc)
157 });
158 if let Err(e) = result {
159 warn!("Error reading structuredclone data");
160 Self::handle_async_request_error(&global, cx, request, e);
161 return;
162 };
163 },
164 IdbResult::Values(serialized_values) => {
165 rooted_vec!(let mut values);
166 for serialized_data in serialized_values.into_iter() {
167 rooted!(in(*cx) let mut val = UndefinedValue());
168 let result = bincode::deserialize(&serialized_data)
169 .map_err(|_| Error::Data(None))
170 .and_then(|data| {
171 structuredclone::read(&global, data, val.handle_mut(), can_gc)
172 });
173 if let Err(e) = result {
174 warn!("Error reading structuredclone data");
175 Self::handle_async_request_error(&global, cx, request, e);
176 return;
177 };
178 values.push(Heap::boxed(val.get()));
179 }
180 values.safe_to_jsval(cx, answer.handle_mut(), can_gc);
181 },
182 IdbResult::Count(count) => {
183 answer.handle_mut().set(DoubleValue(count as f64));
184 },
185 IdbResult::Iterate(records) => {
186 let param = self.iteration_param.as_ref().expect(
187 "iteration_param must be provided by IDBRequest::execute_async for Iterate",
188 );
189 let cursor = match iterate_cursor(&global, cx, param, records, can_gc) {
190 Ok(cursor) => cursor,
191 Err(e) => {
192 warn!("Error reading structuredclone data");
193 Self::handle_async_request_error(&global, cx, request, e);
194 return;
195 },
196 };
197 if let Some(cursor) = cursor {
198 match cursor.downcast::<IDBCursorWithValue>() {
199 Some(cursor_with_value) => {
200 answer.handle_mut().set(ObjectValue(
201 *cursor_with_value.reflector().get_jsobject(),
202 ));
203 },
204 None => {
205 answer
206 .handle_mut()
207 .set(ObjectValue(*cursor.reflector().get_jsobject()));
208 },
209 }
210 }
211 },
212 IdbResult::None => {
213 },
215 IdbResult::Error(error) => {
216 Self::handle_async_request_error(&global, cx, request, error);
218 return;
219 },
220 }
221
222 request.set_result(answer.handle());
224
225 request.set_error(None, CanGc::note());
227
228 let transaction = request
231 .transaction
232 .get()
233 .expect("Request unexpectedly has no transaction");
234
235 let event = Event::new(
236 &global,
237 Atom::from("success"),
238 EventBubbles::DoesNotBubble,
239 EventCancelable::NotCancelable,
240 CanGc::note(),
241 );
242
243 transaction.set_active_flag(true);
244 event
245 .upcast::<Event>()
246 .fire(request.upcast(), CanGc::note());
247 transaction.set_active_flag(false);
248 transaction.request_finished();
250 } else {
251 Self::handle_async_request_error(&global, cx, request, Error::Data(None));
254 }
255 }
256
257 fn handle_async_request_error(
260 global: &GlobalScope,
261 cx: SafeJSContext,
262 request: DomRoot<IDBRequest>,
263 error: Error,
264 ) {
265 rooted!(in(*cx) let undefined = UndefinedValue());
267 request.set_result(undefined.handle());
268
269 request.set_error(Some(error), CanGc::note());
271
272 let transaction = request
275 .transaction
276 .get()
277 .expect("Request has no transaction");
278
279 let event = Event::new(
280 global,
281 Atom::from("error"),
282 EventBubbles::Bubbles,
283 EventCancelable::Cancelable,
284 CanGc::note(),
285 );
286
287 transaction.set_active_flag(true);
289 event
290 .upcast::<Event>()
291 .fire(request.upcast(), CanGc::note());
292 transaction.set_active_flag(false);
293 transaction.request_finished();
295 }
296}
297
298#[dom_struct]
299pub struct IDBRequest {
300 eventtarget: EventTarget,
301 #[ignore_malloc_size_of = "mozjs"]
302 result: Heap<JSVal>,
303 error: MutNullableDom<DOMException>,
304 source: MutNullableDom<IDBObjectStore>,
305 transaction: MutNullableDom<IDBTransaction>,
306 ready_state: Cell<IDBRequestReadyState>,
307}
308
309impl IDBRequest {
310 pub fn new_inherited() -> IDBRequest {
311 IDBRequest {
312 eventtarget: EventTarget::new_inherited(),
313
314 result: Heap::default(),
315 error: Default::default(),
316 source: Default::default(),
317 transaction: Default::default(),
318 ready_state: Cell::new(IDBRequestReadyState::Pending),
319 }
320 }
321
322 pub fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<IDBRequest> {
323 reflect_dom_object(Box::new(IDBRequest::new_inherited()), global, can_gc)
324 }
325
326 pub fn set_source(&self, source: Option<&IDBObjectStore>) {
327 self.source.set(source);
328 }
329
330 pub fn set_ready_state_done(&self) {
331 self.ready_state.set(IDBRequestReadyState::Done);
332 }
333
334 pub fn set_result(&self, result: HandleValue) {
335 self.result.set(result.get());
336 }
337
338 pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
339 if let Some(error) = error {
340 if let Ok(exception) = create_dom_exception(&self.global(), error, can_gc) {
341 self.error.set(Some(&exception));
342 }
343 } else {
344 self.error.set(None);
345 }
346 }
347
348 pub fn set_transaction(&self, transaction: &IDBTransaction) {
349 self.transaction.set(Some(transaction));
350 }
351
352 pub fn execute_async<T>(
354 source: &IDBObjectStore,
355 operation: AsyncOperation,
356 receiver: IpcReceiver<BackendResult<T>>,
357 request: Option<DomRoot<IDBRequest>>,
358 iteration_param: Option<IterationParam>,
359 can_gc: CanGc,
360 ) -> Fallible<DomRoot<IDBRequest>>
361 where
362 T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
363 {
364 let transaction = source.transaction();
366 let global = transaction.global();
367
368 if !transaction.is_active() {
370 return Err(Error::TransactionInactive(None));
371 }
372
373 let request = request.unwrap_or_else(|| {
375 let new_request = IDBRequest::new(&global, can_gc);
376 new_request.set_source(Some(source));
377 new_request.set_transaction(&transaction);
378 new_request
379 });
380
381 transaction.add_request(&request);
383
384 let transaction_mode = match transaction.get_mode() {
387 IDBTransactionMode::Readonly => IndexedDBTxnMode::Readonly,
388 IDBTransactionMode::Readwrite => IndexedDBTxnMode::Readwrite,
389 IDBTransactionMode::Versionchange => IndexedDBTxnMode::Versionchange,
390 };
391
392 if matches!(
393 operation,
394 AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate { .. })
395 ) {
396 assert!(
397 iteration_param.is_some(),
398 "iteration_param must be provided for Iterate"
399 );
400 } else {
401 assert!(
402 iteration_param.is_none(),
403 "iteration_param should not be provided for operation other than Iterate"
404 );
405 }
406
407 let response_listener = RequestListener {
408 request: Trusted::new(&request),
409 iteration_param,
410 };
411
412 let task_source = global
413 .task_manager()
414 .database_access_task_source()
415 .to_sendable();
416
417 ROUTER.add_typed_route(
418 receiver.to_ipc_receiver(),
419 Box::new(move |message| {
420 let response_listener = response_listener.clone();
421 task_source.queue(task!(request_callback: move || {
422 response_listener.handle_async_request_finished(
423 message.expect("Could not unwrap message").inspect_err(|e| {
424 if let BackendError::DbErr(e) = e {
425 error!("Error in IndexedDB operation: {}", e);
426 }
427 }).map(|t| t.into()), CanGc::note());
428 }));
429 }),
430 );
431
432 transaction
433 .global()
434 .storage_threads()
435 .send(IndexedDBThreadMsg::Async(
436 global.origin().immutable().clone(),
437 transaction.get_db_name().to_string(),
438 source.get_name().to_string(),
439 transaction.get_serial_number(),
440 transaction_mode,
441 operation,
442 ))
443 .unwrap();
444
445 Ok(request)
447 }
448}
449
450impl IDBRequestMethods<crate::DomTypeHolder> for IDBRequest {
451 fn Result(&self, _cx: SafeJSContext, mut val: js::rust::MutableHandle<'_, js::jsapi::Value>) {
453 val.set(self.result.get());
454 }
455
456 fn GetError(&self) -> Option<DomRoot<DOMException>> {
458 self.error.get()
459 }
460
461 fn GetSource(&self) -> Option<DomRoot<IDBObjectStore>> {
463 self.source.get()
464 }
465
466 fn GetTransaction(&self) -> Option<DomRoot<IDBTransaction>> {
468 self.transaction.get()
469 }
470
471 fn ReadyState(&self) -> IDBRequestReadyState {
473 self.ready_state.get()
474 }
475
476 event_handler!(success, GetOnsuccess, SetOnsuccess);
478
479 event_handler!(error, GetOnerror, SetOnerror);
481}