Switch indexeddb backend to sqlite and improve IPC messaging (#38187)

- Use sqlite instead of heed. (one indexed database = one sqlite
database)
- Implement the backend for indexes
- Use keyranges where needed (as specified by the spec)
- Implement `getKey`
- Fix channel error messaging (led to a bunch of changes to how async
requests are handled)

Note: `components/net/indexeddb/engines/sqlite/serialize.rs` is unused;
I can delete it if needed.

Testing: Switching to sqlite eliminated many panics (exposing some new
failures).
Fixes: #38040

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
Ashwin Naren 2025-08-16 00:27:17 -07:00 committed by GitHub
parent f4bbdf8010
commit fc3feceee5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 2002 additions and 818 deletions

View file

@ -26,7 +26,8 @@ use crate::dom::domstringlist::DOMStringList;
use crate::dom::globalscope::GlobalScope;
use crate::dom::idbrequest::IDBRequest;
use crate::dom::idbtransaction::IDBTransaction;
use crate::indexed_db::{convert_value_to_key, extract_key};
use crate::indexed_db;
use crate::indexed_db::{convert_value_to_key, convert_value_to_key_range, extract_key};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
@ -125,9 +126,43 @@ impl IDBObjectStore {
.send(IndexedDBThreadMsg::Sync(operation))
.unwrap();
receiver.recv().unwrap()
// First unwrap for ipc
// Second unwrap will never happen unless this db gets manually deleted somehow
receiver.recv().unwrap().unwrap()
}
// fn get_stored_key_path(&mut self) -> Option<KeyPath> {
// let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
//
// let operation = SyncOperation::KeyPath(
// sender,
// self.global().origin().immutable().clone(),
// self.db_name.to_string(),
// self.name.borrow().to_string(),
// );
//
// self.global()
// .resource_threads()
// .sender()
// .send(IndexedDBThreadMsg::Sync(operation))
// .unwrap();
//
// // First unwrap for ipc
// // Second unwrap will never happen unless this db gets manually deleted somehow
// let key_path = receiver.recv().unwrap().unwrap();
// key_path.map(|p| {
// // TODO: have separate storage for string sequence of len 1 and signle string
// if p.len() == 1 {
// KeyPath::String(DOMString::from_string(p[0].clone()))
// } else {
// let strings: Vec<_> = p.into_iter().map(|s| {
// DOMString::from_string(s)
// }).collect();
// KeyPath::StringSequence(strings)
// }
// })
// }
// https://www.w3.org/TR/IndexedDB-2/#object-store-in-line-keys
fn uses_inline_keys(&self) -> bool {
self.key_path.is_some()
@ -200,30 +235,34 @@ impl IDBObjectStore {
serialized_key = convert_value_to_key(cx, key, None)?;
} else {
// Step 11: We should use in-line keys instead
if let Ok(kpk) = extract_key(
cx,
value,
self.key_path.as_ref().expect("No key path"),
None,
) {
if let Some(Ok(kpk)) = self
.key_path
.as_ref()
.map(|p| extract_key(cx, value, p, None))
{
serialized_key = kpk;
} else {
// FIXME:(rasviitanen)
// Check if store has a key generator
// Check if we can inject a key
return Err(Error::Data);
if !self.has_key_generator() {
return Err(Error::Data);
}
// FIXME:(arihant2math)
return Err(Error::NotSupported);
}
}
let serialized_value = structuredclone::write(cx, value, None)?;
let (sender, receiver) = indexed_db::create_channel(self.global());
IDBRequest::execute_async(
self,
AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem(
serialized_key,
serialized_value.serialized,
overwrite,
)),
AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender,
key: serialized_key,
value: serialized_value.serialized,
should_overwrite: overwrite,
}),
receiver,
None,
can_gc,
)
@ -262,10 +301,12 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// TODO: Convert to key range instead
let serialized_query = convert_value_to_key(cx, query, None);
// Step 7
let (sender, receiver) = indexed_db::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem(q)),
AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem { sender, key: q }),
receiver,
None,
CanGc::note(),
)
@ -279,9 +320,12 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// TODO: Step 3
// Steps 4-5
self.check_readwrite_transaction_active()?;
let (sender, receiver) = indexed_db::create_channel(self.global());
IDBRequest::execute_async(
self,
AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear),
AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(sender)),
receiver,
None,
CanGc::note(),
)
@ -295,13 +339,17 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// Step 4
self.check_transaction_active()?;
// Step 5
// TODO: Convert to key range instead
let serialized_query = convert_value_to_key(cx, query, None);
let serialized_query = convert_value_to_key_range(cx, query, None);
// Step 6
let (sender, receiver) = indexed_db::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem(q)),
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender,
key_range: q,
}),
receiver,
None,
CanGc::note(),
)
@ -309,17 +357,29 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getkey
// fn GetKey(&self, _cx: SafeJSContext, _query: HandleValue) -> DomRoot<IDBRequest> {
// // Step 1: Unneeded, handled by self.check_transaction_active()
// // TODO: Step 2
// // TODO: Step 3
// // Step 4
// self.check_transaction_active()?;
// // Step 5
// // TODO: Convert to key range instead
// let serialized_query = IDBObjectStore::convert_value_to_key(cx, query, None);
// unimplemented!();
// }
fn GetKey(&self, cx: SafeJSContext, query: HandleValue) -> Result<DomRoot<IDBRequest>, Error> {
// Step 1: Unneeded, handled by self.check_transaction_active()
// TODO: Step 2
// TODO: Step 3
// Step 4
self.check_transaction_active()?;
// Step 5
let serialized_query = convert_value_to_key_range(cx, query, None);
// Step 6
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,
CanGc::note(),
)
})
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-getall
// fn GetAll(
@ -350,13 +410,18 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
self.check_transaction_active()?;
// Step 5
let serialized_query = convert_value_to_key(cx, query, None);
let serialized_query = convert_value_to_key_range(cx, query, None);
// Step 6
let (sender, receiver) = indexed_db::create_channel(self.global());
serialized_query.and_then(|q| {
IDBRequest::execute_async(
self,
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count(q)),
AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
sender,
key_range: q,
}),
receiver,
None,
CanGc::note(),
)