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

@ -176,6 +176,55 @@ impl SqliteEngine {
Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.data)) Self::get(connection, store, key_range).map(|opt| opt.map(|model| model.data))
} }
fn get_all(
connection: &Connection,
store: object_store_model::Model,
key_range: IndexedDBKeyRange,
count: Option<u32>,
) -> Result<Vec<object_data_model::Model>, Error> {
let query = range_to_query(key_range);
let mut sql_query = sea_query::Query::select();
sql_query
.from(object_data_model::Column::Table)
.columns(vec![
object_data_model::Column::ObjectStoreId,
object_data_model::Column::Key,
object_data_model::Column::Data,
])
.and_where(query.and(Expr::col(object_data_model::Column::ObjectStoreId).is(store.id)));
if let Some(count) = count {
sql_query.limit(count as u64);
}
let (sql, values) = sql_query.build_rusqlite(SqliteQueryBuilder);
let mut stmt = connection.prepare(&sql)?;
let models = stmt
.query_and_then(&*values.as_params(), |row| {
object_data_model::Model::try_from(row)
})?
.collect::<Result<Vec<_>, _>>()?;
Ok(models)
}
fn get_all_keys(
connection: &Connection,
store: object_store_model::Model,
key_range: IndexedDBKeyRange,
count: Option<u32>,
) -> Result<Vec<Vec<u8>>, Error> {
Self::get_all(connection, store, key_range, count)
.map(|models| models.into_iter().map(|m| m.key).collect())
}
fn get_all_items(
connection: &Connection,
store: object_store_model::Model,
key_range: IndexedDBKeyRange,
count: Option<u32>,
) -> Result<Vec<Vec<u8>>, Error> {
Self::get_all(connection, store, key_range, count)
.map(|models| models.into_iter().map(|m| m.data).collect())
}
fn put_item( fn put_item(
connection: &Connection, connection: &Connection,
store: object_store_model::Model, store: object_store_model::Model,
@ -401,6 +450,37 @@ impl KvsEngine for SqliteEngine {
.map_err(|e| BackendError::DbErr(format!("{:?}", e))), .map_err(|e| BackendError::DbErr(format!("{:?}", e))),
); );
}, },
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllKeys {
sender,
key_range,
count,
}) => {
let Ok(object_store) = process_object_store(object_store, &sender) else {
continue;
};
let _ = sender.send(
Self::get_all_keys(&connection, object_store, key_range, count)
.map(|keys| {
keys.into_iter()
.map(|k| bincode::deserialize(&k).unwrap())
.collect()
})
.map_err(|e| BackendError::DbErr(format!("{:?}", e))),
);
},
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
sender,
key_range,
count,
}) => {
let Ok(object_store) = process_object_store(object_store, &sender) else {
continue;
};
let _ = sender.send(
Self::get_all_items(&connection, object_store, key_range, count)
.map_err(|e| BackendError::DbErr(format!("{:?}", e))),
);
},
AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem { AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
sender, sender,
key, key,

View file

@ -9,8 +9,9 @@ use net::indexeddb::idb_thread::IndexedDBDescription;
use net::resource_thread::CoreResourceThreadPool; use net::resource_thread::CoreResourceThreadPool;
use net_traits::indexeddb_thread::{ use net_traits::indexeddb_thread::{
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult, AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, PutItemResult,
}; };
use serde::{Deserialize, Serialize};
use servo_url::ImmutableOrigin; use servo_url::ImmutableOrigin;
use url::Host; use url::Host;
@ -183,6 +184,16 @@ fn test_delete_store() {
#[test] #[test]
fn test_async_operations() { fn test_async_operations() {
fn get_channel<T>() -> (
ipc_channel::ipc::IpcSender<T>,
ipc_channel::ipc::IpcReceiver<T>,
)
where
T: for<'de> Deserialize<'de> + Serialize,
{
ipc_channel::ipc::channel().unwrap()
}
let base_dir = tempfile::tempdir().expect("Failed to create temp dir"); let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
let thread_pool = get_pool(); let thread_pool = get_pool();
let db = SqliteEngine::new( let db = SqliteEngine::new(
@ -197,68 +208,121 @@ fn test_async_operations() {
let store_name = "test_store"; let store_name = "test_store";
db.create_store(store_name, None, false) db.create_store(store_name, None, false)
.expect("Failed to create store"); .expect("Failed to create store");
let channel = ipc_channel::ipc::channel().unwrap(); let put = get_channel();
let channel2 = ipc_channel::ipc::channel().unwrap(); let put2 = get_channel();
let channel3 = ipc_channel::ipc::channel().unwrap(); let put3 = get_channel();
let channel4 = ipc_channel::ipc::channel().unwrap(); let put_dup = get_channel();
let channel5 = ipc_channel::ipc::channel().unwrap(); let get_item_some = get_channel();
let channel6 = ipc_channel::ipc::channel().unwrap(); let get_item_none = get_channel();
let get_all_items = get_channel();
let count = get_channel();
let remove = get_channel();
let clear = get_channel();
let rx = db.process_transaction(KvsTransaction { let rx = db.process_transaction(KvsTransaction {
mode: IndexedDBTxnMode::Readwrite, mode: IndexedDBTxnMode::Readwrite,
requests: VecDeque::from(vec![ requests: VecDeque::from(vec![
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem { operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: channel.0, sender: put.0,
key: Some(IndexedDBKeyType::Number(1.0)), key: Some(IndexedDBKeyType::Number(1.0)),
value: vec![1, 2, 3], value: vec![1, 2, 3],
should_overwrite: false, should_overwrite: false,
}), }),
}, },
KvsOperation {
store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: put2.0,
key: Some(IndexedDBKeyType::String("2.0".to_string())),
value: vec![4, 5, 6],
should_overwrite: false,
}),
},
KvsOperation {
store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: put3.0,
key: Some(IndexedDBKeyType::Array(vec![
IndexedDBKeyType::String("3".to_string()),
IndexedDBKeyType::Number(0.0),
])),
value: vec![7, 8, 9],
should_overwrite: false,
}),
},
// Try to put a duplicate key without overwrite
KvsOperation {
store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: put_dup.0,
key: Some(IndexedDBKeyType::Number(1.0)),
value: vec![10, 11, 12],
should_overwrite: false,
}),
},
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem { operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel2.0, sender: get_item_some.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)), key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}), }),
}, },
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem { operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel3.0, sender: get_item_none.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)), key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
}), }),
}, },
KvsOperation {
store_name: store_name.to_owned(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
sender: get_all_items.0,
key_range: IndexedDBKeyRange::lower_bound(IndexedDBKeyType::Number(0.0), false),
count: None,
}),
},
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count { operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
sender: channel4.0, sender: count.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)), key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}), }),
}, },
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem { operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
sender: channel5.0, sender: remove.0,
key: IndexedDBKeyType::Number(1.0), key: IndexedDBKeyType::Number(1.0),
}), }),
}, },
KvsOperation { KvsOperation {
store_name: store_name.to_owned(), store_name: store_name.to_owned(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(channel6.0)), operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(clear.0)),
}, },
]), ]),
}); });
let _ = rx.blocking_recv().unwrap(); let _ = rx.blocking_recv().unwrap();
channel.1.recv().unwrap().unwrap(); put.1.recv().unwrap().unwrap();
let get_result = channel2.1.recv().unwrap(); put2.1.recv().unwrap().unwrap();
put3.1.recv().unwrap().unwrap();
let err = put_dup.1.recv().unwrap().unwrap();
assert_eq!(err, PutItemResult::CannotOverwrite);
let get_result = get_item_some.1.recv().unwrap();
let value = get_result.unwrap(); let value = get_result.unwrap();
assert_eq!(value, Some(vec![1, 2, 3])); assert_eq!(value, Some(vec![1, 2, 3]));
let get_result = channel3.1.recv().unwrap(); let get_result = get_item_none.1.recv().unwrap();
let value = get_result.unwrap(); let value = get_result.unwrap();
assert_eq!(value, None); assert_eq!(value, None);
let amount = channel4.1.recv().unwrap().unwrap(); let all_items = get_all_items.1.recv().unwrap().unwrap();
assert_eq!(all_items.len(), 3);
// Check that all three items are present
assert!(all_items.contains(&vec![1, 2, 3]));
assert!(all_items.contains(&vec![4, 5, 6]));
assert!(all_items.contains(&vec![7, 8, 9]));
let amount = count.1.recv().unwrap().unwrap();
assert_eq!(amount, 1); assert_eq!(amount, 1);
channel5.1.recv().unwrap().unwrap(); remove.1.recv().unwrap().unwrap();
channel6.1.recv().unwrap().unwrap(); clear.1.recv().unwrap().unwrap();
} }

