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