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}