LibWeb: Fire the "blocked" event at the IDB open request
...rather than at each of the open connections. These disagreed with the spec steps written right above them. The transaction-lifetime.any.html and idbdatabase_close.any.html tests pass instead of timing out with these changes, so they've been imported.
This commit is contained in:
parent
5f114d730b
commit
5eeac1b9dd
7 changed files with 224 additions and 13 deletions
|
|
@ -159,12 +159,17 @@ void open_a_database_connection(JS::Realm& realm, StorageAPI::StorageKey storage
|
|||
auto after_all = GC::create_function(realm.heap(), [&realm, &queue, open_connections, db, version, connection, request, on_complete] {
|
||||
// 4. If any of the connections in openConnections are still not closed,
|
||||
// queue a database task to fire a version change event named blocked at request with db’s version and version.
|
||||
for (auto const& entry : open_connections->elements()) {
|
||||
if (entry->state() != ConnectionState::Closed) {
|
||||
queue_a_database_task(GC::create_function(realm.vm().heap(), [&realm, entry, db, version]() {
|
||||
fire_a_version_change_event(realm, HTML::EventNames::blocked, *entry, db->version(), version);
|
||||
}));
|
||||
auto any_connection_is_not_closed = [&] {
|
||||
for (auto const& entry : open_connections->elements()) {
|
||||
if (entry->state() != ConnectionState::Closed)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (any_connection_is_not_closed) {
|
||||
queue_a_database_task(GC::create_function(realm.vm().heap(), [&realm, request, db, version]() {
|
||||
fire_a_version_change_event(realm, HTML::EventNames::blocked, request, db->version(), version);
|
||||
}));
|
||||
}
|
||||
|
||||
// 5. Wait until all connections in openConnections are closed.
|
||||
|
|
@ -482,7 +487,7 @@ void delete_a_database(JS::Realm& realm, StorageAPI::StorageKey storage_key, Str
|
|||
|
||||
// 2. Add request to queue.
|
||||
// 3. Wait until all previous requests in queue have been processed.
|
||||
queue.enqueue(request, GC::create_function(realm.heap(), [&realm, &queue, storage_key = move(storage_key), name = move(name), on_complete] -> void {
|
||||
queue.enqueue(request, GC::create_function(realm.heap(), [&realm, &queue, storage_key = move(storage_key), name = move(name), request, on_complete] -> void {
|
||||
static constexpr auto call_completion = [](auto& queue, auto completion, auto result) {
|
||||
completion->function()(move(result));
|
||||
queue.on_request_processed();
|
||||
|
|
@ -523,14 +528,20 @@ void delete_a_database(JS::Realm& realm, StorageAPI::StorageKey storage_key, Str
|
|||
dbgln("remaining tasks: {}", task_counter_state ? task_counter_state->remaining_tasks : 0);
|
||||
}
|
||||
|
||||
auto after_all = GC::create_function(realm.heap(), [&realm, &queue, open_connections, db, storage_key, name, on_complete] {
|
||||
// 8. If any of the connections in openConnections are still not closed, queue a database task to fire a version change event named blocked at request with db’s version and null.
|
||||
for (auto const& entry : open_connections->elements()) {
|
||||
if (entry->state() != ConnectionState::Closed) {
|
||||
queue_a_database_task(GC::create_function(realm.vm().heap(), [&realm, entry, db]() {
|
||||
fire_a_version_change_event(realm, HTML::EventNames::blocked, *entry, db->version(), {});
|
||||
}));
|
||||
auto after_all = GC::create_function(realm.heap(), [&realm, &queue, open_connections, db, storage_key, name, request, on_complete] {
|
||||
// 8. If any of the connections in openConnections are still not closed, queue a database task to fire a
|
||||
// version change event named blocked at request with db’s version and null.
|
||||
auto any_connection_is_not_closed = [&] {
|
||||
for (auto const& entry : open_connections->elements()) {
|
||||
if (entry->state() != ConnectionState::Closed)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (any_connection_is_not_closed) {
|
||||
queue_a_database_task(GC::create_function(realm.vm().heap(), [&realm, request, db]() {
|
||||
fire_a_version_change_event(realm, HTML::EventNames::blocked, request, db->version(), {});
|
||||
}));
|
||||
}
|
||||
|
||||
// 9. Wait until all connections in openConnections are closed.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 2 tests
|
||||
|
||||
2 Pass
|
||||
Pass Unblock the version change transaction created by an open database request
|
||||
Pass Unblock the delete database request.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 2 tests
|
||||
|
||||
2 Pass
|
||||
Pass No Blocked event
|
||||
Pass Blocked event
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>IDBDatabase.close()</title>
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../resources/testharness.js"></script>
|
||||
<script src="../resources/testharnessreport.js"></script>
|
||||
<script src="resources/support.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../IndexedDB/idbdatabase_close.any.js"></script>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// META: global=window,worker
|
||||
// META: title=IDBDatabase.close()
|
||||
// META: script=resources/support.js
|
||||
|
||||
// Spec: https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction
|
||||
|
||||
'use strict';
|
||||
|
||||
async_test(t => {
|
||||
let db;
|
||||
let versionchange_fired;
|
||||
let blocked_fired;
|
||||
let upgradeneeded_fired;
|
||||
const open_rq = createdb(t);
|
||||
let counter = 0;
|
||||
|
||||
open_rq.onupgradeneeded = function() {};
|
||||
open_rq.onsuccess = function(e) {
|
||||
db = e.target.result;
|
||||
db.onversionchange = t.step_func((e) => {
|
||||
versionchange_fired = counter++;
|
||||
});
|
||||
const rq = indexedDB.open(db.name, db.version + 1);
|
||||
rq.onblocked = t.step_func((e) => {
|
||||
blocked_fired = counter++;
|
||||
db.close();
|
||||
});
|
||||
rq.onupgradeneeded = t.step_func((e) => {
|
||||
upgradeneeded_fired = counter++;
|
||||
});
|
||||
rq.onsuccess = t.step_func((e) => {
|
||||
assert_equals(versionchange_fired, 0, 'versionchange event fired #');
|
||||
assert_equals(blocked_fired, 1, 'block event fired #');
|
||||
assert_equals(
|
||||
upgradeneeded_fired, 2, 'second upgradeneeded event fired #');
|
||||
|
||||
rq.result.close();
|
||||
t.done();
|
||||
});
|
||||
rq.onerror = t.step_func(e => {
|
||||
assert_unreached('Unexpected database deletion error: ' + e.target.error);
|
||||
});
|
||||
};
|
||||
}, 'Unblock the version change transaction created by an open database request');
|
||||
|
||||
async_test(t => {
|
||||
let db;
|
||||
let blocked_fired = false;
|
||||
let versionchange_fired = false;
|
||||
const open_rq = createdb(t);
|
||||
|
||||
open_rq.onupgradeneeded = t.step_func(e => {});
|
||||
open_rq.onsuccess = t.step_func(e => {
|
||||
db = e.target.result;
|
||||
|
||||
db.onversionchange = t.step_func(e => {
|
||||
versionchange_fired = true;
|
||||
});
|
||||
|
||||
const rq = indexedDB.deleteDatabase(db.name);
|
||||
rq.onblocked = t.step_func(e => {
|
||||
blocked_fired = true;
|
||||
db.close();
|
||||
});
|
||||
rq.onsuccess = t.step_func(e => {
|
||||
assert_true(versionchange_fired, 'versionchange event fired')
|
||||
assert_true(blocked_fired, 'block event fired')
|
||||
t.done();
|
||||
});
|
||||
rq.onerror = t.step_func(e => {
|
||||
assert_unreached('Unexpected database deletion error: ' + e.target.error);
|
||||
});
|
||||
});
|
||||
}, 'Unblock the delete database request.');
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Event order when opening a second database when one connection is open already</title>
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../resources/testharness.js"></script>
|
||||
<script src="../resources/testharnessreport.js"></script>
|
||||
<script src="resources/support.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../IndexedDB/transaction-lifetime.any.js"></script>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// META: title=Event order when opening a second database when one connection is open already
|
||||
// META: global=window,worker
|
||||
// META: script=resources/support.js
|
||||
|
||||
'use strict';
|
||||
|
||||
async function setupDatabase(t, dbname, version) {
|
||||
indexedDB.deleteDatabase(dbname);
|
||||
|
||||
const openrq = indexedDB.open(dbname, version);
|
||||
const eventWatcher = new EventWatcher(
|
||||
t, openrq, ['upgradeneeded', 'error', 'blocked', 'success']);
|
||||
|
||||
let event = await eventWatcher.wait_for('upgradeneeded');
|
||||
const db = event.target.result;
|
||||
db.createObjectStore('store');
|
||||
|
||||
await eventWatcher.wait_for('success');
|
||||
return db;
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const dbname = location + '-' + t.name;
|
||||
const version = 3;
|
||||
const db = await setupDatabase(t, dbname, version);
|
||||
let db2;
|
||||
|
||||
t.add_cleanup(() => {
|
||||
if (db2)
|
||||
db2.close();
|
||||
if (db)
|
||||
db.close();
|
||||
indexedDB.deleteDatabase(dbname);
|
||||
});
|
||||
|
||||
const dbWatcher = new EventWatcher(t, db, ['versionchange', 'close']);
|
||||
const openrq2 = indexedDB.open(dbname, version + 1);
|
||||
let versionChangeEvent = await dbWatcher.wait_for('versionchange');
|
||||
const openrq2Watcher = new EventWatcher(
|
||||
t, openrq2, ['upgradeneeded', 'success', 'error', 'blocked']);
|
||||
|
||||
assert_equals(versionChangeEvent.oldVersion, version, 'old version');
|
||||
assert_equals(versionChangeEvent.newVersion, version + 1, 'new version');
|
||||
db.close();
|
||||
|
||||
await openrq2Watcher.wait_for('upgradeneeded');
|
||||
|
||||
let successEvent = await openrq2Watcher.wait_for('success');
|
||||
db2 = successEvent.target.result;
|
||||
}, 'No Blocked event');
|
||||
|
||||
promise_test(async t => {
|
||||
const dbname = location + '-' + t.name;
|
||||
const version = 3;
|
||||
const db = await setupDatabase(t, dbname, version);
|
||||
let db2;
|
||||
|
||||
t.add_cleanup(() => {
|
||||
if (db2)
|
||||
db2.close();
|
||||
if (db)
|
||||
db.close();
|
||||
indexedDB.deleteDatabase(dbname);
|
||||
});
|
||||
|
||||
const dbWatcher = new EventWatcher(t, db, ['versionchange', 'close']);
|
||||
const openrq2 = indexedDB.open(dbname, version + 1);
|
||||
let versionChangeEvent = await dbWatcher.wait_for('versionchange');
|
||||
const openrq2Watcher = new EventWatcher(
|
||||
t, openrq2, ['blocked', 'upgradeneeded', 'error', 'success']);
|
||||
|
||||
assert_equals(versionChangeEvent.oldVersion, version, 'old version');
|
||||
assert_equals(versionChangeEvent.newVersion, version + 1, 'new version');
|
||||
|
||||
let blockedEvent = await openrq2Watcher.wait_for('blocked');
|
||||
db.close();
|
||||
|
||||
await openrq2Watcher.wait_for('upgradeneeded');
|
||||
|
||||
let successEvent = await openrq2Watcher.wait_for('success');
|
||||
db2 = successEvent.target.result;
|
||||
}, 'Blocked event');
|
||||
Loading…
Reference in a new issue