script/dom/indexeddb/
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::{
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::indexeddb::idbcursor::{IDBCursor, IterationParam, ObjectStoreOrIndex};
36use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
37use crate::dom::indexeddb::idbrequest::IDBRequest;
38use crate::dom::indexeddb::idbtransaction::IDBTransaction;
39use crate::indexeddb::{
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 transaction 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(None));
164        }
165
166        Ok(())
167    }
168
169    /// Checks if the transaction 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(None));
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(None));
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(None));
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(None));
235                    }
236                    // Step 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(None)),
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) = indexeddb::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) = indexeddb::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. 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.
393        let serialized_query = convert_value_to_key_range(cx, query, Some(true));
394        // Step 7. Let operation be an algorithm to run delete records from an object store with store and range.
395        // Step 8. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
396        let (sender, receiver) = indexeddb::create_channel(self.global());
397        serialized_query.and_then(|key_range| {
398            IDBRequest::execute_async(
399                self,
400                AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
401                    sender,
402                    key_range,
403                }),
404                receiver,
405                None,
406                None,
407                CanGc::note(),
408            )
409        })
410    }
411
412    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-clear>
413    fn Clear(&self) -> Fallible<DomRoot<IDBRequest>> {
414        // Step 1. Let transaction be this’s transaction.
415        // Step 2. Let store be this's object store.
416        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
417        self.verify_not_deleted()?;
418
419        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
420        // Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
421        self.check_readwrite_transaction_active()?;
422
423        // Step 6. Let operation be an algorithm to run clear an object store with store.
424        // Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
425        let (sender, receiver) = indexeddb::create_channel(self.global());
426
427        IDBRequest::execute_async(
428            self,
429            AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)),
430            receiver,
431            None,
432            None,
433            CanGc::note(),
434        )
435    }
436
437    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-get>
438    fn Get(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
439        // Step 1. Let transaction be this’s transaction.
440        // Step 2. Let store be this's object store.
441        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
442        self.verify_not_deleted()?;
443
444        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
445        self.check_transaction_active()?;
446
447        // Step 5. Let range be the result of converting a value to a key range with query and true. Rethrow any exceptions.
448        let serialized_query = convert_value_to_key_range(cx, query, None);
449
450        // Step 6. Let operation be an algorithm to run retrieve a value from an object store with the current Realm record, store, and range.
451        // Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
452        let (sender, receiver) = indexeddb::create_channel(self.global());
453        serialized_query.and_then(|q| {
454            IDBRequest::execute_async(
455                self,
456                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
457                    sender,
458                    key_range: q,
459                }),
460                receiver,
461                None,
462                None,
463                CanGc::note(),
464            )
465        })
466    }
467
468    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey>
469    fn GetKey(&self, cx: SafeJSContext, query: HandleValue) -> Result<DomRoot<IDBRequest>, Error> {
470        // Step 1. Let transaction be this’s transaction.
471        // Step 2. Let store be this's object store.
472        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
473        self.verify_not_deleted()?;
474
475        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
476        self.check_transaction_active()?;
477
478        // 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.
479        let serialized_query = convert_value_to_key_range(cx, query, None);
480
481        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
482        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
483        // store as operation, using store and range.
484        let (sender, receiver) = indexeddb::create_channel(self.global());
485        serialized_query.and_then(|q| {
486            IDBRequest::execute_async(
487                self,
488                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetKey {
489                    sender,
490                    key_range: q,
491                }),
492                receiver,
493                None,
494                None,
495                CanGc::note(),
496            )
497        })
498    }
499
500    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall>
501    fn GetAll(
502        &self,
503        cx: SafeJSContext,
504        query: HandleValue,
505        count: Option<u32>,
506    ) -> Fallible<DomRoot<IDBRequest>> {
507        // Step 1. Let transaction be this’s transaction.
508        // Step 2. Let store be this's object store.
509        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
510        self.verify_not_deleted()?;
511
512        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
513        self.check_transaction_active()?;
514
515        // 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.
516        let serialized_query = convert_value_to_key_range(cx, query, None);
517
518        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
519        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
520        // store as operation, using store and range.
521        let (sender, receiver) = indexeddb::create_channel(self.global());
522        serialized_query.and_then(|q| {
523            IDBRequest::execute_async(
524                self,
525                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
526                    sender,
527                    key_range: q,
528                    count,
529                }),
530                receiver,
531                None,
532                None,
533                CanGc::note(),
534            )
535        })
536    }
537
538    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys>
539    fn GetAllKeys(
540        &self,
541        cx: SafeJSContext,
542        query: HandleValue,
543        count: Option<u32>,
544    ) -> Fallible<DomRoot<IDBRequest>> {
545        // Step 1. Let transaction be this’s transaction.
546        // Step 2. Let store be this's object store.
547        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
548        self.verify_not_deleted()?;
549
550        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
551        self.check_transaction_active()?;
552
553        // 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.
554        let serialized_query = convert_value_to_key_range(cx, query, None);
555
556        // Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
557        // The steps are run with this object store handle as source and the steps to retrieve a key from an object
558        // store as operation, using store and range.
559        let (sender, receiver) = indexeddb::create_channel(self.global());
560        serialized_query.and_then(|q| {
561            IDBRequest::execute_async(
562                self,
563                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
564                    sender,
565                    key_range: q,
566                    count,
567                }),
568                receiver,
569                None,
570                None,
571                CanGc::note(),
572            )
573        })
574    }
575
576    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-count>
577    fn Count(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
578        // Step 1. Let transaction be this’s transaction.
579        // Step 2. Let store be this's object store.
580        // Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
581        self.verify_not_deleted()?;
582
583        // Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
584        self.check_transaction_active()?;
585
586        // Step 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
587        let serialized_query = convert_value_to_key_range(cx, query, None);
588
589        // Step 6. Let operation be an algorithm to run count the records in a range with store and range.
590        // Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
591        let (sender, receiver) = indexeddb::create_channel(self.global());
592        serialized_query.and_then(|q| {
593            IDBRequest::execute_async(
594                self,
595                AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
596                    sender,
597                    key_range: q,
598                }),
599                receiver,
600                None,
601                None,
602                CanGc::note(),
603            )
604        })
605    }
606
607    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-opencursor>
608    fn OpenCursor(
609        &self,
610        cx: SafeJSContext,
611        query: HandleValue,
612        direction: IDBCursorDirection,
613    ) -> Fallible<DomRoot<IDBRequest>> {
614        self.open_cursor(cx, query, direction, false, CanGc::note())
615    }
616
617    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor>
618    fn OpenKeyCursor(
619        &self,
620        cx: SafeJSContext,
621        query: HandleValue,
622        direction: IDBCursorDirection,
623    ) -> Fallible<DomRoot<IDBRequest>> {
624        self.open_cursor(cx, query, direction, true, CanGc::note())
625    }
626
627    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-name>
628    fn Name(&self) -> DOMString {
629        self.name.borrow().clone()
630    }
631
632    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-setname>
633    fn SetName(&self, value: DOMString) -> ErrorResult {
634        // Step 2. Let transaction be this’s transaction.
635        let transaction = &self.transaction;
636
637        // Step 3. Let store be this's object store.
638        // Step 4. If store has been deleted, throw an "InvalidStateError" DOMException.
639        self.verify_not_deleted()?;
640
641        // Step 5. If transaction is not an upgrade transaction, throw an "InvalidStateError" DOMException.
642        if transaction.Mode() != IDBTransactionMode::Versionchange {
643            return Err(Error::InvalidState(None));
644        }
645        // Step 6. If transaction’s state is not active, throw a "TransactionInactiveError" DOMException.
646        self.check_transaction_active()?;
647
648        *self.name.borrow_mut() = value;
649        Ok(())
650    }
651
652    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-keypath>
653    fn KeyPath(&self, cx: SafeJSContext, mut ret_val: MutableHandleValue) {
654        match &self.key_path {
655            Some(KeyPath::String(path)) => path.safe_to_jsval(cx, ret_val, CanGc::note()),
656            Some(KeyPath::StringSequence(paths)) => paths.safe_to_jsval(cx, ret_val, CanGc::note()),
657            None => ret_val.set(NullValue()),
658        }
659    }
660
661    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-indexnames>
662    fn IndexNames(&self) -> DomRoot<DOMStringList> {
663        self.index_names.clone()
664    }
665
666    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction>
667    fn Transaction(&self) -> DomRoot<IDBTransaction> {
668        self.transaction()
669    }
670
671    /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-autoincrement>
672    fn AutoIncrement(&self) -> bool {
673        self.has_key_generator()
674    }
675}