1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::jsapi::Heap;
9use js::jsval::{JSVal, UndefinedValue};
10use js::rust::MutableHandleValue;
11use storage_traits::indexeddb_thread::{IndexedDBKeyRange, IndexedDBKeyType, IndexedDBRecord};
12
13use crate::dom::bindings::cell::DomRefCell;
14use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::{
15 IDBCursorDirection, IDBCursorMethods,
16};
17use crate::dom::bindings::codegen::UnionTypes::IDBObjectStoreOrIDBIndex;
18use crate::dom::bindings::error::Error;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
21use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
22use crate::dom::bindings::structuredclone;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::idbindex::IDBIndex;
25use crate::dom::idbobjectstore::IDBObjectStore;
26use crate::dom::idbrequest::IDBRequest;
27use crate::dom::idbtransaction::IDBTransaction;
28use crate::indexed_db::key_type_to_jsval;
29use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
30
31#[derive(JSTraceable, MallocSizeOf)]
32#[expect(unused)]
33#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
34pub(crate) enum ObjectStoreOrIndex {
35 ObjectStore(Dom<IDBObjectStore>),
36 Index(Dom<IDBIndex>),
37}
38
39#[dom_struct]
40pub(crate) struct IDBCursor {
41 reflector_: Reflector,
42
43 transaction: Dom<IDBTransaction>,
45 #[no_trace]
47 range: IndexedDBKeyRange,
48 source: ObjectStoreOrIndex,
50 direction: IDBCursorDirection,
52 #[no_trace]
54 position: DomRefCell<Option<IndexedDBKeyType>>,
55 #[no_trace]
57 key: DomRefCell<Option<IndexedDBKeyType>>,
58 #[ignore_malloc_size_of = "mozjs"]
60 value: Heap<JSVal>,
61 got_value: Cell<bool>,
63 #[no_trace]
65 object_store_position: DomRefCell<Option<IndexedDBKeyType>>,
66 key_only: bool,
68
69 request: MutNullableDom<IDBRequest>,
71}
72
73impl IDBCursor {
74 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
75 pub(crate) fn new_inherited(
76 transaction: &IDBTransaction,
77 direction: IDBCursorDirection,
78 got_value: bool,
79 source: ObjectStoreOrIndex,
80 range: IndexedDBKeyRange,
81 key_only: bool,
82 ) -> IDBCursor {
83 IDBCursor {
84 reflector_: Reflector::new(),
85 transaction: Dom::from_ref(transaction),
86 range,
87 source,
88 direction,
89 position: DomRefCell::new(None),
90 key: DomRefCell::new(None),
91 value: Heap::default(),
92 got_value: Cell::new(got_value),
93 object_store_position: DomRefCell::new(None),
94 key_only,
95 request: Default::default(),
96 }
97 }
98
99 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
100 #[allow(clippy::too_many_arguments)]
101 pub(crate) fn new(
102 global: &GlobalScope,
103 transaction: &IDBTransaction,
104 direction: IDBCursorDirection,
105 got_value: bool,
106 source: ObjectStoreOrIndex,
107 range: IndexedDBKeyRange,
108 key_only: bool,
109 can_gc: CanGc,
110 ) -> DomRoot<IDBCursor> {
111 reflect_dom_object(
112 Box::new(IDBCursor::new_inherited(
113 transaction,
114 direction,
115 got_value,
116 source,
117 range,
118 key_only,
119 )),
120 global,
121 can_gc,
122 )
123 }
124
125 fn set_position(&self, position: Option<IndexedDBKeyType>) {
126 *self.position.borrow_mut() = position;
127 }
128
129 fn set_key(&self, key: Option<IndexedDBKeyType>) {
130 *self.key.borrow_mut() = key;
131 }
132
133 fn set_object_store_position(&self, object_store_position: Option<IndexedDBKeyType>) {
134 *self.object_store_position.borrow_mut() = object_store_position;
135 }
136
137 pub(crate) fn set_request(&self, request: &IDBRequest) {
138 self.request.set(Some(request));
139 }
140
141 pub(crate) fn value(&self, mut out: MutableHandleValue) {
142 out.set(self.value.get());
143 }
144
145 pub(crate) fn effective_key(&self) -> Option<IndexedDBKeyType> {
147 match &self.source {
148 ObjectStoreOrIndex::ObjectStore(_) => self.position.borrow().clone(),
149 ObjectStoreOrIndex::Index(_) => self.object_store_position.borrow().clone(),
150 }
151 }
152}
153
154impl IDBCursorMethods<crate::DomTypeHolder> for IDBCursor {
155 fn Source(&self) -> IDBObjectStoreOrIDBIndex {
157 match &self.source {
158 ObjectStoreOrIndex::ObjectStore(source) => {
159 IDBObjectStoreOrIDBIndex::IDBObjectStore(source.as_rooted())
160 },
161 ObjectStoreOrIndex::Index(source) => {
162 IDBObjectStoreOrIDBIndex::IDBIndex(source.as_rooted())
163 },
164 }
165 }
166
167 fn Direction(&self) -> IDBCursorDirection {
169 self.direction
170 }
171
172 fn Key(&self, cx: SafeJSContext, mut value: MutableHandleValue) {
174 match self.key.borrow().as_ref() {
175 Some(key) => key_type_to_jsval(cx, key, value),
176 None => value.set(UndefinedValue()),
177 }
178 }
179
180 fn PrimaryKey(&self, cx: SafeJSContext, mut value: MutableHandleValue) {
182 match self.effective_key() {
183 Some(effective_key) => key_type_to_jsval(cx, &effective_key, value),
184 None => value.set(UndefinedValue()),
185 }
186 }
187
188 fn Request(&self) -> DomRoot<IDBRequest> {
190 self.request
191 .get()
192 .expect("IDBCursor.request should be set when cursor is opened")
193 }
194}
195
196#[derive(Clone)]
199pub(crate) struct IterationParam {
200 pub(crate) cursor: Trusted<IDBCursor>,
201 pub(crate) key: Option<IndexedDBKeyType>,
202 pub(crate) primary_key: Option<IndexedDBKeyType>,
203 pub(crate) count: Option<u32>,
204}
205
206pub(crate) fn iterate_cursor(
214 global: &GlobalScope,
215 cx: SafeJSContext,
216 param: &IterationParam,
217 records: Vec<IndexedDBRecord>,
218) -> Result<Option<DomRoot<IDBCursor>>, Error> {
219 let cursor = param.cursor.root();
221 let key = param.key.clone();
222 let primary_key = param.primary_key.clone();
223 let count = param.count;
224
225 let source = &cursor.source;
227
228 let direction = cursor.direction;
230
231 if primary_key.is_some() {
233 assert!(matches!(source, ObjectStoreOrIndex::Index(..)));
234 assert!(matches!(
235 direction,
236 IDBCursorDirection::Next | IDBCursorDirection::Prev
237 ));
238 }
239
240 let range = &cursor.range;
245
246 let mut position = cursor.position.borrow().clone();
248
249 let object_store_position = cursor.object_store_position.borrow().clone();
251
252 let mut count = count.unwrap_or(1);
254
255 let mut found_record: Option<&IndexedDBRecord> = None;
256
257 while count > 0 {
259 found_record = match direction {
261 IDBCursorDirection::Next => records.iter().find(|record| {
263 let requirement1 = || match &key {
268 Some(key) => &record.key >= key,
269 None => true,
270 };
271
272 let requirement2 = || match &primary_key {
276 Some(primary_key) => key.as_ref().is_some_and(|key| {
277 (&record.key == key && &record.primary_key >= primary_key) ||
278 &record.key > key
279 }),
280 _ => true,
281 };
282
283 let requirement3 = || match (&position, source) {
286 (Some(position), ObjectStoreOrIndex::ObjectStore(_)) => &record.key > position,
287 _ => true,
288 };
289
290 let requirement4 = || match (&position, source) {
294 (Some(position), ObjectStoreOrIndex::Index(_)) => {
295 (&record.key == position &&
296 object_store_position.as_ref().is_some_and(
297 |object_store_position| &record.primary_key > object_store_position,
298 )) ||
299 &record.key > position
300 },
301 _ => true,
302 };
303
304 let requirement5 = || range.contains(&record.key);
306
307 requirement1() &&
309 requirement2() &&
310 requirement3() &&
311 requirement4() &&
312 requirement5()
313 }),
314 IDBCursorDirection::Nextunique => records.iter().find(|record| {
316 let requirement1 = || match &key {
321 Some(key) => &record.key >= key,
322 None => true,
323 };
324
325 let requirement2 = || match &position {
327 Some(position) => &record.key > position,
328 None => true,
329 };
330
331 let requirement3 = || range.contains(&record.key);
333
334 requirement1() && requirement2() && requirement3()
336 }),
337 IDBCursorDirection::Prev => {
339 records.iter().rev().find(|&record| {
340 let requirement1 = || match &key {
345 Some(key) => &record.key <= key,
346 None => true,
347 };
348
349 let requirement2 = || match &primary_key {
353 Some(primary_key) => key.as_ref().is_some_and(|key| {
354 (&record.key == key && &record.primary_key <= primary_key) ||
355 &record.key < key
356 }),
357 _ => true,
358 };
359
360 let requirement3 = || match (&position, source) {
363 (Some(position), ObjectStoreOrIndex::ObjectStore(_)) => {
364 &record.key < position
365 },
366 _ => true,
367 };
368
369 let requirement4 = || match (&position, source) {
373 (Some(position), ObjectStoreOrIndex::Index(_)) => {
374 (&record.key == position &&
375 object_store_position.as_ref().is_some_and(
376 |object_store_position| {
377 &record.primary_key < object_store_position
378 },
379 )) ||
380 &record.key < position
381 },
382 _ => true,
383 };
384
385 let requirement5 = || range.contains(&record.key);
387
388 requirement1() &&
390 requirement2() &&
391 requirement3() &&
392 requirement4() &&
393 requirement5()
394 })
395 },
396 IDBCursorDirection::Prevunique => records
398 .iter()
399 .rev()
400 .find(|&record| {
401 let requirement1 = || match &key {
406 Some(key) => &record.key <= key,
407 None => true,
408 };
409
410 let requirement2 = || match &position {
412 Some(position) => &record.key < position,
413 None => true,
414 };
415
416 let requirement3 = || range.contains(&record.key);
418
419 requirement1() && requirement2() && requirement3()
421 })
422 .map(|temp_record| {
425 records
426 .iter()
427 .find(|&record| record.key == temp_record.key)
428 .expect(
429 "Record with key equal to temp record's key should exist in records",
430 )
431 }),
432 };
433
434 match found_record {
435 None => {
437 cursor.set_key(None);
439
440 if matches!(source, ObjectStoreOrIndex::Index(_)) {
442 cursor.set_object_store_position(None);
443 }
444
445 if !cursor.key_only {
447 cursor.value.set(UndefinedValue());
448 }
449
450 return Ok(None);
452 },
453 Some(found_record) => {
454 position = Some(found_record.key.clone());
456
457 if matches!(source, ObjectStoreOrIndex::Index(_)) {
459 cursor.set_object_store_position(Some(found_record.primary_key.clone()));
460 }
461
462 count -= 1;
464 },
465 }
466 }
467 let found_record =
468 found_record.expect("The while loop above guarantees found_record is defined");
469
470 cursor.set_position(position);
472
473 if let ObjectStoreOrIndex::Index(_) = source {
475 cursor.set_object_store_position(object_store_position);
476 }
477
478 cursor.set_key(Some(found_record.key.clone()));
480
481 if !cursor.key_only {
483 rooted!(in(*cx) let mut new_cursor_value = UndefinedValue());
486 bincode::deserialize(&found_record.value)
487 .map_err(|_| Error::Data)
488 .and_then(|data| structuredclone::read(global, data, new_cursor_value.handle_mut()))?;
489 cursor.value.set(new_cursor_value.get());
490 }
491
492 cursor.got_value.set(true);
494
495 Ok(Some(cursor))
497}