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