Bug 1182987 - Part 5: Change mode of "readwrite" transaction to "cleanup" after QuotaExceeded is fired; r=baku

--HG--
rename : dom/indexedDB/test/unit/test_quotaExceeded_recovery.js => dom/indexedDB/test/unit/test_cleanup_transaction.js
This commit is contained in:
Jan Varga 2016-04-21 06:53:30 +02:00
parent 1fff16a212
commit 4834a1cc3e
6 changed files with 225 additions and 66 deletions

View File

@ -175,6 +175,7 @@ IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
, mFileHandleDisabled(aRequest->IsFileHandleDisabled())
, mClosed(false)
, mInvalidated(false)
, mQuotaExceeded(false)
{
MOZ_ASSERT(aRequest);
MOZ_ASSERT(aFactory);
@ -665,13 +666,19 @@ IDBDatabase::Transaction(JSContext* aCx,
mode = IDBTransaction::READ_ONLY;
break;
case IDBTransactionMode::Readwrite:
mode = IDBTransaction::READ_WRITE;
if (mQuotaExceeded) {
mode = IDBTransaction::CLEANUP;
mQuotaExceeded = false;
} else {
mode = IDBTransaction::READ_WRITE;
}
break;
case IDBTransactionMode::Readwriteflush:
mode = IDBTransaction::READ_WRITE_FLUSH;
break;
case IDBTransactionMode::Cleanup:
mode = IDBTransaction::CLEANUP;
mQuotaExceeded = false;
break;
case IDBTransactionMode::Versionchange:
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
@ -705,7 +712,7 @@ IDBDatabase::Transaction(JSContext* aCx,
transaction->SetBackgroundActor(actor);
if (aMode == IDBTransactionMode::Cleanup) {
if (mode == IDBTransaction::CLEANUP) {
ExpireFileActors(/* aExpireAll */ true);
}

View File

@ -83,6 +83,7 @@ class IDBDatabase final
const bool mFileHandleDisabled;
bool mClosed;
bool mInvalidated;
bool mQuotaExceeded;
public:
static already_AddRefed<IDBDatabase>
@ -149,6 +150,12 @@ public:
return mInvalidated;
}
void
SetQuotaExceeded()
{
mQuotaExceeded = true;
}
void
EnterSetVersionTransaction(uint64_t aNewVersion);

View File

@ -764,6 +764,10 @@ IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
eNotCancelable);
MOZ_ASSERT(event);
} else {
if (aResult == NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR) {
mDatabase->SetQuotaExceeded();
}
if (!mError && !mAbortedByScript) {
mError = new DOMError(GetOwner(), aResult);
}

View File

@ -0,0 +1,155 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var disableWorkerTest = "Need a way to set temporary prefs from a worker";
var testGenerator = testSteps();
function testSteps()
{
const spec = "http://foo.com";
const name =
this.window ? window.location.pathname : "test_quotaExceeded_recovery";
const objectStoreName = "foo";
// We want 32 MB database, but there's the group limit so we need to
// multiply by 5.
const tempStorageLimitKB = 32 * 1024 * 5;
// Store in 1 MB chunks.
const dataSize = 1024 * 1024;
for (let blobs of [false, true]) {
setTemporaryStorageLimit(tempStorageLimitKB);
clearAllDatabases(continueToNextStepSync);
yield undefined;
info("Opening database");
let request = indexedDB.openForPrincipal(getPrincipal(spec), name);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;;
request.onsuccess = unexpectedSuccessHandler;
yield undefined;
// upgradeneeded
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
info("Creating objectStore");
request.result.createObjectStore(objectStoreName);
yield undefined;
// success
let db = request.result;
db.onerror = errorHandler;
ok(true, "Adding data until quota is reached");
let obj = {
name: "foo"
}
if (!blobs) {
obj.data = getRandomView(dataSize);
}
let i = 1;
let j = 1;
while (true) {
if (blobs) {
obj.data = getBlob(getView(dataSize));
}
let trans = db.transaction(objectStoreName, "readwrite");
request = trans.objectStore(objectStoreName).add(obj, i);
request.onerror = function(event)
{
event.stopPropagation();
}
trans.oncomplete = function(event) {
i++;
j++;
testGenerator.send(true);
}
trans.onabort = function(event) {
is(trans.error.name, "QuotaExceededError", "Reached quota limit");
testGenerator.send(false);
}
let completeFired = yield undefined;
if (completeFired) {
ok(true, "Got complete event");
} else {
ok(true, "Got abort event");
if (j == 1) {
// Plain cleanup transaction (just vacuuming and checkpointing)
// couldn't shrink database any further.
break;
}
j = 1;
trans = db.transaction(objectStoreName, "cleanup");
trans.onabort = unexpectedSuccessHandler;;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
}
}
info("Reopening database");
db.close();
request = indexedDB.openForPrincipal(getPrincipal(spec), name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
yield undefined;
db = request.result;
db.onerror = errorHandler;
info("Deleting some data")
let trans = db.transaction(objectStoreName, "cleanup");
trans.objectStore(objectStoreName).delete(1);
trans.onabort = unexpectedSuccessHandler;;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
info("Adding data again")
trans = db.transaction(objectStoreName, "readwrite");
trans.objectStore(objectStoreName).add(obj, 1);
trans.onabort = unexpectedSuccessHandler;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
info("Deleting database");
db.close();
request = indexedDB.deleteForPrincipal(getPrincipal(spec), name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
yield undefined;
}
finishTest();
yield undefined;
}

View File

@ -14,13 +14,17 @@ function testSteps()
this.window ? window.location.pathname : "test_quotaExceeded_recovery";
const objectStoreName = "foo";
// We want 32 MB database file, but there's the group limit so we need to
// multiply by 5.
const tempStorageLimitKB = 32 * 1024 * 5;
// We want 8 MB database on Android and 32 MB database on other platforms.
const groupLimitMB = mozinfo.os == "android" ? 8 : 32;
// The group limit is calculated as 20% of the global temporary storage limit.
const tempStorageLimitKB = groupLimitMB * 5 * 1024;
// Store in 1 MB chunks.
const dataSize = 1024 * 1024;
const maxIter = 10;
for (let blobs of [false, true]) {
setTemporaryStorageLimit(tempStorageLimitKB);
@ -42,7 +46,7 @@ function testSteps()
info("Creating objectStore");
request.result.createObjectStore(objectStoreName);
request.result.createObjectStore(objectStoreName, { autoIncrement: true });
yield undefined;
@ -50,7 +54,7 @@ function testSteps()
let db = request.result;
db.onerror = errorHandler;
ok(true, "Adding data until quota is reached");
ok(true, "Filling database");
let obj = {
name: "foo"
@ -60,6 +64,7 @@ function testSteps()
obj.data = getRandomView(dataSize);
}
let iter = 1;
let i = 1;
let j = 1;
while (true) {
@ -68,14 +73,16 @@ function testSteps()
}
let trans = db.transaction(objectStoreName, "readwrite");
request = trans.objectStore(objectStoreName).add(obj, i);
request = trans.objectStore(objectStoreName).add(obj);
request.onerror = function(event)
{
event.stopPropagation();
}
trans.oncomplete = function(event) {
i++;
if (iter == 1) {
i++;
}
j++;
testGenerator.send(true);
}
@ -84,68 +91,46 @@ function testSteps()
testGenerator.send(false);
}
let shouldContinue = yield undefined;
if (shouldContinue) {
let completeFired = yield undefined;
if (completeFired) {
ok(true, "Got complete event");
continue;
}
ok(true, "Got abort event");
if (iter++ == maxIter) {
break;
}
if (iter > 1) {
ok(i == j, "Recycled entire database");
j = 1;
}
trans = db.transaction(objectStoreName, "readwrite");
// Don't use a cursor for deleting stored blobs (Cursors prolong live
// of stored files since each record must be fetched from the database
// first which creates a memory reference to the stored blob.)
if (blobs) {
request = trans.objectStore(objectStoreName).clear();
} else {
ok(true, "Got abort event");
if (j==1) {
break;
} else {
j = 1;
trans = db.transaction(objectStoreName, "cleanup");
trans.onabort = unexpectedSuccessHandler;;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
request = trans.objectStore(objectStoreName).openCursor();
request.onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
cursor.delete();
cursor.continue();
}
}
}
trans.onabort = unexpectedSuccessHandler;;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
}
info("Reopening database");
db.close();
request = indexedDB.openForPrincipal(getPrincipal(spec), name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
yield undefined;
db = request.result;
db.onerror = errorHandler;
info("Deleting some data")
let trans = db.transaction(objectStoreName, "cleanup");
trans.objectStore(objectStoreName).delete(1);
trans.onabort = unexpectedSuccessHandler;;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
info("Adding data again")
trans = db.transaction(objectStoreName, "readwrite");
trans.objectStore(objectStoreName).add(obj, 1);
trans.onabort = unexpectedSuccessHandler;
trans.oncomplete = grabEventAndContinueHandler;
yield undefined;
info("Deleting database");
db.close();
request = indexedDB.deleteForPrincipal(getPrincipal(spec), name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
yield undefined;
}
finishTest();

View File

@ -25,6 +25,7 @@ support-files =
[test_blob_file_backed.js]
[test_bug1056939.js]
[test_cleanup_transaction.js]
[test_defaultStorageUpgrade.js]
[test_globalObjects_ipc.js]
skip-if = toolkit == 'android'