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}