mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
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:
parent
f4bbdf8010
commit
fc3feceee5
59 changed files with 2002 additions and 818 deletions
|
@ -7,7 +7,7 @@ use std::cell::Cell;
|
|||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use net_traits::IpcSend;
|
||||
use net_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation};
|
||||
use net_traits::indexeddb_thread::{IndexedDBThreadMsg, KeyPath, SyncOperation};
|
||||
use profile_traits::ipc;
|
||||
use stylo_atoms::Atom;
|
||||
|
||||
|
@ -104,7 +104,10 @@ impl IDBDatabase {
|
|||
.get_idb_thread()
|
||||
.send(IndexedDBThreadMsg::Sync(operation));
|
||||
|
||||
receiver.recv().unwrap()
|
||||
receiver.recv().unwrap().unwrap_or_else(|e| {
|
||||
error!("{e:?}");
|
||||
u64::MAX
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_transaction(&self, transaction: &IDBTransaction) {
|
||||
|
@ -185,7 +188,6 @@ impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
|
|||
name: DOMString,
|
||||
options: &IDBObjectStoreParameters,
|
||||
) -> Fallible<DomRoot<IDBObjectStore>> {
|
||||
// FIXME:(arihant2math) ^^ Change idl to match above.
|
||||
// Step 2
|
||||
let upgrade_transaction = match self.upgrade_transaction.get() {
|
||||
Some(txn) => txn,
|
||||
|
@ -247,11 +249,18 @@ impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
|
|||
|
||||
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
||||
|
||||
let key_paths = key_path.map(|p| match p {
|
||||
StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
|
||||
StringOrStringSequence::StringSequence(s) => {
|
||||
KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
|
||||
},
|
||||
});
|
||||
let operation = SyncOperation::CreateObjectStore(
|
||||
sender,
|
||||
self.global().origin().immutable().clone(),
|
||||
self.name.to_string(),
|
||||
name.to_string(),
|
||||
key_paths,
|
||||
auto_increment,
|
||||
);
|
||||
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ use ipc_channel::router::ROUTER;
|
|||
use js::jsval::UndefinedValue;
|
||||
use js::rust::HandleValue;
|
||||
use net_traits::IpcSend;
|
||||
use net_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation};
|
||||
use net_traits::indexeddb_thread::{BackendResult, IndexedDBThreadMsg, SyncOperation};
|
||||
use profile_traits::ipc;
|
||||
use script_bindings::conversions::SafeToJSValConvertible;
|
||||
use stylo_atoms::Atom;
|
||||
|
@ -84,7 +84,7 @@ impl OpenRequestListener {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_delete_db(&self, result: Result<(), ()>, can_gc: CanGc) {
|
||||
fn handle_delete_db(&self, result: BackendResult<()>, can_gc: CanGc) {
|
||||
let open_request = self.open_request.root();
|
||||
let global = open_request.global();
|
||||
open_request.idbrequest.set_ready_state_done();
|
||||
|
@ -108,7 +108,7 @@ impl OpenRequestListener {
|
|||
event.fire(open_request.upcast(), can_gc);
|
||||
},
|
||||
Err(_e) => {
|
||||
// FIXME(rasviitanen) Set the error of request to the
|
||||
// FIXME(arihant2math) Set the error of request to the
|
||||
// appropriate error
|
||||
|
||||
let event = Event::new(
|
||||
|
|
|
@ -8,13 +8,15 @@ use constellation_traits::StructuredSerializedData;
|
|||
use dom_struct::dom_struct;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::Heap;
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use js::jsval::{DoubleValue, JSVal, UndefinedValue};
|
||||
use js::rust::HandleValue;
|
||||
use net_traits::IpcSend;
|
||||
use net_traits::indexeddb_thread::{
|
||||
AsyncOperation, IdbResult, IndexedDBThreadMsg, IndexedDBTxnMode,
|
||||
AsyncOperation, BackendResult, IndexedDBKeyType, IndexedDBThreadMsg, IndexedDBTxnMode,
|
||||
PutItemResult,
|
||||
};
|
||||
use profile_traits::ipc;
|
||||
use profile_traits::ipc::IpcReceiver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stylo_atoms::Atom;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
|
||||
|
@ -42,8 +44,61 @@ struct RequestListener {
|
|||
request: Trusted<IDBRequest>,
|
||||
}
|
||||
|
||||
pub enum IdbResult {
|
||||
Key(IndexedDBKeyType),
|
||||
Data(Vec<u8>),
|
||||
Count(u64),
|
||||
Error(Error),
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<IndexedDBKeyType> for IdbResult {
|
||||
fn from(value: IndexedDBKeyType) -> Self {
|
||||
IdbResult::Key(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for IdbResult {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
IdbResult::Data(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PutItemResult> for IdbResult {
|
||||
fn from(value: PutItemResult) -> Self {
|
||||
match value {
|
||||
PutItemResult::Success => Self::None,
|
||||
PutItemResult::CannotOverwrite => Self::Error(Error::Constraint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for IdbResult {
|
||||
fn from(_value: ()) -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Option<T>> for IdbResult
|
||||
where
|
||||
T: Into<IdbResult>,
|
||||
{
|
||||
fn from(value: Option<T>) -> Self {
|
||||
match value {
|
||||
Some(value) => value.into(),
|
||||
None => IdbResult::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for IdbResult {
|
||||
fn from(value: u64) -> Self {
|
||||
IdbResult::Count(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestListener {
|
||||
fn handle_async_request_finished(&self, result: Result<Option<IdbResult>, ()>) {
|
||||
fn handle_async_request_finished(&self, result: BackendResult<IdbResult>) {
|
||||
let request = self.request.root();
|
||||
let global = request.global();
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
@ -53,7 +108,7 @@ impl RequestListener {
|
|||
let _ac = enter_realm(&*request);
|
||||
rooted!(in(*cx) let mut answer = UndefinedValue());
|
||||
|
||||
if let Ok(Some(data)) = result {
|
||||
if let Ok(data) = result {
|
||||
match data {
|
||||
IdbResult::Key(key) => {
|
||||
key_type_to_jsval(GlobalScope::get_cx(), &key, answer.handle_mut())
|
||||
|
@ -68,6 +123,17 @@ impl RequestListener {
|
|||
warn!("Error reading structuredclone data");
|
||||
}
|
||||
},
|
||||
IdbResult::Count(count) => {
|
||||
answer.handle_mut().set(DoubleValue(count as f64));
|
||||
},
|
||||
IdbResult::None => {
|
||||
// no-op
|
||||
},
|
||||
IdbResult::Error(_err) => {
|
||||
request.set_result(answer.handle());
|
||||
Self::handle_async_request_error(request, &global);
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
||||
request.set_result(answer.handle());
|
||||
|
@ -92,30 +158,33 @@ impl RequestListener {
|
|||
transaction.set_active_flag(false);
|
||||
} else {
|
||||
request.set_result(answer.handle());
|
||||
|
||||
// FIXME:(rasviitanen)
|
||||
// Set the error of request to result
|
||||
|
||||
let transaction = request
|
||||
.transaction
|
||||
.get()
|
||||
.expect("Request has no transaction");
|
||||
|
||||
let event = Event::new(
|
||||
&global,
|
||||
Atom::from("error"),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::Cancelable,
|
||||
CanGc::note(),
|
||||
);
|
||||
|
||||
transaction.set_active_flag(true);
|
||||
event
|
||||
.upcast::<Event>()
|
||||
.fire(request.upcast(), CanGc::note());
|
||||
transaction.set_active_flag(false);
|
||||
Self::handle_async_request_error(request, &global);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_async_request_error(request: DomRoot<IDBRequest>, global: &GlobalScope) {
|
||||
// FIXME:(rasviitanen)
|
||||
// Set the error of request to result
|
||||
|
||||
let transaction = request
|
||||
.transaction
|
||||
.get()
|
||||
.expect("Request has no transaction");
|
||||
|
||||
let event = Event::new(
|
||||
global,
|
||||
Atom::from("error"),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::Cancelable,
|
||||
CanGc::note(),
|
||||
);
|
||||
|
||||
transaction.set_active_flag(true);
|
||||
event
|
||||
.upcast::<Event>()
|
||||
.fire(request.upcast(), CanGc::note());
|
||||
transaction.set_active_flag(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -174,12 +243,16 @@ impl IDBRequest {
|
|||
}
|
||||
|
||||
// https://www.w3.org/TR/IndexedDB-2/#asynchronously-execute-a-request
|
||||
pub fn execute_async(
|
||||
pub fn execute_async<T>(
|
||||
source: &IDBObjectStore,
|
||||
operation: AsyncOperation,
|
||||
receiver: IpcReceiver<BackendResult<T>>,
|
||||
request: Option<DomRoot<IDBRequest>>,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<DomRoot<IDBRequest>> {
|
||||
) -> Fallible<DomRoot<IDBRequest>>
|
||||
where
|
||||
T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
|
||||
{
|
||||
// Step 1: Let transaction be the transaction associated with source.
|
||||
let transaction = source.transaction().expect("Store has no transaction");
|
||||
let global = transaction.global();
|
||||
|
@ -208,10 +281,6 @@ impl IDBRequest {
|
|||
IDBTransactionMode::Versionchange => IndexedDBTxnMode::Versionchange,
|
||||
};
|
||||
|
||||
let (sender, receiver) =
|
||||
ipc::channel::<Result<Option<IdbResult>, ()>>(global.time_profiler_chan().clone())
|
||||
.unwrap();
|
||||
|
||||
let response_listener = RequestListener {
|
||||
request: Trusted::new(&request),
|
||||
};
|
||||
|
@ -227,7 +296,7 @@ impl IDBRequest {
|
|||
let response_listener = response_listener.clone();
|
||||
task_source.queue(task!(request_callback: move || {
|
||||
response_listener.handle_async_request_finished(
|
||||
message.expect("Could not unwrap message"));
|
||||
message.expect("Could not unwrap message").map(|t| t.into()));
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
@ -237,7 +306,6 @@ impl IDBRequest {
|
|||
.resource_threads()
|
||||
.sender()
|
||||
.send(IndexedDBThreadMsg::Async(
|
||||
sender,
|
||||
global.origin().immutable().clone(),
|
||||
transaction.get_db_name().to_string(),
|
||||
source.get_name().to_string(),
|
||||
|
|
|
@ -229,6 +229,7 @@ impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
|
|||
// returns the same IDBObjectStore instance.
|
||||
let mut store_handles = self.store_handles.borrow_mut();
|
||||
let store = store_handles.entry(name.to_string()).or_insert_with(|| {
|
||||
// TODO: get key path from backend
|
||||
let store = IDBObjectStore::new(
|
||||
&self.global(),
|
||||
self.db.get_name(),
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use std::iter::repeat;
|
||||
use std::ptr;
|
||||
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use js::conversions::jsstr_to_string;
|
||||
use js::gc::MutableHandle;
|
||||
use js::jsapi::{
|
||||
|
@ -14,17 +15,31 @@ use js::jsapi::{
|
|||
};
|
||||
use js::jsval::{DoubleValue, UndefinedValue};
|
||||
use js::rust::{HandleValue, MutableHandleValue};
|
||||
use net_traits::indexeddb_thread::{IndexedDBKeyRange, IndexedDBKeyType};
|
||||
use net_traits::indexeddb_thread::{BackendResult, IndexedDBKeyRange, IndexedDBKeyType};
|
||||
use profile_traits::ipc;
|
||||
use profile_traits::ipc::IpcReceiver;
|
||||
use script_bindings::conversions::{SafeToJSValConvertible, root_from_object};
|
||||
use script_bindings::root::DomRoot;
|
||||
use script_bindings::str::DOMString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
|
||||
use crate::dom::bindings::error::Error;
|
||||
use crate::dom::bindings::import::module::SafeJSContext;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::idbkeyrange::IDBKeyRange;
|
||||
use crate::dom::idbobjectstore::KeyPath;
|
||||
|
||||
pub fn create_channel<T>(
|
||||
global: DomRoot<GlobalScope>,
|
||||
) -> (IpcSender<BackendResult<T>>, IpcReceiver<BackendResult<T>>)
|
||||
where
|
||||
T: for<'a> Deserialize<'a> + Serialize,
|
||||
{
|
||||
ipc::channel::<BackendResult<T>>(global.time_profiler_chan().clone()).unwrap()
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/IndexedDB-2/#convert-key-to-value
|
||||
#[allow(unsafe_code)]
|
||||
pub fn key_type_to_jsval(
|
||||
|
@ -147,7 +162,6 @@ pub fn convert_value_to_key(
|
|||
|
||||
// https://www.w3.org/TR/IndexedDB-2/#convert-a-value-to-a-key-range
|
||||
#[allow(unsafe_code)]
|
||||
#[expect(unused)]
|
||||
pub fn convert_value_to_key_range(
|
||||
cx: SafeJSContext,
|
||||
input: HandleValue,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue