indexeddb: Implement getAll and getAllKeys (#38885)

Implement getAll and getAllKeys for IDBObjectStore.

Testing: WPT & Unit testing
Fixes: Part of #6963.

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
Ashwin Naren 2025-09-05 22:48:11 -07:00 committed by GitHub
parent e64c53972a
commit b5d6555238
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 308 additions and 86 deletions

View file

@ -283,7 +283,7 @@ impl IDBObjectStore {
}
impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-put>
fn Put(
&self,
cx: SafeJSContext,
@ -293,7 +293,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
self.put(cx, value, key, true, CanGc::note())
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-add
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-add>
fn Add(
&self,
cx: SafeJSContext,
@ -303,7 +303,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
self.put(cx, value, key, false, CanGc::note())
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-delete
/// <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 thiss transaction.
// Step 2. Let store be this's object store.
@ -331,7 +331,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
})
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-clear
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-clear>
fn Clear(&self) -> Fallible<DomRoot<IDBRequest>> {
// Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store.
@ -355,7 +355,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
)
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-get
/// <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 thiss transaction.
// Step 2. Let store be this's object store.
@ -385,7 +385,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
})
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey
/// <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 thiss transaction.
// Step 2. Let store be this's object store.
@ -416,27 +416,81 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
})
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall
// fn GetAll(
// &self,
// _cx: SafeJSContext,
// _query: HandleValue,
// _count: Option<u32>,
// ) -> DomRoot<IDBRequest> {
// unimplemented!();
// }
/// <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 thiss 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()?;
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys
// fn GetAllKeys(
// &self,
// _cx: SafeJSContext,
// _query: HandleValue,
// _count: Option<u32>,
// ) -> DomRoot<IDBRequest> {
// unimplemented!();
// }
// Step 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
self.check_transaction_active()?;
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-count
// 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,
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 thiss 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 transactions 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,
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 thiss transaction.
// Step 2. Let store be this's object store.
@ -466,12 +520,12 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
})
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-name
/// <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
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-setname>
fn SetName(&self, value: DOMString) -> ErrorResult {
// Step 2. Let transaction be thiss transaction.
let transaction = &self.transaction;
@ -501,12 +555,12 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// unimplemented!();
// }
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction
/// <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
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-autoincrement>
fn AutoIncrement(&self) -> bool {
self.has_key_generator()
}

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::iter::repeat_n;
use dom_struct::dom_struct;
use ipc_channel::router::ROUTER;
@ -15,6 +16,7 @@ use net_traits::indexeddb_thread::{
IndexedDBTxnMode, PutItemResult,
};
use profile_traits::ipc::IpcReceiver;
use script_bindings::conversions::SafeToJSValConvertible;
use serde::{Deserialize, Serialize};
use stylo_atoms::Atom;
@ -45,7 +47,9 @@ struct RequestListener {
pub enum IdbResult {
Key(IndexedDBKeyType),
Data(Vec<u8>),
Keys(Vec<IndexedDBKeyType>),
Value(Vec<u8>),
Values(Vec<Vec<u8>>),
Count(u64),
Error(Error),
None,
@ -57,9 +61,21 @@ impl From<IndexedDBKeyType> for IdbResult {
}
}
impl From<Vec<IndexedDBKeyType>> for IdbResult {
fn from(value: Vec<IndexedDBKeyType>) -> Self {
IdbResult::Keys(value)
}
}
impl From<Vec<u8>> for IdbResult {
fn from(value: Vec<u8>) -> Self {
IdbResult::Data(value)
IdbResult::Value(value)
}
}
impl From<Vec<Vec<u8>>> for IdbResult {
fn from(value: Vec<Vec<u8>>) -> Self {
IdbResult::Values(value)
}
}
@ -115,7 +131,16 @@ impl RequestListener {
IdbResult::Key(key) => {
key_type_to_jsval(GlobalScope::get_cx(), &key, answer.handle_mut())
},
IdbResult::Data(serialized_data) => {
IdbResult::Keys(keys) => {
rooted_vec!(let mut array <- repeat_n(UndefinedValue(), keys.len()));
for (count, key) in keys.into_iter().enumerate() {
rooted!(in(*cx) let mut val = UndefinedValue());
key_type_to_jsval(GlobalScope::get_cx(), &key, val.handle_mut());
array[count] = val.get();
}
array.safe_to_jsval(cx, answer.handle_mut());
},
IdbResult::Value(serialized_data) => {
let result = bincode::deserialize(&serialized_data)
.map_err(|_| Error::Data)
.and_then(|data| structuredclone::read(&global, data, answer.handle_mut()));
@ -125,6 +150,24 @@ impl RequestListener {
return;
};
},
IdbResult::Values(serialized_values) => {
rooted_vec!(let mut values <- repeat_n(UndefinedValue(), serialized_values.len()));
for (count, serialized_data) in serialized_values.into_iter().enumerate() {
rooted!(in(*cx) let mut val = UndefinedValue());
let result = bincode::deserialize(&serialized_data)
.map_err(|_| Error::Data)
.and_then(|data| {
structuredclone::read(&global, data, val.handle_mut())
});
if let Err(e) = result {
warn!("Error reading structuredclone data");
Self::handle_async_request_error(&global, cx, request, e);
return;
};
values[count] = val.get();
}
values.safe_to_jsval(cx, answer.handle_mut());
},
IdbResult::Count(count) => {
answer.handle_mut().set(DoubleValue(count as f64));
},