From 567d9078a8beb3dfa3e09935fc7545575f6102b9 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sat, 25 Oct 2025 11:31:56 -0700 Subject: [PATCH 1/2] Mark IndexedDB transactions inactive during key serialization --- ...key-serialization-transaction-state.any.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 IndexedDB/key-serialization-transaction-state.any.js diff --git a/IndexedDB/key-serialization-transaction-state.any.js b/IndexedDB/key-serialization-transaction-state.any.js new file mode 100644 index 00000000000000..d264e971128bbd --- /dev/null +++ b/IndexedDB/key-serialization-transaction-state.any.js @@ -0,0 +1,60 @@ +// META: script=resources/support-promises.js +// META: title=Indexed DB transaction state during Structured Serializing +// META: timeout=long +'use strict'; + +promise_test(async testCase => { + const db = await createDatabase(testCase, database => { + database.createObjectStore('store'); + }); + + const transaction = db.transaction(['store'], 'readwrite'); + const objectStore = transaction.objectStore('store'); + + let getterCalled = false; + const activeKey = ['value that should not be used']; + Object.defineProperty(activeKey, '0', { + enumerable: true, + get: testCase.step_func(() => { + getterCalled = true; + assert_throws_dom('TransactionInactiveError', () => { + objectStore.get('key'); + }, 'transaction should not be active during key serialization'); + return 'value that should not be used'; + }), + }); + objectStore.add({}, activeKey); + await promiseForTransaction(testCase, transaction); + db.close(); + + assert_true(getterCalled, + "activeKey's getter should be called during test"); +}, 'Transaction inactive during key serialization in IDBObjectStore.add()'); + +promise_test(async testCase => { + const db = await createDatabase(testCase, database => { + database.createObjectStore('store'); + }); + + const transaction = db.transaction(['store'], 'readwrite'); + const objectStore = transaction.objectStore('store'); + + let getterCalled = false; + const activeKey = ['value that should not be used']; + Object.defineProperty(activeKey, '0', { + enumerable: true, + get: testCase.step_func(() => { + getterCalled = true; + assert_throws_dom('TransactionInactiveError', () => { + objectStore.get('key'); + }, 'transaction should not be active during key serialization'); + return 'value that should not be used'; + }), + }); + objectStore.put({}, activeKey); + await promiseForTransaction(testCase, transaction); + db.close(); + + assert_true(getterCalled, + "activeKey's getter should be called during test"); +}, 'Transaction inactive during key serialization in IDBObjectStore.put()'); From 3ffaca217a6ee144c2fb969941bb9490f426db0e Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 26 Oct 2025 19:41:03 -0700 Subject: [PATCH 2/2] Add IDBCursor continue/continuePrimaryKey --- ...key-serialization-transaction-state.any.js | 90 +++++++++++++------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/IndexedDB/key-serialization-transaction-state.any.js b/IndexedDB/key-serialization-transaction-state.any.js index d264e971128bbd..5f9dfe0467b28b 100644 --- a/IndexedDB/key-serialization-transaction-state.any.js +++ b/IndexedDB/key-serialization-transaction-state.any.js @@ -3,15 +3,23 @@ // META: timeout=long 'use strict'; -promise_test(async testCase => { +const setupKetSerializationTest = async (testCase) => { const db = await createDatabase(testCase, database => { - database.createObjectStore('store'); + const objectStore = database.createObjectStore('store'); + objectStore.createIndex('idx', 'name'); + objectStore.put({ name: 'a' }, 0); }); const transaction = db.transaction(['store'], 'readwrite'); const objectStore = transaction.objectStore('store'); + const index = objectStore.index('idx'); + + return { db, transaction, objectStore, index }; +}; +const testKeySerialization = async (db, testCase, transaction, objectStore, callback) => { let getterCalled = false; + const activeKey = ['value that should not be used']; Object.defineProperty(activeKey, '0', { enumerable: true, @@ -23,38 +31,70 @@ promise_test(async testCase => { return 'value that should not be used'; }), }); - objectStore.add({}, activeKey); + + callback(activeKey) + await promiseForTransaction(testCase, transaction); db.close(); assert_true(getterCalled, - "activeKey's getter should be called during test"); + "activeKey's getter should be called during test"); +}; + +promise_test(async testCase => { + const { db, transaction, objectStore } = await setupKetSerializationTest(testCase); + + await testKeySerialization(db, testCase, transaction, objectStore, activeKey => { + objectStore.add({}, activeKey); + }); }, 'Transaction inactive during key serialization in IDBObjectStore.add()'); promise_test(async testCase => { - const db = await createDatabase(testCase, database => { - database.createObjectStore('store'); + const { db, transaction, objectStore } = await setupKetSerializationTest(testCase); + + await testKeySerialization(db, testCase, transaction, objectStore, activeKey => { + objectStore.put({}, activeKey); }); +}, 'Transaction inactive during key serialization in IDBObjectStore.put()'); - const transaction = db.transaction(['store'], 'readwrite'); - const objectStore = transaction.objectStore('store'); +promise_test(async testCase => { + const { db, transaction, objectStore } = await setupKetSerializationTest(testCase); - let getterCalled = false; - const activeKey = ['value that should not be used']; - Object.defineProperty(activeKey, '0', { - enumerable: true, - get: testCase.step_func(() => { - getterCalled = true; - assert_throws_dom('TransactionInactiveError', () => { - objectStore.get('key'); - }, 'transaction should not be active during key serialization'); - return 'value that should not be used'; - }), + const cursor = await new Promise((resolve, reject) => { + const cursorReq = objectStore.openCursor(); + cursorReq.onerror = reject; + cursorReq.onsuccess = e => resolve(e.target.result); }); - objectStore.put({}, activeKey); - await promiseForTransaction(testCase, transaction); - db.close(); - assert_true(getterCalled, - "activeKey's getter should be called during test"); -}, 'Transaction inactive during key serialization in IDBObjectStore.put()'); + await testKeySerialization(db, testCase, transaction, objectStore, activeKey => { + cursor.continue(activeKey); + }); +}, 'Transaction inactive during key serialization in IDBCursor.continue()'); + +promise_test(async testCase => { + const { db, transaction, objectStore, index } = await setupKetSerializationTest(testCase); + + const cursor = await new Promise((resolve, reject) => { + const cursorReq = index.openCursor(); + cursorReq.onerror = reject; + cursorReq.onsuccess = e => resolve(e.target.result); + }); + + await testKeySerialization(db, testCase, transaction, objectStore, activeKey => { + cursor.continuePrimaryKey(activeKey, 0); + }); +}, 'Transaction inactive during key serialization in IDBCursor.continuePrimaryKey()'); + +promise_test(async testCase => { + const { db, transaction, objectStore, index } = await setupKetSerializationTest(testCase); + + const cursor = await new Promise((resolve, reject) => { + const cursorReq = index.openCursor(); + cursorReq.onerror = reject; + cursorReq.onsuccess = e => resolve(e.target.result); + }); + + await testKeySerialization(db, testCase, transaction, objectStore, activeKey => { + cursor.continuePrimaryKey(0, activeKey); + }); +}, 'Transaction inactive during primary key serialization in IDBCursor.continuePrimaryKey()'); \ No newline at end of file