Make transaction field non-null in IDBObjectStore (#38834)

In the `IDBObjectStore::new` constructor, the `transaction` field is
initialized to null, but when using this constructor, we always execute
`set_transaction` immediately afterward. Therefore, we refactored to
require the `transaction` field to be specified during construction and
thereby also removed some no longer necessary assertions.

We also updated the `transaction` field in WebIDL to remove the nullable
capability.

Testing: WPT
Fixes: #38814

---------

Signed-off-by: criskell <96352451+criskell@users.noreply.github.com>
This commit is contained in:
criskell 2025-08-22 02:10:13 -03:00 committed by GitHub
parent ede9db2e18
commit 56ce19511c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 20 additions and 17 deletions

View file

@ -244,8 +244,8 @@ impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
name.clone(), name.clone(),
Some(options), Some(options),
CanGc::note(), CanGc::note(),
&upgrade_transaction,
); );
object_store.set_transaction(&upgrade_transaction);
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();

View file

@ -19,7 +19,7 @@ use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransacti
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence; use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone; use crate::dom::bindings::structuredclone;
use crate::dom::domstringlist::DOMStringList; use crate::dom::domstringlist::DOMStringList;
@ -42,7 +42,7 @@ pub struct IDBObjectStore {
name: DomRefCell<DOMString>, name: DomRefCell<DOMString>,
key_path: Option<KeyPath>, key_path: Option<KeyPath>,
index_names: DomRoot<DOMStringList>, index_names: DomRoot<DOMStringList>,
transaction: MutNullableDom<IDBTransaction>, transaction: Dom<IDBTransaction>,
auto_increment: bool, auto_increment: bool,
// We store the db name in the object store to be able to find the correct // We store the db name in the object store to be able to find the correct
@ -57,6 +57,7 @@ impl IDBObjectStore {
name: DOMString, name: DOMString,
options: Option<&IDBObjectStoreParameters>, options: Option<&IDBObjectStoreParameters>,
can_gc: CanGc, can_gc: CanGc,
transaction: &IDBTransaction,
) -> IDBObjectStore { ) -> IDBObjectStore {
let key_path: Option<KeyPath> = match options { let key_path: Option<KeyPath> = match options {
Some(options) => options.keyPath.as_ref().map(|path| match path { Some(options) => options.keyPath.as_ref().map(|path| match path {
@ -74,7 +75,7 @@ impl IDBObjectStore {
key_path, key_path,
index_names: DOMStringList::new(global, Vec::new(), can_gc), index_names: DOMStringList::new(global, Vec::new(), can_gc),
transaction: Default::default(), transaction: Dom::from_ref(transaction),
// FIXME:(arihant2math) // FIXME:(arihant2math)
auto_increment: false, auto_increment: false,
@ -88,10 +89,16 @@ impl IDBObjectStore {
name: DOMString, name: DOMString,
options: Option<&IDBObjectStoreParameters>, options: Option<&IDBObjectStoreParameters>,
can_gc: CanGc, can_gc: CanGc,
transaction: &IDBTransaction,
) -> DomRoot<IDBObjectStore> { ) -> DomRoot<IDBObjectStore> {
reflect_dom_object( reflect_dom_object(
Box::new(IDBObjectStore::new_inherited( Box::new(IDBObjectStore::new_inherited(
global, db_name, name, options, can_gc, global,
db_name,
name,
options,
can_gc,
transaction,
)), )),
global, global,
can_gc, can_gc,
@ -102,12 +109,8 @@ impl IDBObjectStore {
self.name.borrow().clone() self.name.borrow().clone()
} }
pub fn set_transaction(&self, transaction: &IDBTransaction) { pub fn transaction(&self) -> DomRoot<IDBTransaction> {
self.transaction.set(Some(transaction)); self.transaction.as_rooted()
}
pub fn transaction(&self) -> Option<DomRoot<IDBTransaction>> {
self.transaction.get()
} }
fn has_key_generator(&self) -> bool { fn has_key_generator(&self) -> bool {
@ -171,7 +174,7 @@ impl IDBObjectStore {
/// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not. /// Checks if the transation is active, throwing a "TransactionInactiveError" DOMException if not.
fn check_transaction_active(&self) -> Fallible<()> { fn check_transaction_active(&self) -> Fallible<()> {
// Let transaction be this object store handle's transaction. // Let transaction be this object store handle's transaction.
let transaction = self.transaction.get().ok_or(Error::TransactionInactive)?; let transaction = &self.transaction;
// If transaction is not active, throw a "TransactionInactiveError" DOMException. // If transaction is not active, throw a "TransactionInactiveError" DOMException.
if !transaction.is_active() { if !transaction.is_active() {
@ -185,7 +188,7 @@ impl IDBObjectStore {
/// it then checks if the transaction is a read-only transaction, throwing a "ReadOnlyError" DOMException if so. /// it then checks if the transaction is a read-only transaction, throwing a "ReadOnlyError" DOMException if so.
fn check_readwrite_transaction_active(&self) -> Fallible<()> { fn check_readwrite_transaction_active(&self) -> Fallible<()> {
// Let transaction be this object store handle's transaction. // Let transaction be this object store handle's transaction.
let transaction = self.transaction.get().ok_or(Error::TransactionInactive)?; let transaction = &self.transaction;
// If transaction is not active, throw a "TransactionInactiveError" DOMException. // If transaction is not active, throw a "TransactionInactiveError" DOMException.
if !transaction.is_active() { if !transaction.is_active() {
@ -449,7 +452,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
// } // }
// https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction // https://www.w3.org/TR/IndexedDB-2/#dom-idbobjectstore-transaction
fn GetTransaction(&self) -> Option<DomRoot<IDBTransaction>> { fn Transaction(&self) -> DomRoot<IDBTransaction> {
self.transaction() self.transaction()
} }

View file

@ -254,7 +254,7 @@ impl IDBRequest {
T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static, T: Into<IdbResult> + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static,
{ {
// Step 1: Let transaction be the transaction associated with source. // Step 1: Let transaction be the transaction associated with source.
let transaction = source.transaction().expect("Store has no transaction"); let transaction = source.transaction();
let global = transaction.global(); let global = transaction.global();
// Step 2: Assert: transaction is active. // Step 2: Assert: transaction is active.

View file

@ -236,8 +236,8 @@ impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
name, name,
None, None,
CanGc::note(), CanGc::note(),
self,
); );
store.set_transaction(self);
Dom::from_ref(&*store) Dom::from_ref(&*store)
}); });

View file

@ -13,7 +13,7 @@ interface IDBObjectStore {
attribute DOMString name; attribute DOMString name;
// readonly attribute any keyPath; // readonly attribute any keyPath;
// readonly attribute DOMStringList indexNames; // readonly attribute DOMStringList indexNames;
[SameObject] readonly attribute IDBTransaction? transaction; [SameObject] readonly attribute IDBTransaction transaction;
readonly attribute boolean autoIncrement; readonly attribute boolean autoIncrement;
[NewObject, Throws] IDBRequest put(any value, optional any key); [NewObject, Throws] IDBRequest put(any value, optional any key);