Commit 51ad6897 authored by jsbell@chromium.org's avatar jsbell@chromium.org

IndexedDB: Ensure transactions created in microtasks are deactivated

The IDB spec defines that transactions are created 'active' and become
'inactive' when control returns to the event loop. The way this was
implemented, transactions created within microtasks (e.g. Promise
callbacks) were not deactivated.

Flip the order of microtasks and deactivation, and add tests.

BUG=380910
R=adamk@chromium.org

Committed: https://src.chromium.org/viewvc/blink?view=rev&revision=175600

Review URL: https://codereview.chromium.org/321433002

git-svn-id: svn://svn.chromium.org/blink/trunk@175737 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 3dde2144
This is a testharness.js-based test.
PASS Transactions created in microtasks are deactivated when control returns to the event loop
PASS Transactions created in microtasks remain active in subsequent microtasks
PASS Within request event dispatch, transactions remain active across microtasks
Harness: the test ran to completion.
<!DOCTYPE html>
<title>IndexedDB: Verify transaction activation behavior around microtasks</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
function idb_test(description, onUpgrade, onOpen) {
var t = async_test(description);
t.step(function() {
var dbName = 'db' + location.pathname + '-' + description;
var deleteRequest = indexedDB.deleteDatabase(dbName);
deleteRequest.onerror = t.unreached_func('deleteDatabase should not fail');
deleteRequest.onsuccess = t.step_func(function(e) {
var openRequest = indexedDB.open(dbName);
openRequest.onerror = t.unreached_func('open should not fail');
openRequest.onupgradeneeded = t.step_func(function(e) {
onUpgrade(t, openRequest.result);
});
openRequest.onsuccess = t.step_func(function(e) {
onOpen(t, openRequest.result);
});
});
});
}
function isTransactionActive(tx, storeName) {
try {
tx.objectStore(storeName).get(0);
return true;
} catch (ex) {
// The exception could be TransactionInactiveError or InvalidStateError
// depending on the test and whether the transaction has committed yet.
return false;
}
}
idb_test(
'Transactions created in microtasks are deactivated when control returns to the event loop',
function(t, db) {
db.createObjectStore('store');
},
function(t, db) {
Promise.resolve().then(t.step_func(function() {
var tx = db.transaction('store');
var request = tx.objectStore('store').get(0);
request.onerror = t.unreached_func('request should not fail');
request.onsuccess = t.step_func(function() {
assert_true(isTransactionActive(tx, 'store'),
'Transaction should be active during event dispatch');
setTimeout(t.step_func(function() {
assert_false(isTransactionActive(tx, 'store'),
'Transaction should be inactive once control returns to event loop');
t.done();
}), 0);
});
}));
}
);
idb_test(
'Transactions created in microtasks remain active in subsequent microtasks',
function(t, db) {
db.createObjectStore('store');
},
function(t, db) {
var tx;
Promise.resolve().then(function() {
tx = db.transaction('store');
assert_true(isTransactionActive(tx, 'store'),
'Transaction should be active when created');
}).then(t.step_func(function() {
assert_true(isTransactionActive(tx, 'store'),
'Transaction should be active until control returns to event loop');
setTimeout(t.step_func(function() {
assert_false(isTransactionActive(tx, 'store'),
'Transaction should be inactive once control returns to event loop');
t.done();
}), 0);
}));
}
);
idb_test(
'Within request event dispatch, transactions remain active across microtasks',
function(t, db) {
db.createObjectStore('store');
},
function(t, db) {
var tx = db.transaction('store');
var request = tx.objectStore('store').get(0);
request.onerror = t.unreached_func('request should not fail');
request.onsuccess = t.step_func(function() {
assert_true(isTransactionActive(tx, 'store'),
'Transaction should be active during event dispatch');
Promise.resolve().then(t.step_func(function() {
assert_true(isTransactionActive(tx, 'store'),
'Microtasks are run as part of event dispatch, so transaction should still be active');
setTimeout(t.step_func(function() {
assert_false(isTransactionActive(tx, 'store'),
'Transaction should be inactive once control returns to the event loop');
t.done();
}), 0);
}));
});
}
);
</script>
...@@ -38,13 +38,11 @@ namespace WebCore { ...@@ -38,13 +38,11 @@ namespace WebCore {
void V8RecursionScope::didLeaveScriptContext() void V8RecursionScope::didLeaveScriptContext()
{ {
// FIXME: Instrument any work that takes place when script exits to c++ (e.g. Mutation Observers). Microtask::performCheckpoint();
// Indexed DB requires that transactions are created with an internal |active| flag // Indexed DB requires that transactions are created with an internal |active| flag
// set to true, but the flag becomes false when control returns to the event loop. // set to true, but the flag becomes false when control returns to the event loop.
IDBPendingTransactionMonitor::from(m_executionContext).deactivateNewTransactions(); IDBPendingTransactionMonitor::from(m_executionContext).deactivateNewTransactions();
Microtask::performCheckpoint();
} }
} // namespace WebCore } // namespace WebCore
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment