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::indexeddb::idbindex::IDBIndex;
25use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
26use crate::dom::indexeddb::idbrequest::IDBRequest;
27use crate::dom::indexeddb::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, can_gc: CanGc, mut value: MutableHandleValue) {
174 match self.key.borrow().as_ref() {
175 Some(key) => key_type_to_jsval(cx, key, value, can_gc),
176 None => value.set(UndefinedValue()),
177 }
178 }
179
180 fn PrimaryKey(&self, cx: SafeJSContext, can_gc: CanGc, mut value: MutableHandleValue) {
182 match self.effective_key() {
183 Some(effective_key) => key_type_to_jsval(cx, &effective_key, value, can_gc),
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 can_gc: CanGc,
219) -> Result<Option<DomRoot<IDBCursor>>, Error> {
220 let cursor = param.cursor.root();
222 let key = param.key.clone();
223 let primary_key = param.primary_key.clone();
224 let count = param.count;
225
226 let source = &cursor.source;
228
229 let direction = cursor.direction;
231
232 if primary_key.is_some() {
234 assert!(matches!(source, ObjectStoreOrIndex::Index(..)));
235 assert!(matches!(
236 direction,
237 IDBCursorDirection::Next | IDBCursorDirection::Prev
238 ));
239 }
240
241 let range = &cursor.range;
246
247 let mut position = cursor.position.borrow().clone();
249
250 let object_store_position = cursor.object_store_position.borrow().clone();
252
253 let mut count = count.unwrap_or(1);
255
256 let mut found_record: Option<&IndexedDBRecord> = None;
257
258 while count > 0 {
260 found_record = match direction {
262 IDBCursorDirection::Next => records.iter().find(|record| {
264 let requirement1 = || match &key {
269 Some(key) => &record.key >= key,
270 None => true,
271 };
272
273 let requirement2 = || match &primary_key {
277 Some(primary_key) => key.as_ref().is_some_and(|key| {
278 (&record.key == key && &record.primary_key >= primary_key) ||
279 &record.key > key
280 }),
281 _ => true,
282 };
283
284 let requirement3 = || match (&position, source) {
287 (Some(position), ObjectStoreOrIndex::ObjectStore(_)) => &record.key > position,
288 _ => true,
289 };
290
291 let requirement4 = || match (&position, source) {
295 (Some(position), ObjectStoreOrIndex::Index(_)) => {
296 (&record.key == position &&
297 object_store_position.as_ref().is_some_and(
298 |object_store_position| &record.primary_key > object_store_position,
299 )) ||
300 &record.key > position
301 },
302 _ => true,
303 };
304
305 let requirement5 = || range.contains(&record.key);
307
308 requirement1() &&
310 requirement2() &&
311 requirement3() &&
312 requirement4() &&
313 requirement5()
314 }),
315 IDBCursorDirection::Nextunique => records.iter().find(|record| {
317 let requirement1 = || match &key {
322 Some(key) => &record.key >= key,
323 None => true,
324 };
325
326 let requirement2 = || match &position {
328 Some(position) => &record.key > position,
329 None => true,
330 };
331
332 let requirement3 = || range.contains(&record.key);
334
335 requirement1() && requirement2() && requirement3()
337 }),
338 IDBCursorDirection::Prev => {
340 records.iter().rev().find(|&record| {
341 let requirement1 = || match &key {
346 Some(key) => &record.key <= key,
347 None => true,
348 };
349
350 let requirement2 = || match &primary_key {
354 Some(primary_key) => key.as_ref().is_some_and(|key| {
355 (&record.key == key && &record.primary_key <= primary_key) ||
356 &record.key < key
357 }),
358 _ => true,
359 };
360
361 let requirement3 = || match (&position, source) {
364 (Some(position), ObjectStoreOrIndex::ObjectStore(_)) => {
365 &record.key < position
366 },
367 _ => true,
368 };
369
370 let requirement4 = || match (&position, source) {
374 (Some(position), ObjectStoreOrIndex::Index(_)) => {
375 (&record.key == position &&
376 object_store_position.as_ref().is_some_and(
377 |object_store_position| {
378 &record.primary_key < object_store_position
379 },
380 )) ||
381 &record.key < position
382 },
383 _ => true,
384 };
385
386 let requirement5 = || range.contains(&record.key);
388
389 requirement1() &&
391 requirement2() &&
392 requirement3() &&
393 requirement4() &&
394 requirement5()
395 })
396 },
397 IDBCursorDirection::Prevunique => records
399 .iter()
400 .rev()
401 .find(|&record| {
402 let requirement1 = || match &key {
407 Some(key) => &record.key <= key,
408 None => true,
409 };
410
411 let requirement2 = || match &position {
413 Some(position) => &record.key < position,
414 None => true,
415 };
416
417 let requirement3 = || range.contains(&record.key);
419
420 requirement1() && requirement2() && requirement3()
422 })
423 .map(|temp_record| {
426 records
427 .iter()
428 .find(|&record| record.key == temp_record.key)
429 .expect(
430 "Record with key equal to temp record's key should exist in records",
431 )
432 }),
433 };
434
435 match found_record {
436 None => {
438 cursor.set_key(None);
440
441 if matches!(source, ObjectStoreOrIndex::Index(_)) {
443 cursor.set_object_store_position(None);
444 }
445
446 if !cursor.key_only {
448 cursor.value.set(UndefinedValue());
449 }
450
451 return Ok(None);
453 },
454 Some(found_record) => {
455 position = Some(found_record.key.clone());
457
458 if matches!(source, ObjectStoreOrIndex::Index(_)) {
460 cursor.set_object_store_position(Some(found_record.primary_key.clone()));
461 }
462
463 count -= 1;
465 },
466 }
467 }
468 let found_record =
469 found_record.expect("The while loop above guarantees found_record is defined");
470
471 cursor.set_position(position);
473
474 if let ObjectStoreOrIndex::Index(_) = source {
476 cursor.set_object_store_position(object_store_position);
477 }
478
479 cursor.set_key(Some(found_record.key.clone()));
481
482 if !cursor.key_only {
484 rooted!(in(*cx) let mut new_cursor_value = UndefinedValue());
487 bincode::deserialize(&found_record.value)
488 .map_err(|_| Error::Data)
489 .and_then(|data| {
490 structuredclone::read(global, data, new_cursor_value.handle_mut(), can_gc)
491 })?;
492 cursor.value.set(new_cursor_value.get());
493 }
494
495 cursor.got_value.set(true);
497
498 Ok(Some(cursor))
500}