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}