gecko-dev/dom/indexedDB/test/unit/test_database_onclose.js
Bevis Tseng 44deb08f5c Bug 1151017 - Support the 'close' Event on Databases. r=khuey
Outline of this patch:

1. Define a new ipdl message called |CloseAfterInvalidationComplete| to trigger the close event after all transactions are complete only if the database is invalidated by the user agent.
2. Make sure the following event sequence is consistent during invalidation according to the steps in |5.2. Closing a database| by the following 2 solutions:
     IDBRequest.onerror -> IDBTransaction.onerror -> IDBTransaction.onabort -> IDBDatabase.onclose.
   2.1. In parent process, do not force to abort the transactions after invalidation but wait for all the transactions in its child process are complete.
   2.2. In child process, make sure that each IDBTransaction will notify its completion to the parent after all its pending IDBRequests are finished.
3. Add test_database_onclose.js to test the close event especially when read/write operation is ongoing.
4. Add test_database_close_without_onclose.js as a XPCShell test because setTimeout() is not preferred in Mochitest to ensure that the IDBDatabase.onclose event won't be sent after closed normally.
2016-05-31 18:08:20 +08:00

245 lines
7.6 KiB
JavaScript

/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
function testInvalidStateError(aDb, aTxn) {
try {
info("The db shall become invalid after closed.");
aDb.transaction("store");
ok(false, "InvalidStateError shall be thrown.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
try {
info("The txn shall become invalid after closed.");
aTxn.objectStore("store");
ok(false, "InvalidStateError shall be thrown.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
}
const name = this.window ? window.location.pathname :
"test_database_onclose.js";
info("#1: Verifying IDBDatabase.onclose after cleared by the agent.");
let openRequest = indexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let event = yield undefined;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
let db = event.target.result;
db.createObjectStore("store");
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "success", "Expect a success event");
is(event.target, openRequest, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
let txn = db.transaction("store", "readwrite");
let objectStore = txn.objectStore("store");
clearAllDatabases(continueToNextStep);
db.onclose = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "close", "Expect a close event");
is(event.target, db, "Correct target");
info("Wait for callback of clearAllDatabases().");
yield undefined;
testInvalidStateError(db, txn);
info("#2: Verifying IDBDatabase.onclose && IDBTransaction.onerror " +
"in *write* operation after cleared by the agent.");
openRequest = indexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
event = yield undefined;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
db = event.target.result;
db.createObjectStore("store");
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "success", "Expect a success event");
is(event.target, openRequest, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
txn = db.transaction("store", "readwrite");
objectStore = txn.objectStore("store");
let objectId = 0;
while(true) {
let addRequest = objectStore.add({foo: "foo"}, objectId);
addRequest.onerror = function(event) {
info("addRequest.onerror, objectId: " + objectId);
txn.onerror = grabEventAndContinueHandler;
testGenerator.send(true);
}
addRequest.onsuccess = function() {
testGenerator.send(false);
}
if (objectId == 0) {
clearAllDatabases(() => {
info("clearAllDatabases is done.");
continueToNextStep();
});
}
objectId++;
let aborted = yield undefined;
if (aborted) {
break;
}
}
event = yield undefined;
is(event.type, "error", "Got an error event");
is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
event.preventDefault();
txn.onabort = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "abort", "Got an abort event");
is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
db.onclose = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "close", "Expect a close event");
is(event.target, db, "Correct target");
testInvalidStateError(db, txn);
info("Wait for the callback of clearAllDatabases().");
yield undefined;
info("#3: Verifying IDBDatabase.onclose && IDBTransaction.onerror " +
"in *read* operation after cleared by the agent.");
openRequest = indexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
event = yield undefined;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
db = event.target.result;
objectStore =
db.createObjectStore("store", { keyPath: "id", autoIncrement: true });
// The number of read records varies between 1~2000 before the db is cleared
// during testing.
let numberOfObjects = 3000;
objectId = 0;
while(true) {
let addRequest = objectStore.add({foo: "foo"});
addRequest.onsuccess = function() {
objectId++;
testGenerator.send(objectId == numberOfObjects);
}
addRequest.onerror = errorHandler;
let done = yield undefined;
if (done) {
break;
}
}
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "success", "Expect a success event");
is(event.target, openRequest, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
txn = db.transaction("store");
objectStore = txn.objectStore("store");
let numberOfReadObjects = 0;
let readRequest = objectStore.openCursor();
readRequest.onerror = function(event) {
info("readRequest.onerror, numberOfReadObjects: " + numberOfReadObjects);
testGenerator.send(true);
}
readRequest.onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
numberOfReadObjects++;
event.target.result.continue();
} else {
info("Cursor is invalid, numberOfReadObjects: " + numberOfReadObjects);
todo(false, "All records are iterated before database is cleared!");
testGenerator.send(false);
}
}
clearAllDatabases(() => {
info("clearAllDatabases is done.");
continueToNextStep();
});
readRequestError = yield undefined;
if (readRequestError) {
txn.onerror = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "error", "Got an error event");
is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
event.preventDefault();
txn.onabort = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "abort", "Got an abort event");
is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
db.onclose = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "close", "Expect a close event");
is(event.target, db, "Correct target");
testInvalidStateError(db, txn);
}
info("Wait for the callback of clearAllDatabases().");
yield undefined;
finishTest();
yield undefined;
}