mirror of
https://github.com/servo/servo.git
synced 2025-09-27 15:20:09 +01:00
Part of #39418. See that PR for a full description. Moves: - `read_json_from_file` - `write_json_to_file` - `IpcSendResult` - `IpcSend` Renames: - `CoreResourceThreadPool` to `ThreadPool` (shorter and more descriptive, as we use it for more than the core resource thread now) Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
673 lines
27 KiB
Rust
673 lines
27 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||
|
||
use base::IpcSend;
|
||
use dom_struct::dom_struct;
|
||
use js::gc::MutableHandleValue;
|
||
use js::jsval::NullValue;
|
||
use js::rust::HandleValue;
|
||
use net_traits::indexeddb_thread::{
|
||
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType,
|
||
IndexedDBThreadMsg, SyncOperation,
|
||
};
|
||
use profile_traits::ipc;
|
||
use script_bindings::conversions::SafeToJSValConvertible;
|
||
use script_bindings::error::ErrorResult;
|
||
|
||
use crate::dom::bindings::cell::DomRefCell;
|
||
use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::IDBCursorDirection;
|
||
use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
|
||
use crate::dom::bindings::codegen::Bindings::IDBObjectStoreBinding::IDBObjectStoreMethods;
|
||
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::{
|
||
IDBTransactionMethods, IDBTransactionMode,
|
||
};
|
||
// We need to alias this name, otherwise test-tidy complains at &String reference.
|
||
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
|
||
use crate::dom::bindings::error::{Error, Fallible};
|
||
use crate::dom::bindings::refcounted::Trusted;
|
||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||
use crate::dom::bindings::str::DOMString;
|
||
use crate::dom::bindings::structuredclone;
|
||
use crate::dom::domstringlist::DOMStringList;
|
||
use crate::dom::globalscope::GlobalScope;
|
||
use crate::dom::idbcursor::{IDBCursor, IterationParam, ObjectStoreOrIndex};
|
||
use crate::dom::idbcursorwithvalue::IDBCursorWithValue;
|
||
use crate::dom::idbrequest::IDBRequest;
|
||
use crate::dom::idbtransaction::IDBTransaction;
|
||
use crate::indexed_db::{
|
||
self, ExtractionResult, convert_value_to_key, convert_value_to_key_range, extract_key,
|
||
};
|
||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||
|
||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||
pub enum KeyPath {
|
||
String(DOMString),
|
||
StringSequence(Vec<DOMString>),
|
||
}
|
||
|
||
#[dom_struct]
|
||
pub struct IDBObjectStore {
|
||
reflector_: Reflector,
|
||
name: DomRefCell<DOMString>,
|
||
key_path: Option<KeyPath>,
|
||
index_names: DomRoot<DOMStringList>,
|
||
transaction: Dom<IDBTransaction>,
|
||
|
||
// We store the db name in the object store to be able to find the correct
|
||
// store in the idb thread when checking if we have a key generator
|
||
db_name: DOMString,
|
||
}
|
||
|
||
impl IDBObjectStore {
|
||
pub fn new_inherited(
|
||
global: &GlobalScope,
|
||
db_name: DOMString,
|
||
name: DOMString,
|
||
options: Option<&IDBObjectStoreParameters>,
|
||
can_gc: CanGc,
|
||
transaction: &IDBTransaction,
|
||
) -> IDBObjectStore {
|
||
let key_path: Option<KeyPath> = match options {
|
||
Some(options) => options.keyPath.as_ref().map(|path| match path {
|
||
StrOrStringSequence::String(inner) => KeyPath::String(inner.clone()),
|
||
StrOrStringSequence::StringSequence(inner) => {
|
||
KeyPath::StringSequence(inner.clone())
|
||
},
|
||
}),
|
||
None => None,
|
||
};
|
||
|
||
IDBObjectStore {
|
||
reflector_: Reflector::new(),
|
||
name: DomRefCell::new(name),
|
||
key_path,
|
||
|
||
index_names: DOMStringList::new(global, Vec::new(), can_gc),
|
||
transaction: Dom::from_ref(transaction),
|
||
db_name,
|
||
}
|
||
}
|
||
|
||
pub fn new(
|
||
global: &GlobalScope,
|
||
db_name: DOMString,
|
||
name: DOMString,
|
||
options: Option<&IDBObjectStoreParameters>,
|
||
can_gc: CanGc,
|
||
transaction: &IDBTransaction,
|
||
) -> DomRoot<IDBObjectStore> {
|
||
reflect_dom_object(
|
||
Box::new(IDBObjectStore::new_inherited(
|
||
global,
|
||
db_name,
|
||
name,
|
||
options,
|
||
can_gc,
|
||
transaction,
|
||
)),
|
||
global,
|
||
can_gc,
|
||
)
|
||
}
|
||
|
||
pub fn get_name(&self) -> DOMString {
|
||
self.name.borrow().clone()
|
||
}
|
||
|
||
pub fn transaction(&self) -> DomRoot<IDBTransaction> {
|
||
self.transaction.as_rooted()
|
||
}
|
||
|
||
fn has_key_generator(&self) -> bool {
|
||
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
||
|
||
let operation = SyncOperation::HasKeyGenerator(
|
||
sender,
|
||
self.global().origin().immutable().clone(),
|
||
self.db_name.to_string(),
|
||
self.name.borrow().to_string(),
|
||
);
|
||
|
||
self.global()
|
||
.resource_threads()
|
||
.send(IndexedDBThreadMsg::Sync(operation))
|
||
.unwrap();
|
||
|
||
// First unwrap for ipc
|
||
// Second unwrap will never happen unless this db gets manually deleted somehow
|
||
receiver.recv().unwrap().unwrap()
|
||
}
|
||
|
||
// https://www.w3.org/TR/IndexedDB-2/#object-store-in-line-keys
|
||
fn uses_inline_keys(&self) -> bool {
|
||
self.key_path.is_some()
|
||
}
|
||
|
||
fn verify_not_deleted(&self) -> ErrorResult {
|
||
let db = self.transaction.Db();
|
||
if !db.object_store_exists(&self.name.borrow()) {
|
||
return Err(Error::InvalidState);
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not.
|
||
fn check_transaction_active(&self) -> Fallible<()> {
|
||
// Let transaction be this object store handle's transaction.
|
||
let transaction = &self.transaction;
|
||
|
||
// If transaction is not active, throw a "TransactionInactiveError" DOMException.
|
||
if !transaction.is_active() {
|
||
return Err(Error::TransactionInactive);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not.
|
||
/// it then checks if the transaction is a read-only transaction, throwing a "ReadOnlyError" DOMException if so.
|
||
fn check_readwrite_transaction_active(&self) -> Fallible<()> {
|
||
// Let transaction be this object store handle's transaction.
|
||
let transaction = &self.transaction;
|
||
|
||
// If transaction is not active, throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
if let IDBTransactionMode::Readonly = transaction.get_mode() {
|
||
return Err(Error::ReadOnly);
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put
|
||
fn put(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
value: HandleValue,
|
||
key: HandleValue,
|
||
overwrite: bool,
|
||
can_gc: CanGc,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be handle’s transaction.
|
||
// Step 2: Let store be this object store handle's object store.
|
||
// This is resolved in the `execute_async` function.
|
||
// Step 3: If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
// Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
|
||
self.check_readwrite_transaction_active()?;
|
||
|
||
// Step 6: If store uses in-line keys and key was given, throw a "DataError" DOMException.
|
||
if !key.is_undefined() && self.uses_inline_keys() {
|
||
return Err(Error::Data);
|
||
}
|
||
|
||
// Step 7: If store uses out-of-line keys and has no key generator
|
||
// and key was not given, throw a "DataError" DOMException.
|
||
if !self.uses_inline_keys() && !self.has_key_generator() && key.is_undefined() {
|
||
return Err(Error::Data);
|
||
}
|
||
|
||
// Step 8: If key was given, then: convert a value to a key with key
|
||
let serialized_key: Option<IndexedDBKeyType>;
|
||
|
||
if !key.is_undefined() {
|
||
serialized_key = Some(convert_value_to_key(cx, key, None)?.into_result()?);
|
||
} else {
|
||
// Step 11: We should use in-line keys instead
|
||
// Step 11.1: Let kpk be the result of running the steps to extract a
|
||
// key from a value using a key path with clone and store’s key path.
|
||
let extraction_result = self
|
||
.key_path
|
||
.as_ref()
|
||
.map(|p| extract_key(cx, value, p, None));
|
||
|
||
match extraction_result {
|
||
Some(Ok(ExtractionResult::Failure)) | None => {
|
||
// Step 11.4. Otherwise:
|
||
// Step 11.4.1. If store does not have a key generator, throw
|
||
// a "DataError" DOMException.
|
||
if !self.has_key_generator() {
|
||
return Err(Error::Data);
|
||
}
|
||
// Stept 11.4.2. Otherwise, if the steps to check that a key could
|
||
// be injected into a value with clone and store’s key path return
|
||
// false, throw a "DataError" DOMException.
|
||
// TODO
|
||
serialized_key = None;
|
||
},
|
||
// Step 11.1. Rethrow any exceptions.
|
||
Some(extraction_result) => match extraction_result? {
|
||
// Step 11.2. If kpk is invalid, throw a "DataError" DOMException.
|
||
ExtractionResult::Invalid => return Err(Error::Data),
|
||
// Step 11.3. If kpk is not failure, let key be kpk.
|
||
ExtractionResult::Key(kpk) => serialized_key = Some(kpk),
|
||
ExtractionResult::Failure => unreachable!(),
|
||
},
|
||
}
|
||
}
|
||
|
||
// Step 10. Let clone be a clone of value in targetRealm during transaction. Rethrow any exceptions.
|
||
let cloned_value = structuredclone::write(cx, value, None)?;
|
||
let Ok(serialized_value) = bincode::serialize(&cloned_value) else {
|
||
return Err(Error::InvalidState);
|
||
};
|
||
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
|
||
// Step 12. Let operation be an algorithm to run store a record into an object store with store, clone, key, and no-overwrite flag.
|
||
// Step 13. Return the result (an IDBRequest) of running asynchronously execute a request with handle and operation.
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
|
||
sender,
|
||
key: serialized_key,
|
||
value: serialized_value,
|
||
should_overwrite: overwrite,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
can_gc,
|
||
)
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-opencursor>
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor>
|
||
fn open_cursor(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
query: HandleValue,
|
||
direction: IDBCursorDirection,
|
||
key_only: bool,
|
||
can_gc: CanGc,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this object store handle's transaction.
|
||
// Step 2. Let store be this object store handle's object store.
|
||
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction is not active, throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// Step 5. Let range be the result of running the steps to convert a value to a key range
|
||
// with query. Rethrow any exceptions.
|
||
//
|
||
// The query parameter may be a key or an IDBKeyRange to use as the cursor's range. If null
|
||
// or not given, an unbounded key range is used.
|
||
let range = convert_value_to_key_range(cx, query, Some(false))?;
|
||
|
||
// Step 6. Let cursor be a new cursor with transaction set to transaction, an undefined
|
||
// position, direction set to direction, got value flag unset, and undefined key and value.
|
||
// The source of cursor is store. The range of cursor is range.
|
||
//
|
||
// NOTE: A cursor that has the key only flag unset implements the IDBCursorWithValue
|
||
// interface as well.
|
||
let cursor = if key_only {
|
||
IDBCursor::new(
|
||
&self.global(),
|
||
&self.transaction,
|
||
direction,
|
||
false,
|
||
ObjectStoreOrIndex::ObjectStore(Dom::from_ref(self)),
|
||
range.clone(),
|
||
key_only,
|
||
can_gc,
|
||
)
|
||
} else {
|
||
DomRoot::upcast(IDBCursorWithValue::new(
|
||
&self.global(),
|
||
&self.transaction,
|
||
direction,
|
||
false,
|
||
ObjectStoreOrIndex::ObjectStore(Dom::from_ref(self)),
|
||
range.clone(),
|
||
key_only,
|
||
can_gc,
|
||
))
|
||
};
|
||
|
||
// Step 7. Run the steps to asynchronously execute a request and return the IDBRequest
|
||
// created by these steps. The steps are run with this object store handle as source and
|
||
// the steps to iterate a cursor as operation, using the current Realm as targetRealm, and
|
||
// cursor.
|
||
let iteration_param = IterationParam {
|
||
cursor: Trusted::new(&cursor),
|
||
key: None,
|
||
primary_key: None,
|
||
count: None,
|
||
};
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
|
||
sender,
|
||
key_range: range,
|
||
}),
|
||
receiver,
|
||
None,
|
||
Some(iteration_param),
|
||
can_gc,
|
||
)
|
||
.inspect(|request| cursor.set_request(request))
|
||
}
|
||
}
|
||
|
||
impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put>
|
||
fn Put(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
value: HandleValue,
|
||
key: HandleValue,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
self.put(cx, value, key, true, CanGc::note())
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-add>
|
||
fn Add(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
value: HandleValue,
|
||
key: HandleValue,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
self.put(cx, value, key, false, CanGc::note())
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-delete>
|
||
fn Delete(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
// Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
|
||
self.check_readwrite_transaction_active()?;
|
||
|
||
// Step 6
|
||
// TODO: Convert to key range instead
|
||
let serialized_query = convert_value_to_key(cx, query, None)?.into_result();
|
||
// Step 7. Let operation be an algorithm to run delete records from an object store with store and range.
|
||
// Stpe 8. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem { sender, key: q }),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-clear>
|
||
fn Clear(&self) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
// Step 5. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException.
|
||
self.check_readwrite_transaction_active()?;
|
||
|
||
// Step 6. Let operation be an algorithm to run clear an object store with store.
|
||
// Stpe 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-get>
|
||
fn Get(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// Step 5. Let range be the result of converting a value to a key range with query and true. Rethrow any exceptions.
|
||
let serialized_query = convert_value_to_key_range(cx, query, None);
|
||
|
||
// Step 6. Let operation be an algorithm to run retrieve a value from an object store with the current Realm record, store, and range.
|
||
// Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
|
||
sender,
|
||
key_range: q,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey>
|
||
fn GetKey(&self, cx: SafeJSContext, query: HandleValue) -> Result<DomRoot<IDBRequest>, Error> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// 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.
|
||
let serialized_query = convert_value_to_key_range(cx, query, None);
|
||
|
||
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
|
||
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
|
||
// store as operation, using store and range.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetKey {
|
||
sender,
|
||
key_range: q,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall>
|
||
fn GetAll(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
query: HandleValue,
|
||
count: Option<u32>,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// 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.
|
||
let serialized_query = convert_value_to_key_range(cx, query, None);
|
||
|
||
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
|
||
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
|
||
// store as operation, using store and range.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
|
||
sender,
|
||
key_range: q,
|
||
count,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys>
|
||
fn GetAllKeys(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
query: HandleValue,
|
||
count: Option<u32>,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// 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.
|
||
let serialized_query = convert_value_to_key_range(cx, query, None);
|
||
|
||
// Step 6. Run the steps to asynchronously execute a request and return the IDBRequest created by these steps.
|
||
// The steps are run with this object store handle as source and the steps to retrieve a key from an object
|
||
// store as operation, using store and range.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
|
||
sender,
|
||
key_range: q,
|
||
count,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-count>
|
||
fn Count(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
|
||
// Step 1. Let transaction be this’s transaction.
|
||
// Step 2. Let store be this's object store.
|
||
// Step 3. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 4. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
// Step 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
|
||
let serialized_query = convert_value_to_key_range(cx, query, None);
|
||
|
||
// Step 6. Let operation be an algorithm to run count the records in a range with store and range.
|
||
// Step 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
|
||
let (sender, receiver) = indexed_db::create_channel(self.global());
|
||
serialized_query.and_then(|q| {
|
||
IDBRequest::execute_async(
|
||
self,
|
||
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
|
||
sender,
|
||
key_range: q,
|
||
}),
|
||
receiver,
|
||
None,
|
||
None,
|
||
CanGc::note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-opencursor>
|
||
fn OpenCursor(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
query: HandleValue,
|
||
direction: IDBCursorDirection,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
self.open_cursor(cx, query, direction, false, CanGc::note())
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-openkeycursor>
|
||
fn OpenKeyCursor(
|
||
&self,
|
||
cx: SafeJSContext,
|
||
query: HandleValue,
|
||
direction: IDBCursorDirection,
|
||
) -> Fallible<DomRoot<IDBRequest>> {
|
||
self.open_cursor(cx, query, direction, true, CanGc::note())
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-name>
|
||
fn Name(&self) -> DOMString {
|
||
self.name.borrow().clone()
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-setname>
|
||
fn SetName(&self, value: DOMString) -> ErrorResult {
|
||
// Step 2. Let transaction be this’s transaction.
|
||
let transaction = &self.transaction;
|
||
|
||
// Step 3. Let store be this's object store.
|
||
// Step 4. If store has been deleted, throw an "InvalidStateError" DOMException.
|
||
self.verify_not_deleted()?;
|
||
|
||
// Step 5. If transaction is not an upgrade transaction, throw an "InvalidStateError" DOMException.
|
||
if transaction.Mode() != IDBTransactionMode::Versionchange {
|
||
return Err(Error::InvalidState);
|
||
}
|
||
// Step 6. If transaction’s state is not active, throw a "TransactionInactiveError" DOMException.
|
||
self.check_transaction_active()?;
|
||
|
||
*self.name.borrow_mut() = value;
|
||
Ok(())
|
||
}
|
||
|
||
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-keypath
|
||
fn KeyPath(&self, cx: SafeJSContext, mut ret_val: MutableHandleValue) {
|
||
match &self.key_path {
|
||
Some(KeyPath::String(path)) => path.safe_to_jsval(cx, ret_val),
|
||
Some(KeyPath::StringSequence(paths)) => paths.safe_to_jsval(cx, ret_val),
|
||
None => ret_val.set(NullValue()),
|
||
}
|
||
}
|
||
|
||
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-indexnames
|
||
fn IndexNames(&self) -> DomRoot<DOMStringList> {
|
||
self.index_names.clone()
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction>
|
||
fn Transaction(&self) -> DomRoot<IDBTransaction> {
|
||
self.transaction()
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-autoincrement>
|
||
fn AutoIncrement(&self) -> bool {
|
||
self.has_key_generator()
|
||
}
|
||
}
|