mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
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:
parent
1fff16a212
commit
4834a1cc3e
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
155
dom/indexedDB/test/unit/test_cleanup_transaction.js
Normal file
155
dom/indexedDB/test/unit/test_cleanup_transaction.js
Normal 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;
|
||||
}
|
@ -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();
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user