mirror of
https://github.com/servo/servo.git
synced 2025-09-10 15:08:21 +01:00
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:
parent
e64c53972a
commit
b5d6555238
9 changed files with 308 additions and 86 deletions
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 this’s transaction.
|
// Step 1. Let transaction be this’s 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 this’s transaction.
|
// Step 1. Let transaction be this’s 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 this’s transaction.
|
// Step 1. Let transaction be this’s 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 this’s transaction.
|
// Step 1. Let transaction be this’s 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 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()?;
|
||||||
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getallkeys
|
// Step 4. If transaction’s 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 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,
|
||||||
|
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 this’s transaction.
|
// Step 1. Let transaction be this’s 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 this’s transaction.
|
// Step 2. Let transaction be this’s 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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
12
tests/wpt/meta/IndexedDB/idlharness.any.js.ini
vendored
12
tests/wpt/meta/IndexedDB/idlharness.any.js.ini
vendored
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue