script: Ensure autoincrement and keypath are passed in correctly from IDBTransaction (#38738)

Previously, the correct autoincremented and keypath parameters were only
being passed if the object store is being created. This PR queries this
info from the backend and passes it onto the constructor in
IDBTransaction. Furthermore it exposes keypath and index_names from
IDBObjectStore, mainly for WPT.

Testing: WPT
Fixes: None

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
Ashwin Naren 2025-09-11 02:13:15 -07:00 committed by GitHub
parent 722b0de8d8
commit 97690b1cba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 84 additions and 170 deletions

View file

@ -3,6 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use js::gc::MutableHandleValue;
use js::jsval::NullValue;
use js::rust::HandleValue;
use net_traits::IpcSend;
use net_traits::indexeddb_thread::{
@ -10,6 +12,7 @@ use net_traits::indexeddb_thread::{
IndexedDBThreadMsg, SyncOperation,
};
use profile_traits::ipc;
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::error::ErrorResult;
use crate::dom::bindings::cell::DomRefCell;
@ -34,7 +37,7 @@ use crate::indexed_db::{
};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub enum KeyPath {
String(DOMString),
StringSequence(Vec<DOMString>),
@ -133,38 +136,6 @@ impl IDBObjectStore {
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()
@ -546,14 +517,18 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-keypath
// fn KeyPath(&self, _cx: SafeJSContext, _val: MutableHandleValue) {
// unimplemented!();
// }
fn KeyPath(&self, cx: SafeJSContext, mut ret_val: MutableHandleValue) {
match &self.key_path {
Some(KeyPath::String(path)) => path.safe_to_jsval(cx, ret_val),
Some(KeyPath::StringSequence(paths)) => paths.safe_to_jsval(cx, ret_val),
None => ret_val.set(NullValue()),
}
}
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-indexnames
// fn IndexNames(&self) -> DomRoot<DOMStringList> {
// unimplemented!();
// }
fn IndexNames(&self) -> DomRoot<DOMStringList> {
self.index_names.clone()
}
/// <https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction>
fn Transaction(&self) -> DomRoot<IDBTransaction> {

View file

@ -8,12 +8,14 @@ use std::collections::HashMap;
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 script_bindings::codegen::GenericUnionTypes::StringOrStringSequence;
use stylo_atoms::Atom;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DOMStringListBinding::DOMStringListMethods;
use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::{
IDBTransactionMethods, IDBTransactionMode,
};
@ -203,6 +205,52 @@ impl IDBTransaction {
fn get_idb_thread(&self) -> IpcSender<IndexedDBThreadMsg> {
self.global().resource_threads().sender()
}
fn object_store_parameters(
&self,
object_store_name: &DOMString,
) -> Option<IDBObjectStoreParameters> {
let global = self.global();
let idb_sender = global.resource_threads().sender();
let (sender, receiver) =
ipc::channel(global.time_profiler_chan().clone()).expect("failed to create channel");
let origin = global.origin().immutable().clone();
let db_name = self.db.get_name().to_string();
let object_store_name = object_store_name.to_string();
let operation = SyncOperation::HasKeyGenerator(
sender,
origin.clone(),
db_name.clone(),
object_store_name.clone(),
);
let _ = idb_sender.send(IndexedDBThreadMsg::Sync(operation));
// First unwrap for ipc
// Second unwrap will never happen unless this db gets manually deleted somehow
let auto_increment = receiver.recv().ok()?.ok()?;
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).ok()?;
let operation = SyncOperation::KeyPath(sender, origin, db_name, object_store_name);
let _ = idb_sender.send(IndexedDBThreadMsg::Sync(operation));
// First unwrap for ipc
// Second unwrap will never happen unless this db gets manually deleted somehow
let key_path = receiver.recv().unwrap().ok()?;
let key_path = key_path.map(|key_path| match key_path {
KeyPath::String(s) => StringOrStringSequence::String(DOMString::from_string(s)),
KeyPath::Sequence(seq) => StringOrStringSequence::StringSequence(
seq.into_iter().map(DOMString::from_string).collect(),
),
});
Some(IDBObjectStoreParameters {
autoIncrement: auto_increment,
keyPath: key_path,
})
}
}
impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
@ -213,7 +261,7 @@ impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
// https://www.w3.org/TR/IndexedDB-2/#dom-idbtransaction-objectstore
fn ObjectStore(&self, name: DOMString) -> Fallible<DomRoot<IDBObjectStore>> {
// Step 1: Handle the case where transaction has finised
// Step 1: If transaction has finished, throw an "InvalidStateError" DOMException.
if self.finished.get() {
return Err(Error::InvalidState);
}
@ -228,12 +276,12 @@ 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 parameters = self.object_store_parameters(&name);
let store = IDBObjectStore::new(
&self.global(),
self.db.get_name(),
name,
None,
parameters.as_ref(),
CanGc::note(),
self,
);

View file

@ -11,8 +11,8 @@
[Pref="dom_indexeddb_enabled", Exposed=(Window,Worker)]
interface IDBObjectStore {
[SetterThrows] attribute DOMString name;
// readonly attribute any keyPath;
// readonly attribute DOMStringList indexNames;
readonly attribute any keyPath;
readonly attribute DOMStringList indexNames;
[SameObject] readonly attribute IDBTransaction transaction;
readonly attribute boolean autoIncrement;