View file

@ -283,7 +283,7 @@ impl IDBObjectStore {
} }
impl IDBObjectStoreMethods<crate::DomTypeHolder> for 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( fn Put(
&self, &self,
cx: SafeJSContext, cx: SafeJSContext,
@ -293,7 +293,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
self.put(cx, value, key, true, CanGc::note()) 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( fn Add(
&self, &self,
cx: SafeJSContext, cx: SafeJSContext,
@ -303,7 +303,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
self.put(cx, value, key, false, CanGc::note()) 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>> { fn Delete(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
// Step 1. Let transaction be thiss transaction. // Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store. // 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>> { fn Clear(&self) -> Fallible<DomRoot<IDBRequest>> {
// Step 1. Let transaction be thiss transaction. // Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store. // 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>> { fn Get(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
// Step 1. Let transaction be thiss transaction. // Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store. // 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> { fn GetKey(&self, cx: SafeJSContext, query: HandleValue) -> Result<DomRoot<IDBRequest>, Error> {
// Step 1. Let transaction be thiss transaction. // Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store. // 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 /// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall>
// fn GetAll( fn GetAll(
// &self, &self,
// _cx: SafeJSContext, cx: SafeJSContext,
// _query: HandleValue, query: HandleValue,
// _count: Option<u32>, count: Option<u32>,
// ) -> DomRoot<IDBRequest> { ) -> Fallible<DomRoot<IDBRequest>> {
// unimplemented!(); // 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 // Step 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
// fn GetAllKeys( self.check_transaction_active()?;
// &self,
// _cx: SafeJSContext,
// _query: HandleValue,
// _count: Option<u32>,
// ) -> DomRoot<IDBRequest> {
// unimplemented!();
// }
// 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>> { fn Count(&self, cx: SafeJSContext, query: HandleValue) -> Fallible<DomRoot<IDBRequest>> {
// Step 1. Let transaction be thiss transaction. // Step 1. Let transaction be thiss transaction.
// Step 2. Let store be this's object store. // 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 { fn Name(&self) -> DOMString {
self.name.borrow().clone() 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 { fn SetName(&self, value: DOMString) -> ErrorResult {
// Step 2. Let transaction be thiss transaction. // Step 2. Let transaction be thiss transaction.
let transaction = &self.transaction; let transaction = &self.transaction;
@ -501,12 +555,12 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// unimplemented!(); // 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> { fn Transaction(&self) -> DomRoot<IDBTransaction> {
self.transaction() 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 { fn AutoIncrement(&self) -> bool {
self.has_key_generator() self.has_key_generator()
} }

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell; use std::cell::Cell;
use std::iter::repeat_n;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
@ -15,6 +16,7 @@ use net_traits::indexeddb_thread::{
IndexedDBTxnMode, PutItemResult, IndexedDBTxnMode, PutItemResult,
}; };
use profile_traits::ipc::IpcReceiver; use profile_traits::ipc::IpcReceiver;
use script_bindings::conversions::SafeToJSValConvertible;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use stylo_atoms::Atom; use stylo_atoms::Atom;
@ -45,7 +47,9 @@ struct RequestListener {
pub enum IdbResult { pub enum IdbResult {
Key(IndexedDBKeyType), Key(IndexedDBKeyType),
Data(Vec<u8>), Keys(Vec<IndexedDBKeyType>),
Value(Vec<u8>),
Values(Vec<Vec<u8>>),
Count(u64), Count(u64),
Error(Error), Error(Error),
None, 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 { impl From<Vec<u8>> for IdbResult {
fn from(value: Vec<u8>) -> Self { 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) => { IdbResult::Key(key) => {
key_type_to_jsval(GlobalScope::get_cx(), &key, answer.handle_mut()) 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) let result = bincode::deserialize(&serialized_data)
.map_err(|_| Error::Data) .map_err(|_| Error::Data)
.and_then(|data| structuredclone::read(&global, data, answer.handle_mut())); .and_then(|data| structuredclone::read(&global, data, answer.handle_mut()));
@ -125,6 +150,24 @@ impl RequestListener {
return; 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) => { IdbResult::Count(count) => {
answer.handle_mut().set(DoubleValue(count as f64)); answer.handle_mut().set(DoubleValue(count as f64));
}, },

View file

@ -22,10 +22,10 @@ interface IDBObjectStore {
[NewObject, Throws] IDBRequest clear(); [NewObject, Throws] IDBRequest clear();
[NewObject, Throws] IDBRequest get(any query); [NewObject, Throws] IDBRequest get(any query);
[NewObject, Throws] IDBRequest getKey(any query); [NewObject, Throws] IDBRequest getKey(any query);
// [NewObject] IDBRequest getAll(optional any query, [NewObject, Throws] IDBRequest getAll(optional any query,
// optional [EnforceRange] unsigned long count); optional [EnforceRange] unsigned long count);
// [NewObject] IDBRequest getAllKeys(optional any query, [NewObject, Throws] IDBRequest getAllKeys(optional any query,
// optional [EnforceRange] unsigned long count); optional [EnforceRange] unsigned long count);
[NewObject, Throws] IDBRequest count(optional any query); [NewObject, Throws] IDBRequest count(optional any query);
// [NewObject] IDBRequest openCursor(optional any query, // [NewObject] IDBRequest openCursor(optional any query,

View file

@ -248,7 +248,7 @@ fn test_as_singleton() {
assert!(full_range.as_singleton().is_none()); assert!(full_range.as_singleton().is_none());
} }
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum PutItemResult { pub enum PutItemResult {
Success, Success,
CannotOverwrite, CannotOverwrite,
@ -266,6 +266,17 @@ pub enum AsyncReadOnlyOperation {
key_range: IndexedDBKeyRange, key_range: IndexedDBKeyRange,
}, },
GetAllKeys {
sender: IpcSender<BackendResult<Vec<IndexedDBKeyType>>>,
key_range: IndexedDBKeyRange,
count: Option<u32>,
},
GetAllItems {
sender: IpcSender<BackendResult<Vec<Vec<u8>>>>,
key_range: IndexedDBKeyRange,
count: Option<u32>,
},
Count { Count {
sender: IpcSender<BackendResult<u64>>, sender: IpcSender<BackendResult<u64>>,
key_range: IndexedDBKeyRange, key_range: IndexedDBKeyRange,

View file

@ -12,12 +12,6 @@
[Cross-realm IDBObjectStore::clear() method from detached <iframe> works as expected] [Cross-realm IDBObjectStore::clear() method from detached <iframe> works as expected]
expected: FAIL expected: FAIL
[Cross-realm IDBObjectStore::getAll() method from detached <iframe> works as expected]
expected: FAIL
[Cross-realm IDBObjectStore::getAllKeys() method from detached <iframe> works as expected]
expected: FAIL
[Cross-realm IDBObjectStore::count() method from detached <iframe> works as expected] [Cross-realm IDBObjectStore::count() method from detached <iframe> works as expected]
expected: FAIL expected: FAIL

View file

@ -2,15 +2,9 @@
[IDBObjectStore.get exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.get exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL
[IDBObjectStore.getAll exception order: InvalidStateError vs. TransactionInactiveError]
expected: FAIL
[IDBObjectStore.getAll exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.getAll exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL
[IDBObjectStore.getAllKeys exception order: InvalidStateError vs. TransactionInactiveError]
expected: FAIL
[IDBObjectStore.getAllKeys exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.getAllKeys exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL
@ -37,15 +31,9 @@
[IDBObjectStore.get exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.get exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL
[IDBObjectStore.getAll exception order: InvalidStateError vs. TransactionInactiveError]
expected: FAIL
[IDBObjectStore.getAll exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.getAll exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL
[IDBObjectStore.getAllKeys exception order: InvalidStateError vs. TransactionInactiveError]
expected: FAIL
[IDBObjectStore.getAllKeys exception order: TransactionInactiveError vs. DataError] [IDBObjectStore.getAllKeys exception order: TransactionInactiveError vs. DataError]
expected: FAIL expected: FAIL

View file

@ -11,12 +11,6 @@
[IDBObjectStore interface: attribute indexNames] [IDBObjectStore interface: attribute indexNames]
expected: FAIL expected: FAIL
[IDBObjectStore interface: operation getAll(optional any, optional unsigned long)]
expected: FAIL
[IDBObjectStore interface: operation getAllKeys(optional any, optional unsigned long)]
expected: FAIL
[IDBObjectStore interface: operation openCursor(optional any, optional IDBCursorDirection)] [IDBObjectStore interface: operation openCursor(optional any, optional IDBCursorDirection)]
expected: FAIL expected: FAIL
@ -127,12 +121,6 @@
[IDBObjectStore interface: attribute indexNames] [IDBObjectStore interface: attribute indexNames]
expected: FAIL expected: FAIL
[IDBObjectStore interface: operation getAll(optional any, optional unsigned long)]
expected: FAIL
[IDBObjectStore interface: operation getAllKeys(optional any, optional unsigned long)]
expected: FAIL
[IDBObjectStore interface: operation openCursor(optional any, optional IDBCursorDirection)] [IDBObjectStore interface: operation openCursor(optional any, optional IDBCursorDirection)]
expected: FAIL expected: FAIL