LibWeb: Open waiting IDB connections when the previous one is GCed
Without this, an open request could hang if a prior connection was not explicitly close()d but instead allowed to go out of scope. The spec says that the dangling connection should be closed when the execution context it was opened in is destroyed, but no other browser does so. Detecting the execution context being destroyed would likely mean a lot of overhead in JS calls, so it's best to avoid that, despite this being observable.
This commit is contained in:
parent
ed8ba673de
commit
61b9be47ce
4 changed files with 62 additions and 0 deletions
|
|
@ -29,6 +29,18 @@ IDBDatabase::IDBDatabase(JS::Realm& realm, Database& db)
|
|||
|
||||
IDBDatabase::~IDBDatabase() = default;
|
||||
|
||||
void IDBDatabase::finalize()
|
||||
{
|
||||
Base::finalize();
|
||||
|
||||
m_associated_database->dissociate(*this);
|
||||
heap().enqueue_post_gc_task([database = GC::Weak(m_associated_database)] {
|
||||
if (!database)
|
||||
return;
|
||||
database->check_pending_connection_wait();
|
||||
});
|
||||
}
|
||||
|
||||
GC::Ref<IDBDatabase> IDBDatabase::create(JS::Realm& realm, Database& db)
|
||||
{
|
||||
return realm.create<IDBDatabase>(realm, db);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ class IDBDatabase : public DOM::EventTarget {
|
|||
GC_DECLARE_ALLOCATOR(IDBDatabase);
|
||||
|
||||
public:
|
||||
static constexpr bool OVERRIDES_FINALIZE = true;
|
||||
|
||||
virtual ~IDBDatabase() override;
|
||||
virtual void finalize() override;
|
||||
|
||||
[[nodiscard]] static GC::Ref<IDBDatabase> create(JS::Realm&, Database&);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
PASS
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="include.js"></script>
|
||||
<script>
|
||||
asyncTest(done => {
|
||||
const DB_NAME = "gc-closes-unreachable-connection";
|
||||
|
||||
indexedDB.deleteDatabase(DB_NAME);
|
||||
|
||||
function openAndForget() {
|
||||
return new Promise((resolve) => {
|
||||
let req = indexedDB.open(DB_NAME, 1);
|
||||
req.onupgradeneeded = (e) => {
|
||||
e.target.result.createObjectStore("store");
|
||||
};
|
||||
req.onsuccess = (e) => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function tryUpgrade() {
|
||||
let req = indexedDB.open(DB_NAME, 2);
|
||||
|
||||
req.onblocked = () => {
|
||||
println("FAIL: upgrade blocked, connection was not closed by GC");
|
||||
done();
|
||||
};
|
||||
|
||||
req.onupgradeneeded = () => {
|
||||
println("PASS");
|
||||
};
|
||||
|
||||
req.onsuccess = () => {
|
||||
req.result.close();
|
||||
indexedDB.deleteDatabase(DB_NAME);
|
||||
done();
|
||||
};
|
||||
}
|
||||
|
||||
openAndForget().then(() => {
|
||||
return internals.gcAsync()
|
||||
}).then(() => {
|
||||
tryUpgrade();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue