script/dom/
idbobjectstore.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use base::IpcSend;
6use dom_struct::dom_struct;
7use js::gc::MutableHandleValue;
8use js::jsval::NullValue;
9use js::rust::HandleValue;
10use profile_traits::ipc;
11use script_bindings::conversions::SafeToJSValConvertible;
12use script_bindings::error::ErrorResult;
13use storage_traits::indexeddb_thread::{
14    AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType,
15    IndexedDBThreadMsg, SyncOperation,
16};
17
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::IDBCursorDirection;
20use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
21use crate::dom::bindings::codegen::Bindings::IDBObjectStoreBinding::IDBObjectStoreMethods;
22use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::{
23    IDBTransactionMethods, IDBTransactionMode,
24};
25// We need to alias this name, otherwise test-tidy complains at &String reference.
26use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
27use crate::dom::bindings::error::{Error, Fallible};
28use crate::dom::bindings::refcounted::Trusted;
29use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
30use crate::dom::bindings::root::{Dom, DomRoot};
31use crate::dom::bindings::str::DOMString;
32use crate::dom::bindings::structuredclone;
33use crate::dom::domstringlist::DOMStringList;
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::idbcursor::{IDBCursor, IterationParam, ObjectStoreOrIndex};
36use crate::dom::idbcursorwithvalue::IDBCursorWithValue;
37use crate::dom::idbrequest::IDBRequest;
38use crate::dom::idbtransaction::IDBTransaction;
39use crate::indexed_db::{
40    self, ExtractionResult, convert_value_to_key, convert_value_to_key_range, extract_key,
41};
42use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
43
44#[derive(Clone, JSTraceable, MallocSizeOf)]
45pub enum KeyPath {
46    String(DOMString),
47    StringSequence(Vec<DOMString>),
48}
49
50#[dom_struct]
51pub struct IDBObjectStore {
52    reflector_: Reflector,
53    name: DomRefCell<DOMString>,
54    key_path: Option<KeyPath>,
55    index_names: DomRoot<DOMStringList>,
56    transaction: Dom<IDBTransaction>,
57
58    // We store the db name in the object store to be able to find the correct
59    // store in the idb thread when checking if we have a key generator
60    db_name: DOMString,
61}
62
63impl IDBObjectStore {
64    pub fn new_inherited(
65        global: &GlobalScope,
66        db_name: DOMString,
67        name: DOMString,
68        options: Option<&IDBObjectStoreParameters>,
69        can_gc: CanGc,
70        transaction: &IDBTransaction,
71    ) -> IDBObjectStore {
72        let key_path: Option<KeyPath> = match options {
73            Some(options) => options.keyPath.as_ref().map(|path| match path {
74                StrOrStringSequence::String(inner) => KeyPath::String(inner.clone()),
75                StrOrStringSequence::StringSequence(inner) => {
76                    KeyPath::StringSequence(inner.clone())
77                },
78            }),
79            None => None,
80        };
81
82        IDBObjectStore {
83            reflector_: Reflector::new(),
84            name: DomRefCell::new(name),
85            key_path,
86
87            index_names: DOMStringList::new(global, Vec::new(), can_gc),
88            transaction: Dom::from_ref(transaction),
89            db_name,
90        }
91    }
92
93    pub fn new(
94        global: &GlobalScope,
95        db_name: DOMString,
96        name: DOMString,
97        options: Option<&IDBObjectStoreParameters>,
98        can_gc: CanGc,
99        transaction: &IDBTransaction,
100    ) -> DomRoot<IDBObjectStore> {
101        reflect_dom_object(
102            Box::new(IDBObjectStore::new_inherited(
103                global,
104                db_name,
105                name,
106                options,
107                can_gc,
108                transaction,
109            )),
110            global,
111            can_gc,
112        )
113    }
114
115    pub fn get_name(&self) -> DOMString {
116        self.name.borrow().clone()
117    }
118
119    pub fn transaction(&self) -> DomRoot<IDBTransaction> {
120        self.transaction.as_rooted()
121    }
122
123    fn has_key_generator(&self) -> bool {
124        let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
125
126        let operation = SyncOperation::HasKeyGenerator(
127            sender,
128            self.global().origin().immutable().clone(),
129            self.db_name.to_string(),
130            self.name.borrow().to_string(),
131        );
132
133        self.global()
134            .storage_threads()
135            .send(IndexedDBThreadMsg::Sync(operation))
136            .unwrap();
137
138        // First unwrap for ipc
139        // Second unwrap will never happen unless this db gets manually deleted somehow
140        receiver.recv().unwrap().unwrap()
141    }
142
143    // https://www.w3.org/TR/IndexedDB-2/#object-store-in-line-keys
144    fn uses_inline_keys(&self) -> bool {
145        self.key_path.is_some()
146    }
147
148    fn verify_not_deleted(&self) -> ErrorResult {
149        let db = self.transaction.Db();
150        if !db.object_store_exists(&self.name.borrow()) {
151            return Err(Error::InvalidState(None));
152        }
153        Ok(())
154    }
155
156    /// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not.
157    fn check_transaction_active(&self) -> Fallible<()> {
158        // Let transaction be this object store handle's transaction.
159        let transaction = &self.transaction;
160
161        // If transaction is not active, throw a "TransactionInactiveError" DOMException.
162        if !transaction.is_active() {
163            return Err(Error::TransactionInactive);
164        }
165
166        Ok(())
167    }
168
169    /// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not.
170    /// it then checks if the transaction is a read-only transaction, throwing a "ReadOnlyError" DOMException if so.
171    fn check_readwrite_transaction_active(&self) -> Fallible<()> {
172        // Let transaction be this object store handle's transaction.
173        let transaction = &self.transaction;
174
175        // If transaction is not active, throw a "TransactionInactiveError" DOMException.
176        self.check_transaction_active()?;
177
178        if let IDBTransactionMode::Readonly = transaction.get_mode() {
179            return Err(Error::ReadOnly);
180        }
181        Ok(())
182    }
183
184    // https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put
185    fn put(
186        &self,
187        cx: SafeJSContext,
188        value: HandleValue,
189        key: HandleValue,
190        overwrite: bool,
191        can_gc: CanGc,
192    ) -> Fallible<DomRoot<IDBRequest>> {
193        // Step 1. Let transaction be handle’s transaction.
194        // Step 2: Let store be this object store handle's object store.
195        // This is resolved in the `execute_async` function.
196        // Step 3: If store has been deleted, throw an "InvalidStateError" DOMException.
197        self.verify_not_deleted()?;
198
199        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
200        // Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
201        self.check_readwrite_transaction_active()?;
202
203        // Step 6: If store uses in-line keys and key was given, throw a "DataError" DOMException.
204        if !key.is_undefined() && self.uses_inline_keys() {
205            return Err(Error::Data);
206        }
207
208        // Step 7: If store uses out-of-line keys and has no key generator
209        // and key was not given, throw a "DataError" DOMException.
210        if !self.uses_inline_keys() && !self.has_key_generator() && key.is_undefined() {
211            return Err(Error::Data);
212        }
213
214        // Step 8: If key was given, then: convert a value to a key with key
215        let serialized_key: Option<IndexedDBKeyType>;
216
217        if !key.is_undefined() {
218            serialized_key = Some(convert_value_to_key(cx, key, None)?.into_result()?);
219        } else {
220            // Step 11: We should use in-line keys instead
221            // Step 11.1: Let kpk be the result of running the steps to extract a
222            // key from a value using a key path with clone and store’s key path.
223            let extraction_result = self
224                .key_path
225                .as_ref()
226                .map(|p| extract_key(cx, value, p, None));
227
228            match extraction_result {
229                Some(Ok(ExtractionResult::Failure)) | None => {
230                    // Step 11.4. Otherwise:
231                    // Step 11.4.1. If store does not have a key generator, throw
232                    // a "DataError" DOMException.
233                    if !self.has_key_generator() {
234                        return Err(Error::Data);
235                    }
236                    // Stept 11.4.2. Otherwise, if the steps to check that a key could
237                    // be injected into a value with clone and store’s key path return
238                    // false, throw a "DataError" DOMException.
239                    // TODO
240                    serialized_key = None;
241                },
242                // Step 11.1. Rethrow any exceptions.
243                Some(extraction_result) => match extraction_result? {
244                    // Step 11.2. If kpk is invalid, throw a "DataError" DOMException.
245                    ExtractionResult::Invalid => return Err(Error::Data),
246                    // Step 11.3. If kpk is not failure, let key be kpk.
247                    ExtractionResult::Key(kpk) => serialized_key = Some(kpk),
248                    ExtractionResult::Failure => unreachable!(),
249                },
250            }
251        }
252
253        // Step 10. Let clone be a clone of value in targetRealm during transaction. Rethrow any exceptions.
254        let cloned_value = structuredclone::write(cx, value, None)?;
255        let Ok(serialized_value) = bincode::serialize(&cloned_value) else {
256            return Err(Error::InvalidState(None));
257        };
258
259        let (sender, receiver) = indexed_db::create_channel(self.global());
260
261        // Step 12. Let operation be an algorithm to run store a record into an object store with store, clone, key, and no-overwrite flag.
262        // Step 13. Return the result (an IDBRequest) of running asynchronously execute a request with handle and operation.
263        IDBRequest::execute_async(
264            self,
265            AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
266                sender,
267                key: serialized_key,
268                value: serialized_value,
269                should_overwrite: overwrite,
270            }),
271            receiver,
272            None,
273            None,
274            can_gc,
275        )
276    }
277
278    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-opencursor>
279    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor>
280    fn open_cursor(
281        &self,
282        cx: SafeJSContext,
283        query: HandleValue,
284        direction: IDBCursorDirection,
285        key_only: bool,
286        can_gc: CanGc,
287    ) -> Fallible<DomRoot<IDBRequest>> {
288        // Step 1. Let transaction be this object store handle's transaction.
289        // Step 2. Let store be this object store handle's object store.
290
291        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
292        self.verify_not_deleted()?;
293
294        // Step 4. If transaction is not active, throw a "TransactionInactiveError" DOMException.
295        self.check_transaction_active()?;
296
297        // Step 5. Let range be the result of running the steps to convert a value to a key range
298        // with query. Rethrow any exceptions.
299        //
300        // The query parameter may be a key or an IDBKeyRange to use as the cursor's range. If null
301        // or not given, an unbounded key range is used.
302        let range = convert_value_to_key_range(cx, query, Some(false))?;
303
304        // Step 6. Let cursor be a new cursor with transaction set to transaction, an undefined
305        // position, direction set to direction, got value flag unset, and undefined key and value.
306        // The source of cursor is store. The range of cursor is range.
307        //
308        // NOTE: A cursor that has the key only flag unset implements the IDBCursorWithValue
309        // interface as well.
310        let cursor = if key_only {
311            IDBCursor::new(
312                &self.global(),
313                &self.transaction,
314                direction,
315                false,
316                ObjectStoreOrIndex::ObjectStore(Dom::from_ref(self)),
317                range.clone(),
318                key_only,
319                can_gc,
320            )
321        } else {
322            DomRoot::upcast(IDBCursorWithValue::new(
323                &self.global(),
324                &self.transaction,
325                direction,
326                false,
327                ObjectStoreOrIndex::ObjectStore(Dom::from_ref(self)),
328                range.clone(),
329                key_only,
330                can_gc,
331            ))
332        };
333
334        // Step 7. Run the steps to asynchronously execute a request and return the IDBRequest
335        // created by these steps. The steps are run with this object store handle as source and
336        // the steps to iterate a cursor as operation, using the current Realm as targetRealm, and
337        // cursor.
338        let iteration_param = IterationParam {
339            cursor: Trusted::new(&cursor),
340            key: None,
341            primary_key: None,
342            count: None,
343        };
344        let (sender, receiver) = indexed_db::create_channel(self.global());
345        IDBRequest::execute_async(
346            self,
347            AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
348                sender,
349                key_range: range,
350            }),
351            receiver,
352            None,
353            Some(iteration_param),
354            can_gc,
355        )
356        .inspect(|request| cursor.set_request(request))
357    }
358}
359
360impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
361    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put>
362    fn Put(
363        &self,
364        cx: SafeJSContext,
365        value: HandleValue,
366        key: HandleValue,
367    ) -> Fallible<DomRoot<IDBRequest>> {
368        self.put(cx, value, key, true, CanGc::note())
369    }
370
371    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-add>
372    fn Add(
373        &self,
374        cx: SafeJSContext,
375        value: HandleValue,
376        key: HandleValue,
377    ) -> Fallible<DomRoot<IDBRequest>> {
378        self.put(cx, value, key, false, CanGc::note())
379    }
380
381    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-delete>
382    fn Delete(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
383        // Step 1. Let transaction be this’s transaction.
384        // Step 2. Let store be this's object store.
385        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
386        self.verify_not_deleted()?;
387
388        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
389        // Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
390        self.check_readwrite_transaction_active()?;
391
392        // Step 6
393        // TODO: Convert to key range instead
394        let serialized_query = convert_value_to_key(cx, query, None)?.into_result();
395        // Step 7. Let operation be an algorithm to run delete records from an object store with store and range.
396        // Stpe 8. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
397        let (sender, receiver) = indexed_db::create_channel(self.global());
398        serialized_query.and_then(|q| {
399            IDBRequest::execute_async(
400                self,
401                AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem { sender, key: q }),
402                receiver,
403                None,
404                None,
405                CanGc::note(),
406            )
407        })
408    }
409
410    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-clear>
411    fn Clear(&self) -> Fallible<DomRoot<IDBRequest>> {
412        // Step 1. Let transaction be this’s transaction.
413        // Step 2. Let store be this's object store.
414        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
415        self.verify_not_deleted()?;
416
417        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
418        // Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
419        self.check_readwrite_transaction_active()?;
420
421        // Step 6. Let operation be an algorithm to run clear an object store with store.
422        // Stpe 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
423        let (sender, receiver) = indexed_db::create_channel(self.global());
424
425        IDBRequest::execute_async(
426            self,
427            AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)),
428            receiver,
429            None,
430            None,
431            CanGc::note(),
432        )
433    }
434
435    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-get>
436    fn Get(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
437        // Step 1. Let transaction be this’s transaction.
438        // Step 2. Let store be this's object store.
439        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
440        self.verify_not_deleted()?;
441
442        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
443        self.check_transaction_active()?;
444
445        // Step 5. Let range be the result of converting a value to a key range with query and true. Rethrow any exceptions.
446        let serialized_query = convert_value_to_key_range(cx, query, None);
447
448        // Step 6. Let operation be an algorithm to run retrieve a value from an object store with the current Realm record, store, and range.
449        // Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
450        let (sender, receiver) = indexed_db::create_channel(self.global());
451        serialized_query.and_then(|q| {
452            IDBRequest::execute_async(
453                self,
454                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
455                    sender,
456                    key_range: q,
457                }),
458                receiver,
459                None,
460                None,
461                CanGc::note(),
462            )
463        })
464    }
465
466    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey>
467    fn GetKey(&self, cx: SafeJSContext, query: HandleValue) -> Result<DomRoot<IDBRequest>, Error> {
468        // Step 1. Let transaction be this’s transaction.
469        // Step 2. Let store be this's object store.
470        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
471        self.verify_not_deleted()?;
472
473        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
474        self.check_transaction_active()?;
475
476        // Step 5. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. Rethrow any exceptions.
477        let serialized_query = convert_value_to_key_range(cx, query, None);
478
479        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
480        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
481        // store as operation, using store and range.
482        let (sender, receiver) = indexed_db::create_channel(self.global());
483        serialized_query.and_then(|q| {
484            IDBRequest::execute_async(
485                self,
486                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetKey {
487                    sender,
488                    key_range: q,
489                }),
490                receiver,
491                None,
492                None,
493                CanGc::note(),
494            )
495        })
496    }
497
498    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall>
499    fn GetAll(
500        &self,
501        cx: SafeJSContext,
502        query: HandleValue,
503        count: Option<u32>,
504    ) -> Fallible<DomRoot<IDBRequest>> {
505        // Step 1. Let transaction be this’s transaction.
506        // Step 2. Let store be this's object store.
507        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
508        self.verify_not_deleted()?;
509
510        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
511        self.check_transaction_active()?;
512
513        // Step 5. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. Rethrow any exceptions.
514        let serialized_query = convert_value_to_key_range(cx, query, None);
515
516        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
517        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
518        // store as operation, using store and range.
519        let (sender, receiver) = indexed_db::create_channel(self.global());
520        serialized_query.and_then(|q| {
521            IDBRequest::execute_async(
522                self,
523                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
524                    sender,
525                    key_range: q,
526                    count,
527                }),
528                receiver,
529                None,
530                None,
531                CanGc::note(),
532            )
533        })
534    }
535
536    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys>
537    fn GetAllKeys(
538        &self,
539        cx: SafeJSContext,
540        query: HandleValue,
541        count: Option<u32>,
542    ) -> Fallible<DomRoot<IDBRequest>> {
543        // Step 1. Let transaction be this’s transaction.
544        // Step 2. Let store be this's object store.
545        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
546        self.verify_not_deleted()?;
547
548        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
549        self.check_transaction_active()?;
550
551        // Step 5. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. Rethrow any exceptions.
552        let serialized_query = convert_value_to_key_range(cx, query, None);
553
554        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
555        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
556        // store as operation, using store and range.
557        let (sender, receiver) = indexed_db::create_channel(self.global());
558        serialized_query.and_then(|q| {
559            IDBRequest::execute_async(
560                self,
561                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
562                    sender,
563                    key_range: q,
564                    count,
565                }),
566                receiver,
567                None,
568                None,
569                CanGc::note(),
570            )
571        })
572    }
573
574    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-count>
575    fn Count(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
576        // Step 1. Let transaction be this’s transaction.
577        // Step 2. Let store be this's object store.
578        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
579        self.verify_not_deleted()?;
580
581        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
582        self.check_transaction_active()?;
583
584        // Step 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
585        let serialized_query = convert_value_to_key_range(cx, query, None);
586
587        // Step 6. Let operation be an algorithm to run count the records in a range with store and range.
588        // Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
589        let (sender, receiver) = indexed_db::create_channel(self.global());
590        serialized_query.and_then(|q| {
591            IDBRequest::execute_async(
592                self,
593                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
594                    sender,
595                    key_range: q,
596                }),
597                receiver,
598                None,
599                None,
600                CanGc::note(),
601            )
602        })
603    }
604
605    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-opencursor>
606    fn OpenCursor(
607        &self,
608        cx: SafeJSContext,
609        query: HandleValue,
610        direction: IDBCursorDirection,
611    ) -> Fallible<DomRoot<IDBRequest>> {
612        self.open_cursor(cx, query, direction, false, CanGc::note())
613    }
614
615    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor>
616    fn OpenKeyCursor(
617        &self,
618        cx: SafeJSContext,
619        query: HandleValue,
620        direction: IDBCursorDirection,
621    ) -> Fallible<DomRoot<IDBRequest>> {
622        self.open_cursor(cx, query, direction, true, CanGc::note())
623    }
624
625    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-name>
626    fn Name(&self) -> DOMString {
627        self.name.borrow().clone()
628    }
629
630    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-setname>
631    fn SetName(&self, value: DOMString) -> ErrorResult {
632        // Step 2. Let transaction be this’s transaction.
633        let transaction = &self.transaction;
634
635        // Step 3. Let store be this's object store.
636        // Step 4. If store has been deleted, throw an "InvalidStateError" DOMException.
637        self.verify_not_deleted()?;
638
639        // Step 5. If transaction is not an upgrade transaction, throw an "InvalidStateError" DOMException.
640        if transaction.Mode() != IDBTransactionMode::Versionchange {
641            return Err(Error::InvalidState(None));
642        }
643        // Step 6. If transaction’s state is not active, throw a "TransactionInactiveError" DOMException.
644        self.check_transaction_active()?;
645
646        *self.name.borrow_mut() = value;
647        Ok(())
648    }
649
650    // https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-keypath
651    fn KeyPath(&self, cx: SafeJSContext, mut ret_val: MutableHandleValue) {
652        match &self.key_path {
653            Some(KeyPath::String(path)) => path.safe_to_jsval(cx, ret_val),
654            Some(KeyPath::StringSequence(paths)) => paths.safe_to_jsval(cx, ret_val),
655            None => ret_val.set(NullValue()),
656        }
657    }
658
659    // https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-indexnames
660    fn IndexNames(&self) -> DomRoot<DOMStringList> {
661        self.index_names.clone()
662    }
663
664    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction>
665    fn Transaction(&self) -> DomRoot<IDBTransaction> {
666        self.transaction()
667    }
668
669    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-autoincrement>
670    fn AutoIncrement(&self) -> bool {
671        self.has_key_generator()
672    }
673}