diff options
Diffstat (limited to 'dom/indexedDB/test')
340 files changed, 31180 insertions, 0 deletions
diff --git a/dom/indexedDB/test/bfcache_iframe1.html b/dom/indexedDB/test/bfcache_iframe1.html new file mode 100644 index 000000000..ade5dc555 --- /dev/null +++ b/dom/indexedDB/test/bfcache_iframe1.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> + <script> + var request = indexedDB.open(parent.location, 1); + request.onupgradeneeded = function(e) { + var db = e.target.result; + // This should never be called + db.onversionchange = function(e) { + db.transaction(["mystore"]).objectStore("mystore").put({ hello: "fail" }, 42); + } + var trans = e.target.transaction; + if (db.objectStoreNames.contains("mystore")) { + db.deleteObjectStore("mystore"); + } + var store = db.createObjectStore("mystore"); + store.add({ hello: "world" }, 42); + trans.oncomplete = function() { + parent.postMessage("go", "http://mochi.test:8888"); + } + }; + </script> +</head> +<body> + This is page one. +</body> +</html> diff --git a/dom/indexedDB/test/bfcache_iframe2.html b/dom/indexedDB/test/bfcache_iframe2.html new file mode 100644 index 000000000..43cb92a58 --- /dev/null +++ b/dom/indexedDB/test/bfcache_iframe2.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <script> + var res = {}; + var request = indexedDB.open(parent.location, 2); + request.onblocked = function() { + res.blockedFired = true; + } + request.onupgradeneeded = function(e) { + var db = e.target.result; + res.version = db.version; + res.storeCount = db.objectStoreNames.length; + + var trans = request.transaction; + trans.objectStore("mystore").get(42).onsuccess = function(e) { + res.value = JSON.stringify(e.target.result); + } + trans.oncomplete = function() { + parent.postMessage(JSON.stringify(res), "http://mochi.test:8888"); + } + }; + + </script> +</head> +<body> + This is page two. +</body> +</html> diff --git a/dom/indexedDB/test/blob_worker_crash_iframe.html b/dom/indexedDB/test/blob_worker_crash_iframe.html new file mode 100644 index 000000000..304b87ea0 --- /dev/null +++ b/dom/indexedDB/test/blob_worker_crash_iframe.html @@ -0,0 +1,98 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript"> + function report(result) { + var message = { source: "iframe" }; + message.result = result; + window.parent.postMessage(message, "*"); + } + + function runIndexedDBTest() { + var db = null; + + // Create the data-store + function createDatastore() { + try { + var request = indexedDB.open(window.location.pathname, 1); + request.onupgradeneeded = function(event) { + event.target.result.createObjectStore("foo"); + } + request.onsuccess = function(event) { + db = event.target.result; + createAndStoreBlob(); + } + } + catch (e) { +dump("EXCEPTION IN CREATION: " + e + "\n " + e.stack + "\n"); + report(false); + } + } + + function createAndStoreBlob() { + const BLOB_DATA = ["fun ", "times ", "all ", "around!"]; + var blob = new Blob(BLOB_DATA, { type: "text/plain" }); + var objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add({ blob: blob }, 42).onsuccess = refetchBlob; + } + + function refetchBlob() { + var foo = db.transaction("foo").objectStore("foo"); + foo.get(42).onsuccess = fetchedBlobCreateWorkerAndSendBlob; + } + + function fetchedBlobCreateWorkerAndSendBlob(event) { + var idbBlob = event.target.result.blob; + var compositeBlob = new Blob(['I like the following blob: ', idbBlob], + { type: "text/fancy" }); + + function workerScript() { + onmessage = function(event) { + // Save the Blob to the worker's global scope. + self.holdOntoBlob = event.data; + // Send any message so we can serialize and keep our runtime behaviour + // consistent. + postMessage('kung fu death grip established'); + } + } + + var url = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + // Keep a reference to the worker on the window. + var worker = window.worker = new Worker(url); + worker.postMessage(compositeBlob); + worker.onmessage = workerLatchedBlobDeleteFromDB; + } + + function workerLatchedBlobDeleteFromDB() { + // Delete the reference to the Blob from the database leaving the worker + // thread reference as the only live reference once a GC has cleaned + // out our references that we sent to the worker. The page that owns + // us triggers a GC just for that reason. + var objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.delete(42).onsuccess = closeDBTellOwningThread; + } + + function closeDBTellOwningThread(event) { + // Now that worker has latched the blob, clean up the database. + db.close(); + db = null; + report('ready'); + } + + createDatastore(); + } + </script> + +</head> + +<body onload="runIndexedDBTest();"> +</body> + +</html> diff --git a/dom/indexedDB/test/browser.ini b/dom/indexedDB/test/browser.ini new file mode 100644 index 000000000..85671568d --- /dev/null +++ b/dom/indexedDB/test/browser.ini @@ -0,0 +1,21 @@ +[DEFAULT] +skip-if = (buildapp != "browser") +support-files = + head.js + browser_forgetThisSiteAdd.html + browser_forgetThisSiteGet.html + browserHelpers.js + browser_permissionsPrompt.html + browser_permissionsSharedWorker.html + browser_permissionsSharedWorker.js + browser_permissionsWorker.html + browser_permissionsWorker.js + bug839193.js + bug839193.xul + +[browser_forgetThisSite.js] +[browser_permissionsPromptAllow.js] +[browser_permissionsPromptDeny.js] +[browser_permissionsPromptWorker.js] +[browser_perwindow_privateBrowsing.js] +[browser_bug839193.js] diff --git a/dom/indexedDB/test/browserHelpers.js b/dom/indexedDB/test/browserHelpers.js new file mode 100644 index 000000000..c61c78943 --- /dev/null +++ b/dom/indexedDB/test/browserHelpers.js @@ -0,0 +1,56 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +var testResult; +var testException; + +function runTest() +{ + testGenerator.next(); +} + +function finishTestNow() +{ + if (testGenerator) { + testGenerator.close(); + testGenerator = undefined; + } +} + +function finishTest() +{ + setTimeout(finishTestNow, 0); + setTimeout(() => { + if (window.testFinishedCallback) + window.testFinishedCallback(testResult, testException); + else { + let message; + if (testResult) + message = "ok"; + else + message = testException; + window.parent.postMessage(message, "*"); + } + }, 0); +} + +function grabEventAndContinueHandler(event) +{ + testGenerator.send(event); +} + +function errorHandler(event) +{ + throw new Error("indexedDB error, code " + event.target.error.name); +} + +function continueToNextStep() +{ + SimpleTest.executeSoon(function() { + testGenerator.next(); + }); +} diff --git a/dom/indexedDB/test/browser_bug839193.js b/dom/indexedDB/test/browser_bug839193.js new file mode 100644 index 000000000..eef284794 --- /dev/null +++ b/dom/indexedDB/test/browser_bug839193.js @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var gTestRoot = getRootDirectory(gTestPath); +var gBugWindow = null; +var gIterations = 5; + +function onLoad() { + gBugWindow.close(); +} + +function onUnload() { + if (!gIterations) { + gBugWindow = null; + Services.obs.removeObserver(onLoad, "bug839193-loaded"); + Services.obs.removeObserver(onUnload, "bug839193-unloaded"); + + window.focus(); + finish(); + } else { + gBugWindow = window.openDialog(gTestRoot + "bug839193.xul"); + gIterations--; + } +} + +// This test is about leaks, which are handled by the test harness, so +// there are no actual checks here. Whether or not this test passes or fails +// will be apparent by the checks the harness performs. +function test() { + waitForExplicitFinish(); + + // This test relies on the test timing out in order to indicate failure so + // let's add a dummy pass. + ok(true, "Each test requires at least one pass, fail or todo so here is a pass."); + + Services.obs.addObserver(onLoad, "bug839193-loaded", false); + Services.obs.addObserver(onUnload, "bug839193-unloaded", false); + + gBugWindow = window.openDialog(gTestRoot + "bug839193.xul"); +} diff --git a/dom/indexedDB/test/browser_forgetThisSite.js b/dom/indexedDB/test/browser_forgetThisSite.js new file mode 100644 index 000000000..c1177908f --- /dev/null +++ b/dom/indexedDB/test/browser_forgetThisSite.js @@ -0,0 +1,109 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm"); + +const domains = [ + "mochi.test:8888", + "www.example.com" +]; + +const addPath = "/browser/dom/indexedDB/test/browser_forgetThisSiteAdd.html"; +const getPath = "/browser/dom/indexedDB/test/browser_forgetThisSiteGet.html"; + +const testPageURL1 = "http://" + domains[0] + addPath; +const testPageURL2 = "http://" + domains[1] + addPath; +const testPageURL3 = "http://" + domains[0] + getPath; +const testPageURL4 = "http://" + domains[1] + getPath; + +function test() +{ + requestLongerTimeout(2); + waitForExplicitFinish(); + // Avoids the prompt + setPermission(testPageURL1, "indexedDB"); + setPermission(testPageURL2, "indexedDB"); + executeSoon(test1); +} + +function test1() +{ + // Set database version for domain 1 + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(result, exception) { + ok(result == 11, "Set version on database in " + testPageURL1); + ok(!exception, "No exception"); + gBrowser.removeCurrentTab(); + + executeSoon(test2); + }); + }, true); + content.location = testPageURL1; +} + +function test2() +{ + // Set database version for domain 2 + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(result, exception) { + ok(result == 11, "Set version on database in " + testPageURL2); + ok(!exception, "No exception"); + gBrowser.removeCurrentTab(); + + executeSoon(test3); + }); + }, true); + content.location = testPageURL2; +} + +function test3() +{ + // Remove database from domain 2 + ForgetAboutSite.removeDataFromDomain(domains[1]); + setPermission(testPageURL4, "indexedDB"); + executeSoon(test4); +} + +function test4() +{ + // Get database version for domain 1 + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(result, exception) { + ok(result == 11, "Got correct version on database in " + testPageURL3); + ok(!exception, "No exception"); + gBrowser.removeCurrentTab(); + + executeSoon(test5); + }); + }, true); + content.location = testPageURL3; +} + +function test5() +{ + // Get database version for domain 2 + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(result, exception) { + ok(result == 1, "Got correct version on database in " + testPageURL4); + ok(!exception, "No exception"); + gBrowser.removeCurrentTab(); + + executeSoon(finish); + }); + }, true); + content.location = testPageURL4; +} diff --git a/dom/indexedDB/test/browser_forgetThisSiteAdd.html b/dom/indexedDB/test/browser_forgetThisSiteAdd.html new file mode 100644 index 000000000..2982012a6 --- /dev/null +++ b/dom/indexedDB/test/browser_forgetThisSiteAdd.html @@ -0,0 +1,39 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + let request = indexedDB.open("browser_forgetThisSite.js", 11); + request.onerror = grabEventAndContinueHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + if (event.type == "error") { + testException = event.target.error.name; + } + else { + let db = event.target.result; + + testResult = db.version; + + event.target.transaction.oncomplete = finishTest; + yield undefined; + } + + yield undefined; + } + </script> + + <script type="text/javascript;version=1.7" src="browserHelpers.js"></script> + + </head> + + <body onload="runTest();" onunload="finishTestNow();"></body> + +</html> diff --git a/dom/indexedDB/test/browser_forgetThisSiteGet.html b/dom/indexedDB/test/browser_forgetThisSiteGet.html new file mode 100644 index 000000000..948eaa68f --- /dev/null +++ b/dom/indexedDB/test/browser_forgetThisSiteGet.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + let request = indexedDB.open("browser_forgetThisSite.js"); + request.onerror = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + if (event.type == "error") { + testException = event.target.error.name; + } + else { + let db = event.target.result; + testResult = db.version; + } + + finishTest() + yield undefined; + } + </script> + + <script type="text/javascript;version=1.7" src="browserHelpers.js"></script> + + </head> + + <body onload="runTest();" onunload="finishTestNow();"></body> + +</html> diff --git a/dom/indexedDB/test/browser_permissionsPrompt.html b/dom/indexedDB/test/browser_permissionsPrompt.html new file mode 100644 index 000000000..0ee698b97 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsPrompt.html @@ -0,0 +1,41 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <meta charset=UTF-8> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, { version: 1, + storage: "persistent" }); + request.onerror = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + if (event.type == "success") { + testResult = event.target.result instanceof IDBDatabase; + } + else { + testException = event.target.error.name; + } + + event.preventDefault(); + + finishTest() + yield undefined; + } + </script> + + <script type="text/javascript;version=1.7" src="browserHelpers.js"></script> + + </head> + + <body onload="runTest();" onunload="finishTestNow();"></body> + +</html> diff --git a/dom/indexedDB/test/browser_permissionsPromptAllow.js b/dom/indexedDB/test/browser_permissionsPromptAllow.js new file mode 100644 index 000000000..dd0921872 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsPromptAllow.js @@ -0,0 +1,90 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const testPageURL = "http://mochi.test:8888/browser/" + + "dom/indexedDB/test/browser_permissionsPrompt.html"; +const notificationID = "indexedDB-permissions-prompt"; + +function test() +{ + waitForExplicitFinish(); + + // We want a prompt. + removePermission(testPageURL, "indexedDB"); + executeSoon(test1); +} + +function test1() +{ + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(isIDBDatabase, + "First database creation was successful"); + ok(!exception, "No exception"); + is(getPermission(testPageURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.ALLOW_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); + executeSoon(test2); + }); + + registerPopupEventHandler("popupshowing", function () { + ok(true, "prompt showing"); + }); + registerPopupEventHandler("popupshown", function () { + ok(true, "prompt shown"); + triggerMainCommand(this); + }); + registerPopupEventHandler("popuphidden", function () { + ok(true, "prompt hidden"); + }); + + }, true); + + info("loading test page: " + testPageURL); + content.location = testPageURL; +} + +function test2() +{ + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(isIDBDatabase, + "First database creation was successful"); + ok(!exception, "No exception"); + is(getPermission(testPageURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.ALLOW_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); + unregisterAllPopupEventHandlers(); + removePermission(testPageURL, "indexedDB"); + executeSoon(finish); + }); + + registerPopupEventHandler("popupshowing", function () { + ok(false, "Shouldn't show a popup this time"); + }); + registerPopupEventHandler("popupshown", function () { + ok(false, "Shouldn't show a popup this time"); + }); + registerPopupEventHandler("popuphidden", function () { + ok(false, "Shouldn't show a popup this time"); + }); + + }, true); + + info("loading test page: " + testPageURL); + content.location = testPageURL; +} diff --git a/dom/indexedDB/test/browser_permissionsPromptDeny.js b/dom/indexedDB/test/browser_permissionsPromptDeny.js new file mode 100644 index 000000000..e7132e004 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsPromptDeny.js @@ -0,0 +1,110 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const testPageURL = "http://mochi.test:8888/browser/" + + "dom/indexedDB/test/browser_permissionsPrompt.html"; +const notificationID = "indexedDB-permissions-prompt"; + +function promiseMessage(aMessage, browser) { + return ContentTask.spawn(browser.selectedBrowser, aMessage, function* (aMessage) { + yield new Promise((resolve, reject) => { + content.addEventListener("message", function messageListener(event) { + content.removeEventListener("message", messageListener); + is(event.data, aMessage, "received " + aMessage); + if (event.data == aMessage) + resolve(); + else + reject(); + }); + }); + }); +} + +add_task(function test1() { + removePermission(testPageURL, "indexedDB"); + + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + info("loading test page: " + testPageURL); + gBrowser.selectedBrowser.loadURI(testPageURL); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + + registerPopupEventHandler("popupshowing", function () { + ok(true, "prompt showing"); + }); + registerPopupEventHandler("popupshown", function () { + ok(true, "prompt shown"); + triggerSecondaryCommand(this, 0); + }); + registerPopupEventHandler("popuphidden", function () { + ok(true, "prompt hidden"); + }); + + yield promiseMessage("InvalidStateError", gBrowser); + + is(getPermission(testPageURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.DENY_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); +}); + +add_task(function test2() { + info("creating private window"); + let win = yield BrowserTestUtils.openNewBrowserWindow({ private : true }); + + info("creating private tab"); + win.gBrowser.selectedTab = win.gBrowser.addTab(); + + info("loading test page: " + testPageURL); + win.gBrowser.selectedBrowser.loadURI(testPageURL); + yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); + + registerPopupEventHandler("popupshowing", function () { + ok(false, "prompt showing"); + }); + registerPopupEventHandler("popupshown", function () { + ok(false, "prompt shown"); + }); + registerPopupEventHandler("popuphidden", function () { + ok(false, "prompt hidden"); + }); + yield promiseMessage("InvalidStateError", win.gBrowser); + + is(getPermission(testPageURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.DENY_ACTION, + "Correct permission set"); + unregisterAllPopupEventHandlers(); + win.gBrowser.removeCurrentTab(); + win.close(); +}); + +add_task(function test3() { + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + info("loading test page: " + testPageURL); + gBrowser.selectedBrowser.loadURI(testPageURL); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + + registerPopupEventHandler("popupshowing", function () { + ok(false, "Shouldn't show a popup this time"); + }); + registerPopupEventHandler("popupshown", function () { + ok(false, "Shouldn't show a popup this time"); + }); + registerPopupEventHandler("popuphidden", function () { + ok(false, "Shouldn't show a popup this time"); + }); + + yield promiseMessage("InvalidStateError", gBrowser); + + is(getPermission(testPageURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.DENY_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); + unregisterAllPopupEventHandlers(); + removePermission(testPageURL, "indexedDB"); +}); diff --git a/dom/indexedDB/test/browser_permissionsPromptWorker.js b/dom/indexedDB/test/browser_permissionsPromptWorker.js new file mode 100644 index 000000000..a60704e69 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsPromptWorker.js @@ -0,0 +1,91 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const testWorkerURL = "http://mochi.test:8888/browser/" + + "dom/indexedDB/test/browser_permissionsWorker.html"; +const testSharedWorkerURL = "http://mochi.test:8888/browser/" + + "dom/indexedDB/test/browser_permissionsSharedWorker.html"; +const notificationID = "indexedDB-permissions-prompt"; + +function test() +{ + waitForExplicitFinish(); + executeSoon(test1); +} + +function test1() +{ + // We want a prompt. + removePermission(testWorkerURL, "indexedDB"); + + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(isIDBDatabase, "First database creation was successful"); + ok(!exception, "No exception"); + is(getPermission(testWorkerURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.ALLOW_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); + executeSoon(test2); + }); + + registerPopupEventHandler("popupshowing", function () { + ok(true, "prompt showing"); + }); + registerPopupEventHandler("popupshown", function () { + ok(true, "prompt shown"); + triggerMainCommand(this); + }); + registerPopupEventHandler("popuphidden", function () { + ok(true, "prompt hidden"); + }); + + }, true); + + info("loading test page: " + testWorkerURL); + content.location = testWorkerURL; +} + +function test2() +{ + // We want a prompt. + removePermission(testSharedWorkerURL, "indexedDB"); + + info("creating tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(!isIDBDatabase, "First database creation was successful"); + ok(exception, "No exception"); + is(getPermission(testSharedWorkerURL, "indexedDB"), + Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION, + "Correct permission set"); + gBrowser.removeCurrentTab(); + executeSoon(finish); + }); + + registerPopupEventHandler("popupshowing", function () { + ok(false, "prompt showing"); + }); + registerPopupEventHandler("popupshown", function () { + ok(false, "prompt shown"); + }); + registerPopupEventHandler("popuphidden", function () { + ok(false, "prompt hidden"); + }); + + }, true); + + info("loading test page: " + testSharedWorkerURL); + content.location = testSharedWorkerURL; +} diff --git a/dom/indexedDB/test/browser_permissionsSharedWorker.html b/dom/indexedDB/test/browser_permissionsSharedWorker.html new file mode 100644 index 000000000..295f01fc5 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsSharedWorker.html @@ -0,0 +1,34 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + let testIsIDBDatabase; + let testException; + + function runTest() { + let w = new SharedWorker('browser_permissionsSharedWorker.js'); + w.port.onmessage = function(e) { + if (e.data.status == 'success') { + testIsIDBDatabase = e.data.isIDBDatabase; + } else { + testException = e.data.error; + } + + setTimeout(testFinishedCallback, 0, testIsIDBDatabase, testException); + } + + const name = window.location.pathname + "_sharedWorker"; + w.port.postMessage(name); + } + </script> + + </head> + + <body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/browser_permissionsSharedWorker.js b/dom/indexedDB/test/browser_permissionsSharedWorker.js new file mode 100644 index 000000000..59fe53326 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsSharedWorker.js @@ -0,0 +1,14 @@ +onconnect = function(e) { + e.ports[0].onmessage = function(e) { + var request = indexedDB.open(e.data, { version: 1, + storage: "persistent" }); + request.onsuccess = function(event) { + e.target.postMessage({ status: 'success', + isIDBDatabase: (event.target.result instanceof IDBDatabase) }); + } + + request.onerror = function(event) { + e.target.postMessage({ status: 'error', error: event.target.error.name }); + } + } +} diff --git a/dom/indexedDB/test/browser_permissionsWorker.html b/dom/indexedDB/test/browser_permissionsWorker.html new file mode 100644 index 000000000..c53adf975 --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsWorker.html @@ -0,0 +1,34 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + let testIsIDBDatabase; + let testException; + + function runTest() { + let w = new Worker('browser_permissionsWorker.js'); + w.onmessage = function(e) { + if (e.data.status == 'success') { + testIsIDBDatabase = e.data.isIDBDatabase; + } else { + testException = e.data.error; + } + + setTimeout(testFinishedCallback, 0, testIsIDBDatabase, testException); + } + + const name = window.location.pathname; + w.postMessage(name); + } + </script> + + </head> + + <body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/browser_permissionsWorker.js b/dom/indexedDB/test/browser_permissionsWorker.js new file mode 100644 index 000000000..3712acdeb --- /dev/null +++ b/dom/indexedDB/test/browser_permissionsWorker.js @@ -0,0 +1,12 @@ +onmessage = function(e) { + var request = indexedDB.open(e.data, { version: 1, + storage: "persistent" }); + request.onsuccess = function(event) { + postMessage({ status: 'success', + isIDBDatabase: (event.target.result instanceof IDBDatabase) }); + } + + request.onerror = function(event) { + postMessage({ status: 'error', error: event.target.error.name }); + } +} diff --git a/dom/indexedDB/test/browser_perwindow_privateBrowsing.js b/dom/indexedDB/test/browser_perwindow_privateBrowsing.js new file mode 100644 index 000000000..08d329cbc --- /dev/null +++ b/dom/indexedDB/test/browser_perwindow_privateBrowsing.js @@ -0,0 +1,69 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const testPageURL = "http://mochi.test:8888/browser/" + + "dom/indexedDB/test/browser_permissionsPrompt.html"; +const notificationID = "indexedDB-permissions-prompt"; + +function test() +{ + waitForExplicitFinish(); + // Avoids the actual prompt + setPermission(testPageURL, "indexedDB"); + executeSoon(test1); +} + +function test1() +{ + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function () { + if (content.location != testPageURL) { + content.location = testPageURL; + return; + } + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(isIDBDatabase, + "First database creation was successful"); + ok(!exception, "No exception"); + gBrowser.removeCurrentTab(); + + executeSoon(test2); + }); + }, true); + content.location = testPageURL; +} + +function test2() +{ + var win = OpenBrowserWindow({private: true}); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + executeSoon(() => test3(win)); + }, false); + registerCleanupFunction(() => win.close()); +} + +function test3(win) +{ + win.gBrowser.selectedTab = win.gBrowser.addTab(); + win.gBrowser.selectedBrowser.addEventListener("load", function () { + if (win.content.location != testPageURL) { + win.content.location = testPageURL; + return; + } + win.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + setFinishedCallback(function(isIDBDatabase, exception) { + ok(!isIDBDatabase, "No database"); + is(exception, "InvalidStateError", "Correct exception"); + win.gBrowser.removeCurrentTab(); + + executeSoon(finish); + }, win); + }, true); + win.content.location = testPageURL; +} diff --git a/dom/indexedDB/test/bug839193.js b/dom/indexedDB/test/bug839193.js new file mode 100644 index 000000000..0982c5594 --- /dev/null +++ b/dom/indexedDB/test/bug839193.js @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const nsIQuotaManagerService = Components.interfaces.nsIQuotaManagerService; + +var gURI = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI("http://localhost", null, null); + +function onUsageCallback(request) {} + +function onLoad() +{ + var quotaManagerService = + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService); + let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Components.interfaces.nsIScriptSecurityManager) + .createCodebasePrincipal(gURI, {}); + var quotaRequest = quotaManagerService.getUsageForPrincipal(principal, + onUsageCallback); + quotaRequest.cancel(); + Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService) + .notifyObservers(window, "bug839193-loaded", null); +} + +function onUnload() +{ + Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService) + .notifyObservers(window, "bug839193-unloaded", null); +} diff --git a/dom/indexedDB/test/bug839193.xul b/dom/indexedDB/test/bug839193.xul new file mode 100644 index 000000000..ccda48f95 --- /dev/null +++ b/dom/indexedDB/test/bug839193.xul @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<window id="main-window" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + windowtype="Browser:bug839193" + onload="onLoad()" + onunload="onUnload()" + align="stretch" + screenX="10" screenY="10" + width="600" height="600" + persist="screenX screenY width height sizemode"> + + <script type="application/javascript" src="chrome://mochitests/content/browser/dom/indexedDB/test/bug839193.js"/> +</window> diff --git a/dom/indexedDB/test/chrome.ini b/dom/indexedDB/test/chrome.ini new file mode 100644 index 000000000..a809b45b5 --- /dev/null +++ b/dom/indexedDB/test/chrome.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = chromeHelpers.js + +[test_globalObjects_chrome.xul] +[test_globalObjects_other.xul] diff --git a/dom/indexedDB/test/chromeHelpers.js b/dom/indexedDB/test/chromeHelpers.js new file mode 100644 index 000000000..16508f62a --- /dev/null +++ b/dom/indexedDB/test/chromeHelpers.js @@ -0,0 +1,42 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; + +var testGenerator = testSteps(); + +if (!window.runTest) { + window.runTest = function() + { + SimpleTest.waitForExplicitFinish(); + + testGenerator.next(); + } +} + +function finishTest() +{ + SimpleTest.executeSoon(function() { + testGenerator.close(); + SimpleTest.finish(); + }); +} + +function grabEventAndContinueHandler(event) +{ + testGenerator.send(event); +} + +function continueToNextStep() +{ + SimpleTest.executeSoon(function() { + testGenerator.next(); + }); +} + +function errorHandler(event) +{ + throw new Error("indexedDB error, code " + event.target.error.name); +} diff --git a/dom/indexedDB/test/error_events_abort_transactions_iframe.html b/dom/indexedDB/test/error_events_abort_transactions_iframe.html new file mode 100644 index 000000000..fd0414975 --- /dev/null +++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html @@ -0,0 +1,241 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript;version=1.7"> + + let testGenerator = testSteps(); + + function ok(val, message) { + val = val ? "true" : "false"; + window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message + + "');", "*"); + } + + function is(a, b, message) { + ok(a == b, message); + } + + function grabEventAndContinueHandler(event) { + testGenerator.send(event); + } + + function errorHandler(event) { + ok(false, "indexedDB error, code " + event.target.errorCcode); + finishTest(); + } + + function unexpectedSuccessHandler(event) { + ok(false, "got success when it was not expected!"); + finishTest(); + } + + function finishTest() { + // Let window.onerror have a chance to fire + setTimeout(function() { + setTimeout(function() { + testGenerator.close(); + window.parent.postMessage("SimpleTest.finish();", "*"); + }, 0); + }, 0); + } + + window.onerror = function(message, filename, lineno) { + is(message, "ConstraintError", "Expect a constraint error"); + }; + + function testSteps() { + window.parent.SpecialPowers.addPermission("indexedDB", true, document); + + let request = indexedDB.open(window.location.pathname, 1); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = grabEventAndContinueHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onversionchange = function(event) { + event.target.close(); + }; + + is(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + + let trans = event.target.transaction; + + trans.oncomplete = unexpectedSuccessHandler; + trans.onabort = grabEventAndContinueHandler; + + let objectStore = db.createObjectStore("foo"); + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + + let originalRequest = request; + request = objectStore.add({}, 1); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + request = objectStore.add({}, 1); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = function(event) { + // Don't do anything! ConstraintError is expected in window.onerror. + } + event = yield undefined; + + is(event.type, "abort", "Got a transaction abort event"); + is(db.version, 0, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + is(trans.error.name, "ConstraintError", "Right error"); + ok(trans.error === request.error, "Object identity holds"); + is(originalRequest.transaction, trans, "request.transaction should still be set"); + + event = yield undefined; + is(event.type, "error", "Got request error event"); + is(event.target, originalRequest, "error event has right target"); + is(event.target.error.name, "AbortError", "Right error"); + is(originalRequest.transaction, null, "request.transaction should now be null"); + // Skip the verification of ConstraintError in window.onerror. + event.preventDefault(); + + request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + db.onversionchange = function(event) { + event.target.close(); + }; + + event.target.transaction.oncomplete = grabEventAndContinueHandler; + event.target.transaction.onabort = unexpectedSuccessHandler; + + is(db.version, "1", "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + + objectStore = db.createObjectStore("foo"); + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + + objectStore.createIndex("baz", "key.path"); + objectStore.createIndex("dontDeleteMe", ""); + + is(objectStore.indexNames.length, 2, "Correct indexNames length"); + ok(objectStore.indexNames.contains("baz"), "Has correct index"); + ok(objectStore.indexNames.contains("dontDeleteMe"), "Has correct index"); + + let objectStoreForDeletion = db.createObjectStore("bar"); + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("bar"), "Has correct objectStore"); + + objectStoreForDeletion.createIndex("foo", "key.path"); + + is(objectStoreForDeletion.indexNames.length, 1, "Correct indexNames length"); + ok(objectStoreForDeletion.indexNames.contains("foo"), "Has correct index"); + + request = objectStore.add({}, 1); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + request = objectStore.add({}, 1); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = function(event) { + // Expected, but prevent the abort. + event.preventDefault(); + } + event = yield undefined; + + is(event.type, "complete", "Got a transaction complete event"); + + is(db.version, "1", "Correct version"); + is(db.objectStoreNames.length, 2, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + ok(db.objectStoreNames.contains("bar"), "Has correct objectStore"); + + request = indexedDB.open(window.location.pathname, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + db.onversionchange = function(event) { + event.target.close(); + }; + + trans = event.target.transaction; + trans.oncomplete = unexpectedSuccessHandler; + + is(db.version, "2", "Correct version"); + is(db.objectStoreNames.length, 2, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + ok(db.objectStoreNames.contains("bar"), "Has correct objectStore"); + + let createdObjectStore = db.createObjectStore("newlyCreated"); + objectStore = trans.objectStore("foo"); + let deletedObjectStore = trans.objectStore("bar"); + deletedObjectStore.deleteIndex("foo"); + db.deleteObjectStore("bar"); + + createdObjectStore.createIndex("newIndex", "key.path"); + objectStore.createIndex("newIndex", "key.path"); + objectStore.deleteIndex("baz"); + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("newlyCreated"), "Has correct objectStore"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + + is(createdObjectStore.indexNames.length, 1, "Correct indexNames length"); + ok(createdObjectStore.indexNames.contains("newIndex"), "Has correct index"); + + is(objectStore.indexNames.length, 2, "Correct indexNames length"); + ok(objectStore.indexNames.contains("dontDeleteMe"), "Has correct index"); + ok(objectStore.indexNames.contains("newIndex"), "Has correct index"); + + // ConstraintError is expected in window.onerror. + objectStore.add({}, 1); + trans.onabort = grabEventAndContinueHandler; + + event = yield undefined; + + // Test that the world has been restored. + is(db.version, "1", "Correct version"); + is(db.objectStoreNames.length, 2, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + ok(db.objectStoreNames.contains("bar"), "Has correct objectStore"); + + is(objectStore.indexNames.length, 2, "Correct indexNames length"); + ok(objectStore.indexNames.contains("dontDeleteMe"), "Has correct index"); + ok(objectStore.indexNames.contains("baz"), "Has correct index"); + + is(createdObjectStore.indexNames.length, 0, "Correct indexNames length"); + + is(deletedObjectStore.indexNames.length, 1, "Correct indexNames length"); + ok(deletedObjectStore.indexNames.contains("foo"), "Has correct index"); + + request.onerror = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "error", "Got request error event"); + is(event.target.error.name, "AbortError", "Right error"); + // Skip the verification of ConstraintError in window.onerror. + event.preventDefault(); + + finishTest(); + yield undefined; + } + </script> + +</head> + +<body onload="testGenerator.next();"></body> + +</html> diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html new file mode 100644 index 000000000..c571421c8 --- /dev/null +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -0,0 +1,148 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript;version=1.7"> + + let testGenerator = testSteps(); + + function ok(val, message) { + val = val ? "true" : "false"; + window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message + + "');", "*"); + } + + function grabEventAndContinueHandler(event) { + testGenerator.send(event); + } + + function errorHandler(event) { + ok(false, "indexedDB error, code " + event.target.error.name); + finishTest(); + } + + function finishTest() { + // Let window.onerror have a chance to fire + setTimeout(function() { + setTimeout(function() { + testGenerator.close(); + ok(windowErrorCount == 1, "Good window.onerror count"); + window.parent.postMessage("SimpleTest.finish();", "*"); + }, 0); + }, 0); + } + + const eventChain = [ + "IDBRequest", + "IDBTransaction", + "IDBDatabase" + ]; + + let captureCount = 0; + let bubbleCount = 0; + let atTargetCount = 0; + let windowErrorCount = 0; + + window.onerror = function(event) { + ok(!windowErrorCount++, "Correct number of window.onerror events"); + setTimeout(function() { testGenerator.next(); }, 0); + }; + + function errorEventCounter(event) { + ok(event.type == "error", "Got an error event"); + ok(event.target instanceof window[eventChain[0]], + "Correct event.target"); + + let constructor; + if (event.eventPhase == event.AT_TARGET) { + atTargetCount++; + constructor = eventChain[0]; + } + else if (event.eventPhase == event.CAPTURING_PHASE) { + constructor = eventChain[eventChain.length - 1 - captureCount++]; + } + else if (event.eventPhase == event.BUBBLING_PHASE) { + constructor = eventChain[++bubbleCount]; + if (windowErrorCount && bubbleCount == eventChain.length - 1) { + event.preventDefault(); + } + } + ok(event.currentTarget instanceof window[constructor], + "Correct event.currentTarget"); + + if (bubbleCount == eventChain.length - 1) { + ok(bubbleCount == captureCount, + "Got same number of calls for both phases"); + ok(atTargetCount == 1, "Got one atTarget event"); + + captureCount = bubbleCount = atTargetCount = 0; + if (windowErrorCount) { + finishTest(); + } + } + } + + function testSteps() { + window.parent.SpecialPowers.addPermission("indexedDB", true, document); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorEventCounter; + db.addEventListener("error", errorEventCounter, true); + + event.target.onsuccess = grabEventAndContinueHandler; + + db.createObjectStore("foo", { autoIncrement: true }); + yield undefined; + + let transaction = db.transaction("foo", "readwrite"); + transaction.addEventListener("error", errorEventCounter, false); + transaction.addEventListener("error", errorEventCounter, true); + + let objectStore = transaction.objectStore("foo"); + + request = objectStore.add({}, 1); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + request = objectStore.add({}, 1); + request.onsuccess = function(event) { + ok(false, "Did not expect second add to succeed."); + }; + request.onerror = errorEventCounter; + yield undefined; + + transaction = db.transaction("foo", "readwrite"); + transaction.addEventListener("error", errorEventCounter, false); + transaction.addEventListener("error", errorEventCounter, true); + + objectStore = transaction.objectStore("foo"); + + request = objectStore.add({}, 1); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + request = objectStore.add({}, 1); + request.onsuccess = function(event) { + ok(false, "Did not expect second add to succeed."); + }; + request.onerror = errorEventCounter; + yield undefined; + } + </script> + +</head> + +<body onload="testGenerator.next();"></body> + +</html> diff --git a/dom/indexedDB/test/exceptions_in_events_iframe.html b/dom/indexedDB/test/exceptions_in_events_iframe.html new file mode 100644 index 000000000..e8002d56c --- /dev/null +++ b/dom/indexedDB/test/exceptions_in_events_iframe.html @@ -0,0 +1,182 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + let testGenerator = testSteps(); + + function ok(val, message) { + val = val ? "true" : "false"; + window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message + + "');", "*"); + } + + function is(a, b, message) { + ok(a == b, message); + } + + function grabEventAndContinueHandler(event) { + testGenerator.send(event); + } + + function errorHandler(event) { + ok(false, "indexedDB error, code " + event.target.error.name); + finishTest(); + } + + function unexpectedSuccessHandler(event) { + ok(false, "got success when it was not expected!"); + finishTest(); + } + + function finishTest() { + // Let window.onerror have a chance to fire + setTimeout(function() { + setTimeout(function() { + testGenerator.close(); + window.parent.postMessage("SimpleTest.finish();", "*"); + }, 0); + }, 0); + } + + window.onerror = function() { + return false; + }; + + function testSteps() { + window.parent.SpecialPowers.addPermission("indexedDB", true, document); + + // Test 1: Throwing an exception in an upgradeneeded handler should + // abort the versionchange transaction and fire an error at the request. + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = function () { + let transaction = request.transaction; + transaction.oncomplete = unexpectedSuccessHandler; + transaction.onabort = grabEventAndContinueHandler + throw "STOP"; + }; + + let event = yield undefined; + is(event.type, "abort", + "Throwing during an upgradeneeded event should abort the transaction."); + is(event.target.error.name, "AbortError", "Got AbortError object"); + + request.onerror = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "error", + "Throwing during an upgradeneeded event should fire an error."); + + // Test 2: Throwing during a request's success handler should abort the + // transaction. + request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let openrequest = request; + event = yield undefined; + + request.onupgradeneeded = unexpectedSuccessHandler; + + let db = event.target.result; + db.onerror = function(event) { + event.preventDefault(); + }; + + event.target.transaction.oncomplete = unexpectedSuccessHandler; + event.target.transaction.onabort = grabEventAndContinueHandler; + + is(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + + let objectStore = db.createObjectStore("foo"); + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + + request = objectStore.add({}, 1); + request.onsuccess = function(event) { + throw "foo"; + }; + + event = yield undefined; + + is(event.type, "abort", "Got transaction abort event"); + is(event.target.error.name, "AbortError", "Got AbortError object"); + openrequest.onerror = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "error", "Got IDBOpenDBRequest error event"); + is(event.target, openrequest, "Right event target"); + is(event.target.error.name, "AbortError", "Right error name"); + + // Test 3: Throwing during a request's error handler should abort the + // transaction, even if preventDefault is called on the error event. + request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + openrequest = request; + event = yield undefined; + + request.onupgradeneeded = unexpectedSuccessHandler; + + db = event.target.result; + db.onerror = function(event) { + event.preventDefault(); + }; + + event.target.transaction.oncomplete = unexpectedSuccessHandler; + event.target.transaction.onabort = grabEventAndContinueHandler; + + is(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + + objectStore = db.createObjectStore("foo"); + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + ok(db.objectStoreNames.contains("foo"), "Has correct objectStore"); + + request = objectStore.add({}, 1); + request.onerror = errorHandler; + request = objectStore.add({}, 1); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = function (event) { + event.preventDefault(); + throw "STOP"; + }; + + event = yield undefined; + + is(event.type, "abort", "Got transaction abort event"); + is(event.target.error.name, "AbortError", "Got AbortError object"); + openrequest.onerror = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "error", "Got IDBOpenDBRequest error event"); + is(event.target, openrequest, "Right event target"); + is(event.target.error.name, "AbortError", "Right error name"); + + finishTest(); + yield undefined; + } + </script> + +</head> + +<body onload="testGenerator.next();"></body> + +</html> diff --git a/dom/indexedDB/test/extensions/bootstrap.js b/dom/indexedDB/test/extensions/bootstrap.js new file mode 100644 index 000000000..357ac462e --- /dev/null +++ b/dom/indexedDB/test/extensions/bootstrap.js @@ -0,0 +1,84 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var Ci = Components.interfaces; +var Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function testForExpectedSymbols(stage, data) { + const expectedSymbols = [ "IDBKeyRange", "indexedDB" ]; + for (var symbol of expectedSymbols) { + Services.prefs.setBoolPref("indexeddbtest.bootstrap." + stage + "." + + symbol, symbol in this); + } +} + +function GlobalObjectsComponent() { + this.wrappedJSObject = this; +} + +GlobalObjectsComponent.prototype = +{ + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]), + + runTest: function() { + const name = "Splendid Test"; + + let ok = this.ok; + let finishTest = this.finishTest; + + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = function(event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); + } + request.onsuccess = function(event) { + let db = event.target.result; + ok(db, "Got database"); + finishTest(); + } + } +}; + +var gFactory = { + register: function() { + var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + var classID = Components.ID("{d6f85dcb-537d-447e-b783-75d4b405622d}"); + var description = "IndexedDBTest"; + var contractID = "@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"; + var factory = XPCOMUtils._getFactory(GlobalObjectsComponent); + + registrar.registerFactory(classID, description, contractID, factory); + + this.unregister = function() { + registrar.unregisterFactory(classID, factory); + delete this.unregister; + }; + } +}; + +function install(data, reason) { + testForExpectedSymbols("install"); +} + +function startup(data, reason) { + testForExpectedSymbols("startup"); + gFactory.register(); +} + +function shutdown(data, reason) { + testForExpectedSymbols("shutdown"); + gFactory.unregister(); +} + +function uninstall(data, reason) { + testForExpectedSymbols("uninstall"); +} diff --git a/dom/indexedDB/test/extensions/indexedDB-test@mozilla.org.xpi b/dom/indexedDB/test/extensions/indexedDB-test@mozilla.org.xpi Binary files differnew file mode 100644 index 000000000..bbe2430e2 --- /dev/null +++ b/dom/indexedDB/test/extensions/indexedDB-test@mozilla.org.xpi diff --git a/dom/indexedDB/test/extensions/install.rdf b/dom/indexedDB/test/extensions/install.rdf new file mode 100644 index 000000000..e7afc68d6 --- /dev/null +++ b/dom/indexedDB/test/extensions/install.rdf @@ -0,0 +1,31 @@ +<?xml version="1.0"?> + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:name>IndexedDBTest</em:name> + <em:description>IndexedDB functions for use in testing.</em:description> + <em:creator>Mozilla</em:creator> + <em:version>2016.03.09</em:version> + <em:id>indexedDB-test@mozilla.org</em:id> + <em:type>2</em:type> + <em:bootstrap>true</em:bootstrap> + <em:targetApplication> + <Description> + <!-- Firefox --> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <Description> + <!-- Fennec --> + <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF> diff --git a/dom/indexedDB/test/extensions/moz.build b/dom/indexedDB/test/extensions/moz.build new file mode 100644 index 000000000..a810e8a9e --- /dev/null +++ b/dom/indexedDB/test/extensions/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPI_NAME = 'indexedDB' + +FINAL_TARGET_FILES += [ + 'bootstrap.js', + 'install.rdf', +] + +TEST_HARNESS_FILES.testing.mochitest.extensions += [ + 'indexedDB-test@mozilla.org.xpi', +] diff --git a/dom/indexedDB/test/file.js b/dom/indexedDB/test/file.js new file mode 100644 index 000000000..a0287cbe0 --- /dev/null +++ b/dom/indexedDB/test/file.js @@ -0,0 +1,266 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var bufferCache = []; +var utils = SpecialPowers.getDOMWindowUtils(window); + +function getBuffer(size) +{ + let buffer = new ArrayBuffer(size); + is(buffer.byteLength, size, "Correct byte length"); + return buffer; +} + +function getRandomBuffer(size) +{ + let buffer = getBuffer(size); + let view = new Uint8Array(buffer); + for (let i = 0; i < size; i++) { + view[i] = parseInt(Math.random() * 255) + } + return buffer; +} + +function getView(size) +{ + let buffer = new ArrayBuffer(size); + let view = new Uint8Array(buffer); + is(buffer.byteLength, size, "Correct byte length"); + return view; +} + +function getRandomView(size) +{ + let view = getView(size); + for (let i = 0; i < size; i++) { + view[i] = parseInt(Math.random() * 255) + } + return view; +} + +function compareBuffers(buffer1, buffer2) +{ + if (buffer1.byteLength != buffer2.byteLength) { + return false; + } + let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1); + let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2); + for (let i = 0; i < buffer1.byteLength; i++) { + if (view1[i] != view2[i]) { + return false; + } + } + return true; +} + +function getBlob(type, view) +{ + return new Blob([view], {type: type}); +} + +function getFile(name, type, view) +{ + return new File([view], name, {type: type}); +} + +function getRandomBlob(size) +{ + return getBlob("binary/random", getRandomView(size)); +} + +function getRandomFile(name, size) +{ + return getFile(name, "binary/random", getRandomView(size)); +} + +function getNullBlob(size) +{ + return getBlob("binary/null", getView(size)); +} + +function getNullFile(name, size) +{ + return getFile(name, "binary/null", getView(size)); +} + +// This needs to be async to make it available on workers too. +function getWasmBinary(text) +{ + let binary = getWasmBinarySync(text); + SimpleTest.executeSoon(function() { + testGenerator.send(binary); + }); +} + +function getWasmModule(binary) +{ + let module = new WebAssembly.Module(binary); + return module; +} + +function verifyBuffers(buffer1, buffer2) +{ + ok(compareBuffers(buffer1, buffer2), "Correct buffer data"); +} + +function verifyBlob(blob1, blob2, fileId, blobReadHandler) +{ + is(blob1 instanceof Components.interfaces.nsIDOMBlob, true, + "Instance of nsIDOMBlob"); + is(blob1 instanceof File, blob2 instanceof File, + "Instance of DOM File"); + is(blob1.size, blob2.size, "Correct size"); + is(blob1.type, blob2.type, "Correct type"); + if (blob2 instanceof File) { + is(blob1.name, blob2.name, "Correct name"); + } + is(utils.getFileId(blob1), fileId, "Correct file id"); + + let buffer1; + let buffer2; + + for (let i = 0; i < bufferCache.length; i++) { + if (bufferCache[i].blob == blob2) { + buffer2 = bufferCache[i].buffer; + break; + } + } + + if (!buffer2) { + let reader = new FileReader(); + reader.readAsArrayBuffer(blob2); + reader.onload = function(event) { + buffer2 = event.target.result; + bufferCache.push({ blob: blob2, buffer: buffer2 }); + if (buffer1) { + verifyBuffers(buffer1, buffer2); + if (blobReadHandler) { + blobReadHandler(); + } + else { + testGenerator.next(); + } + } + } + } + + let reader = new FileReader(); + reader.readAsArrayBuffer(blob1); + reader.onload = function(event) { + buffer1 = event.target.result; + if (buffer2) { + verifyBuffers(buffer1, buffer2); + if (blobReadHandler) { + blobReadHandler(); + } + else { + testGenerator.next(); + } + } + } +} + +function verifyBlobArray(blobs1, blobs2, expectedFileIds) +{ + is(blobs1 instanceof Array, true, "Got an array object"); + is(blobs1.length, blobs2.length, "Correct length"); + + if (!blobs1.length) { + return; + } + + let verifiedCount = 0; + + function blobReadHandler() { + if (++verifiedCount == blobs1.length) { + testGenerator.next(); + } + else { + verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount], + expectedFileIds[verifiedCount], blobReadHandler); + } + } + + verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount], + expectedFileIds[verifiedCount], blobReadHandler); +} + +function verifyMutableFile(mutableFile1, file2) +{ + ok(mutableFile1 instanceof IDBMutableFile, "Instance of IDBMutableFile"); + is(mutableFile1.name, file2.name, "Correct name"); + is(mutableFile1.type, file2.type, "Correct type"); + continueToNextStep(); +} + +function verifyView(view1, view2) +{ + is(view1.byteLength, view2.byteLength, "Correct byteLength"); + verifyBuffers(view1, view2); + continueToNextStep(); +} + +function verifyWasmModule(module1, module2) +{ + let getGlobalForObject = SpecialPowers.Cu.getGlobalForObject; + let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); + let wasmExtractCode = SpecialPowers.unwrap(testingFunctions.wasmExtractCode); + let exp1 = wasmExtractCode(module1); + let exp2 = wasmExtractCode(module2); + let code1 = exp1.code; + let code2 = exp2.code; + ok(code1 instanceof getGlobalForObject(code1).Uint8Array, "Instance of Uint8Array"); + ok(code2 instanceof getGlobalForObject(code1).Uint8Array, "Instance of Uint8Array"); + ok(code1.length == code2.length, "Correct length"); + verifyBuffers(code1, code2); + continueToNextStep(); +} + +function grabFileUsageAndContinueHandler(request) +{ + testGenerator.send(request.result.fileUsage); +} + +function getCurrentUsage(usageHandler) +{ + let qms = SpecialPowers.Services.qms; + let principal = SpecialPowers.wrap(document).nodePrincipal; + let cb = SpecialPowers.wrapCallback(usageHandler); + qms.getUsageForPrincipal(principal, cb); +} + +function getFileId(file) +{ + return utils.getFileId(file); +} + +function getFilePath(file) +{ + return utils.getFilePath(file); +} + +function hasFileInfo(name, id) +{ + return utils.getFileReferences(name, id); +} + +function getFileRefCount(name, id) +{ + let count = {}; + utils.getFileReferences(name, id, null, count); + return count.value; +} + +function getFileDBRefCount(name, id) +{ + let count = {}; + utils.getFileReferences(name, id, null, {}, count); + return count.value; +} + +function flushPendingFileDeletions() +{ + utils.flushPendingFileDeletions(); +} diff --git a/dom/indexedDB/test/file_app_isolation.html b/dom/indexedDB/test/file_app_isolation.html new file mode 100644 index 000000000..0ff74e689 --- /dev/null +++ b/dom/indexedDB/test/file_app_isolation.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<html> + <body> + foobar! + </body> + <script> + var data = [ + { id: "0", name: "foo" }, + ]; + + var action = window.location.search.substring(1); + var finished = false; + var created = false; // We use that for 'read-no' action. + + function finish(value) { + value ? alert('success') : alert('failure'); + finished = true; + } + + var request = window.indexedDB.open('AppIsolationTest'); + + request.onupgradeneeded = function(event) { + if (finished) { + finish(false); + return; + } + + switch (action) { + case 'read-no': + created = true; + break; + case 'read-yes': + finish(false); + break; + case 'write': + created = true; + + var db = event.target.result; + + var objectStore = db.createObjectStore("test", { keyPath: "id" }); + for (var i in data) { + objectStore.add(data[i]); + } + break; + } + } + + request.onsuccess = function(event) { + if (finished) { + finish(false); + return; + } + + var db = event.target.result; + + // Think about close the db! + switch (action) { + case 'read-no': + db.close(); + + if (created) { // That means we have created it. + indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() { + finish(true); + }; + } else { + finish(false); + } + break; + case 'read-yes': + db.transaction("test").objectStore("test").get("0").onsuccess = function(event) { + var name = event.target.result.name; + db.close(); + + indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() { + finish(name == 'foo'); + }; + }; + break; + case 'write': + db.close(); + + // Success only if the db was actually created. + finish(created); + break; + } + }; + </script> +</html> diff --git a/dom/indexedDB/test/file_app_isolation.js b/dom/indexedDB/test/file_app_isolation.js new file mode 100644 index 000000000..d6ed3fab4 --- /dev/null +++ b/dom/indexedDB/test/file_app_isolation.js @@ -0,0 +1,161 @@ +SimpleTest.waitForExplicitFinish(); + +var fileTestOnCurrentOrigin = (location.protocol + '//' + location.host + location.pathname) + .replace('test_', 'file_') + .replace('_inproc', '').replace('_oop', ''); + +var previousPrefs = { + mozBrowserFramesEnabled: undefined, + oop_by_default: undefined, +}; + +try { + previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled'); +} catch(e) +{ +} + +try { + previousPrefs.oop_by_default = SpecialPowers.getBoolPref('dom.ipc.browser_frames.oop_by_default'); +} catch(e) { +} + +SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true); +SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", location.pathname.indexOf('_inproc') == -1); + +SpecialPowers.addPermission("browser", true, window.document); + +var gData = [ + // APP 1 + { + app: 'http://example.org/manifest.webapp', + action: 'read-no', + src: fileTestOnCurrentOrigin, + }, + { + app: 'http://example.org/manifest.webapp', + action: 'write', + src: fileTestOnCurrentOrigin, + }, + { + app: 'http://example.org/manifest.webapp', + action: 'read-yes', + src: fileTestOnCurrentOrigin, + }, + // APP 2 + { + app: 'https://example.com/manifest.webapp', + action: 'read-no', + src: fileTestOnCurrentOrigin, + }, + { + app: 'https://example.com/manifest.webapp', + action: 'write', + src: fileTestOnCurrentOrigin, + }, + { + app: 'https://example.com/manifest.webapp', + action: 'read-yes', + src: fileTestOnCurrentOrigin, + }, + // Browser + { + browser: true, + action: 'read-no', + src: fileTestOnCurrentOrigin, + }, + { + browser: true, + action: 'write', + src: fileTestOnCurrentOrigin, + }, + { + browser: true, + action: 'read-yes', + src: fileTestOnCurrentOrigin, + }, +]; + +function runTest() { + for (var i in gData) { + var iframe = document.createElement('iframe'); + var data = gData[i]; + + if (data.app) { + iframe.setAttribute('mozbrowser', ''); + iframe.setAttribute('mozapp', data.app); + } else if (data.browser) { + iframe.setAttribute('mozbrowser', ''); + } + + if (data.app || data.browser) { + iframe.addEventListener('mozbrowsershowmodalprompt', function(e) { + is(e.detail.message, 'success', 'test number ' + i); + +// document.getElementById('content').removeChild(iframe); + + i++; + if (i >= gData.length) { + if (previousPrefs.mozBrowserFramesEnabled !== undefined) { + SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled); + } + if (previousPrefs.oop_by_default !== undefined) { + SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", previousPrefs.oop_by_default); + } + + SpecialPowers.removePermission("browser", window.document); + + indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() { + SimpleTest.finish(); + }; + } else { + gTestRunner.next(); + } + }); + } + + iframe.src = data.src + '?' + data.action; + + document.getElementById('content').appendChild(iframe); + + yield undefined; + } +} + +var gTestRunner = runTest(); + +function startTest() { + var request = window.indexedDB.open('AppIsolationTest'); + var created = false; + + request.onupgradeneeded = function(event) { + created = true; + var db = event.target.result; + var data = [ + { id: "0", name: "foo" }, + ]; + var objectStore = db.createObjectStore("test", { keyPath: "id" }); + for (var i in data) { + objectStore.add(data[i]); + } + } + + request.onsuccess = function(event) { + var db = event.target.result; + is(created, true, "we should have created the db"); + + db.transaction("test").objectStore("test").get("0").onsuccess = function(event) { + is(event.target.result.name, 'foo', 'data have been written'); + db.close(); + + gTestRunner.next(); + }; + } +} + +// TODO: remove unsetting network.disable.ipc.security as part of bug 820712 +SpecialPowers.pushPrefEnv({ + "set": [ + ["network.disable.ipc.security", true], + ] +}, startTest); diff --git a/dom/indexedDB/test/head.js b/dom/indexedDB/test/head.js new file mode 100644 index 000000000..898a40e8f --- /dev/null +++ b/dom/indexedDB/test/head.js @@ -0,0 +1,158 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gActiveListeners = {}; + +function registerPopupEventHandler(eventName, callback) { + gActiveListeners[eventName] = function (event) { + if (event.target != PopupNotifications.panel) + return; + PopupNotifications.panel.removeEventListener(eventName, + gActiveListeners[eventName], + false); + delete gActiveListeners[eventName]; + + callback.call(PopupNotifications.panel); + } + PopupNotifications.panel.addEventListener(eventName, + gActiveListeners[eventName], + false); +} + +function unregisterPopupEventHandler(eventName) +{ + PopupNotifications.panel.removeEventListener(eventName, + gActiveListeners[eventName], + false); + delete gActiveListeners[eventName]; +} + +function unregisterAllPopupEventHandlers() +{ + for (let eventName in gActiveListeners) { + PopupNotifications.panel.removeEventListener(eventName, + gActiveListeners[eventName], + false); + } + gActiveListeners = {}; +} + +function triggerMainCommand(popup) +{ + info("triggering main command"); + let notifications = popup.childNodes; + ok(notifications.length > 0, "at least one notification displayed"); + let notification = notifications[0]; + info("triggering command: " + notification.getAttribute("buttonlabel")); + + // 20, 10 so that the inner button is hit + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); +} + +function triggerSecondaryCommand(popup, index) +{ + info("triggering secondary command, " + index); + let notifications = popup.childNodes; + ok(notifications.length > 0, "at least one notification displayed"); + let notification = notifications[0]; + + // Cancel the arrow panel slide-in transition (bug 767133) such that + // it won't interfere with us interacting with the dropdown. + SpecialPowers.wrap(document).getAnonymousNodes(popup)[0].style.transition = "none"; + + notification.button.focus(); + + popup.addEventListener("popupshown", function () { + popup.removeEventListener("popupshown", arguments.callee, false); + + // Press down until the desired command is selected + for (let i = 0; i <= index; i++) + EventUtils.synthesizeKey("VK_DOWN", {}); + + // Activate + EventUtils.synthesizeKey("VK_RETURN", {}); + }, false); + + // One down event to open the popup + EventUtils.synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) }); +} + +function dismissNotification(popup) +{ + info("dismissing notification"); + executeSoon(function () { + EventUtils.synthesizeKey("VK_ESCAPE", {}); + }); +} + +function setFinishedCallback(callback, win) +{ + if (!win) { + win = window; + } + ContentTask.spawn(win.gBrowser.selectedBrowser, null, function*() { + return yield new Promise(resolve => { + content.wrappedJSObject.testFinishedCallback = (result, exception) => { + info("got finished callback"); + resolve({result, exception}); + }; + }); + }).then(({result, exception}) => { + callback(result, exception); + }); +} + +function dispatchEvent(eventName) +{ + info("dispatching event: " + eventName); + let event = document.createEvent("Events"); + event.initEvent(eventName, false, false); + gBrowser.selectedBrowser.contentWindow.dispatchEvent(event); +} + +function setPermission(url, permission) +{ + const nsIPermissionManager = Components.interfaces.nsIPermissionManager; + + let uri = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService) + .newURI(url, null, null); + let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + Components.classes["@mozilla.org/permissionmanager;1"] + .getService(nsIPermissionManager) + .addFromPrincipal(principal, permission, + nsIPermissionManager.ALLOW_ACTION); +} + +function removePermission(url, permission) +{ + let uri = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService) + .newURI(url, null, null); + let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager) + .removeFromPrincipal(principal, permission); +} + +function getPermission(url, permission) +{ + let uri = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService) + .newURI(url, null, null); + let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + return Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager) + .testPermissionFromPrincipal(principal, permission); +} diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js new file mode 100644 index 000000000..e6e27f3f3 --- /dev/null +++ b/dom/indexedDB/test/helpers.js @@ -0,0 +1,628 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); +var archiveReaderEnabled = false; + +// The test js is shared between xpcshell (which has no SpecialPowers object) +// and content mochitests (where the |Components| object is accessible only as +// SpecialPowers.Components). Expose Components if necessary here to make things +// work everywhere. +// +// Even if the real |Components| doesn't exist, we might shim in a simple JS +// placebo for compat. An easy way to differentiate this from the real thing +// is whether the property is read-only or not. +var c = Object.getOwnPropertyDescriptor(this, 'Components'); +if ((!c.value || c.writable) && typeof SpecialPowers === 'object') + Components = SpecialPowers.Components; + +function executeSoon(aFun) +{ + let comp = SpecialPowers.wrap(Components); + + let thread = comp.classes["@mozilla.org/thread-manager;1"] + .getService(comp.interfaces.nsIThreadManager) + .mainThread; + + thread.dispatch({ + run: function() { + aFun(); + } + }, Components.interfaces.nsIThread.DISPATCH_NORMAL); +} + +function clearAllDatabases(callback) { + let qms = SpecialPowers.Services.qms; + let principal = SpecialPowers.wrap(document).nodePrincipal; + let request = qms.clearStoragesForPrincipal(principal); + let cb = SpecialPowers.wrapCallback(callback); + request.callback = cb; +} + +var testHarnessGenerator = testHarnessSteps(); +testHarnessGenerator.next(); + +function testHarnessSteps() { + function nextTestHarnessStep(val) { + testHarnessGenerator.send(val); + } + + let testScriptPath; + let testScriptFilename; + + let scripts = document.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + let src = scripts[i].src; + let match = src.match(/indexedDB\/test\/unit\/(test_[^\/]+\.js)$/); + if (match && match.length == 2) { + testScriptPath = src; + testScriptFilename = match[1]; + break; + } + } + + yield undefined; + + info("Running" + + (testScriptFilename ? " '" + testScriptFilename + "'" : "")); + + info("Pushing preferences"); + + SpecialPowers.pushPrefEnv( + { + "set": [ + ["dom.indexedDB.testing", true], + ["dom.indexedDB.experimental", true], + ["dom.archivereader.enabled", true], + ["dom.workers.latestJSVersion", true], + ["javascript.options.wasm", true] + ] + }, + nextTestHarnessStep + ); + yield undefined; + + info("Pushing permissions"); + + SpecialPowers.pushPermissions( + [ + { + type: "indexedDB", + allow: true, + context: document + } + ], + nextTestHarnessStep + ); + yield undefined; + + info("Clearing old databases"); + + clearAllDatabases(nextTestHarnessStep); + yield undefined; + + if (testScriptFilename && !window.disableWorkerTest) { + info("Running test in a worker"); + + let workerScriptBlob = + new Blob([ "(" + workerScript.toString() + ")();" ], + { type: "text/javascript;version=1.7" }); + let workerScriptURL = URL.createObjectURL(workerScriptBlob); + + let worker = new Worker(workerScriptURL); + + worker._expectingUncaughtException = false; + worker.onerror = function(event) { + if (worker._expectingUncaughtException) { + ok(true, "Worker had an expected error: " + event.message); + worker._expectingUncaughtException = false; + event.preventDefault(); + return; + } + ok(false, "Worker had an error: " + event.message); + worker.terminate(); + nextTestHarnessStep(); + }; + + worker.onmessage = function(event) { + let message = event.data; + switch (message.op) { + case "ok": + ok(message.condition, message.name, message.diag); + break; + + case "todo": + todo(message.condition, message.name, message.diag); + break; + + case "info": + info(message.msg); + break; + + case "ready": + worker.postMessage({ op: "load", files: [ testScriptPath ] }); + break; + + case "loaded": + worker.postMessage({ op: "start", wasmSupported: isWasmSupported() }); + break; + + case "done": + ok(true, "Worker finished"); + nextTestHarnessStep(); + break; + + case "expectUncaughtException": + worker._expectingUncaughtException = message.expecting; + break; + + case "clearAllDatabases": + clearAllDatabases(function(){ + worker.postMessage({ op: "clearAllDatabasesDone" }); + }); + break; + + case "getWasmBinary": + worker.postMessage({ op: "getWasmBinaryDone", + wasmBinary: getWasmBinarySync(message.text) }); + break; + + default: + ok(false, + "Received a bad message from worker: " + JSON.stringify(message)); + nextTestHarnessStep(); + } + }; + + URL.revokeObjectURL(workerScriptURL); + + yield undefined; + + if (worker._expectingUncaughtException) { + ok(false, "expectUncaughtException was called but no uncaught " + + "exception was detected!"); + } + + worker.terminate(); + worker = null; + + clearAllDatabases(nextTestHarnessStep); + yield undefined; + } else if (testScriptFilename) { + todo(false, + "Skipping test in a worker because it is explicitly disabled: " + + disableWorkerTest); + } else { + todo(false, + "Skipping test in a worker because it's not structured properly"); + } + + info("Running test in main thread"); + + // Now run the test script in the main thread. + testGenerator.next(); + + yield undefined; +} + +if (!window.runTest) { + window.runTest = function() + { + SimpleTest.waitForExplicitFinish(); + testHarnessGenerator.next(); + } +} + +function finishTest() +{ + SpecialPowers.notifyObserversInParentProcess(null, + "disk-space-watcher", + "free"); + + SimpleTest.executeSoon(function() { + testGenerator.close(); + testHarnessGenerator.close(); + clearAllDatabases(function() { SimpleTest.finish(); }); + }); +} + +function browserRunTest() +{ + testGenerator.next(); +} + +function browserFinishTest() +{ + setTimeout(function() { testGenerator.close(); }, 0); +} + +function grabEventAndContinueHandler(event) +{ + testGenerator.send(event); +} + +function continueToNextStep() +{ + SimpleTest.executeSoon(function() { + testGenerator.next(); + }); +} + +function continueToNextStepSync() +{ + testGenerator.next(); +} + +function errorHandler(event) +{ + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); +} + +// For error callbacks where the argument is not an event object. +function errorCallbackHandler(err) +{ + ok(false, "got unexpected error callback: " + err); + finishTest(); +} + +function expectUncaughtException(expecting) +{ + SimpleTest.expectUncaughtException(expecting); +} + +function browserErrorHandler(event) +{ + browserFinishTest(); + throw new Error("indexedDB error (" + event.code + "): " + event.message); +} + +function unexpectedSuccessHandler() +{ + ok(false, "Got success, but did not expect it!"); + finishTest(); +} + +function expectedErrorHandler(name) +{ + return function(event) { + is(event.type, "error", "Got an error event"); + is(event.target.error.name, name, "Expected error was thrown."); + event.preventDefault(); + grabEventAndContinueHandler(event); + }; +} + +function ExpectError(name, preventDefault) +{ + this._name = name; + this._preventDefault = preventDefault; +} +ExpectError.prototype = { + handleEvent: function(event) + { + is(event.type, "error", "Got an error event"); + is(event.target.error.name, this._name, "Expected error was thrown."); + if (this._preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } + grabEventAndContinueHandler(event); + } +}; + +function compareKeys(_k1_, _k2_) { + let t = typeof _k1_; + if (t != typeof _k2_) + return false; + + if (t !== "object") + return _k1_ === _k2_; + + if (_k1_ instanceof Date) { + return (_k2_ instanceof Date) && + _k1_.getTime() === _k2_.getTime(); + } + + if (_k1_ instanceof Array) { + if (!(_k2_ instanceof Array) || + _k1_.length != _k2_.length) + return false; + + for (let i = 0; i < _k1_.length; ++i) { + if (!compareKeys(_k1_[i], _k2_[i])) + return false; + } + + return true; + } + + return false; +} + +function removePermission(type, url) +{ + if (!url) { + url = window.document; + } + SpecialPowers.removePermission(type, url); +} + +function gc() +{ + SpecialPowers.forceGC(); + SpecialPowers.forceCC(); +} + +function scheduleGC() +{ + SpecialPowers.exactGC(continueToNextStep); +} + +function isWasmSupported() +{ + let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); + return testingFunctions.wasmIsSupported(); +} + +function getWasmBinarySync(text) +{ + let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); + let wasmTextToBinary = SpecialPowers.unwrap(testingFunctions.wasmTextToBinary); + let binary = wasmTextToBinary(text); + return binary; +} + +function workerScript() { + "use strict"; + + self.wasmSupported = false; + + self.repr = function(_thing_) { + if (typeof(_thing_) == "undefined") { + return "undefined"; + } + + let str; + + try { + str = _thing_ + ""; + } catch (e) { + return "[" + typeof(_thing_) + "]"; + } + + if (typeof(_thing_) == "function") { + str = str.replace(/^\s+/, ""); + let idx = str.indexOf("{"); + if (idx != -1) { + str = str.substr(0, idx) + "{...}"; + } + } + + return str; + }; + + self.ok = function(_condition_, _name_, _diag_) { + self.postMessage({ op: "ok", + condition: !!_condition_, + name: _name_, + diag: _diag_ }); + }; + + self.is = function(_a_, _b_, _name_) { + let pass = (_a_ == _b_); + let diag = pass ? "" : "got " + repr(_a_) + ", expected " + repr(_b_); + ok(pass, _name_, diag); + }; + + self.isnot = function(_a_, _b_, _name_) { + let pass = (_a_ != _b_); + let diag = pass ? "" : "didn't expect " + repr(_a_) + ", but got it"; + ok(pass, _name_, diag); + }; + + self.todo = function(_condition_, _name_, _diag_) { + self.postMessage({ op: "todo", + condition: !!_condition_, + name: _name_, + diag: _diag_ }); + }; + + self.info = function(_msg_) { + self.postMessage({ op: "info", msg: _msg_ }); + }; + + self.executeSoon = function(_fun_) { + var channel = new MessageChannel(); + channel.port1.postMessage(""); + channel.port2.onmessage = function(event) { _fun_(); }; + }; + + self.finishTest = function() { + if (self._expectingUncaughtException) { + self.ok(false, "expectUncaughtException was called but no uncaught " + + "exception was detected!"); + } + self.postMessage({ op: "done" }); + }; + + self.grabEventAndContinueHandler = function(_event_) { + testGenerator.send(_event_); + }; + + self.continueToNextStep = function() { + executeSoon(function() { + testGenerator.next(); + }); + }; + + self.continueToNextStepSync = function() { + testGenerator.next(); + }; + + self.errorHandler = function(_event_) { + ok(false, "indexedDB error, '" + _event_.target.error.name + "'"); + finishTest(); + }; + + self.unexpectedSuccessHandler = function() + { + ok(false, "Got success, but did not expect it!"); + finishTest(); + }; + + self.expectedErrorHandler = function(_name_) + { + return function(_event_) { + is(_event_.type, "error", "Got an error event"); + is(_event_.target.error.name, _name_, "Expected error was thrown."); + _event_.preventDefault(); + grabEventAndContinueHandler(_event_); + }; + }; + + self.ExpectError = function(_name_, _preventDefault_) + { + this._name = _name_; + this._preventDefault = _preventDefault_; + } + self.ExpectError.prototype = { + handleEvent: function(_event_) + { + is(_event_.type, "error", "Got an error event"); + is(_event_.target.error.name, this._name, "Expected error was thrown."); + if (this._preventDefault) { + _event_.preventDefault(); + _event_.stopPropagation(); + } + grabEventAndContinueHandler(_event_); + } + }; + + self.compareKeys = function(_k1_, _k2_) { + let t = typeof _k1_; + if (t != typeof _k2_) + return false; + + if (t !== "object") + return _k1_ === _k2_; + + if (_k1_ instanceof Date) { + return (_k2_ instanceof Date) && + _k1_.getTime() === _k2_.getTime(); + } + + if (_k1_ instanceof Array) { + if (!(_k2_ instanceof Array) || + _k1_.length != _k2_.length) + return false; + + for (let i = 0; i < _k1_.length; ++i) { + if (!compareKeys(_k1_[i], _k2_[i])) + return false; + } + + return true; + } + + return false; + } + + self.getRandomBuffer = function(_size_) { + let buffer = new ArrayBuffer(_size_); + is(buffer.byteLength, _size_, "Correct byte length"); + let view = new Uint8Array(buffer); + for (let i = 0; i < _size_; i++) { + view[i] = parseInt(Math.random() * 255) + } + return buffer; + }; + + self._expectingUncaughtException = false; + self.expectUncaughtException = function(_expecting_) { + self._expectingUncaughtException = !!_expecting_; + self.postMessage({ op: "expectUncaughtException", expecting: !!_expecting_ }); + }; + + self._clearAllDatabasesCallback = undefined; + self.clearAllDatabases = function(_callback_) { + self._clearAllDatabasesCallback = _callback_; + self.postMessage({ op: "clearAllDatabases" }); + } + + self.onerror = function(_message_, _file_, _line_) { + if (self._expectingUncaughtException) { + self._expectingUncaughtException = false; + ok(true, "Worker: expected exception [" + _file_ + ":" + _line_ + "]: '" + + _message_ + "'"); + return; + } + ok(false, + "Worker: uncaught exception [" + _file_ + ":" + _line_ + "]: '" + + _message_ + "'"); + self.finishTest(); + self.close(); + return true; + }; + + self.isWasmSupported = function() { + return self.wasmSupported; + } + + self.getWasmBinarySync = function(_text_) { + self.ok(false, "This can't be used on workers"); + } + + self.getWasmBinary = function(_text_) { + self.postMessage({ op: "getWasmBinary", text: _text_ }); + } + + self.getWasmModule = function(_binary_) { + let module = new WebAssembly.Module(_binary_); + return module; + } + + self.verifyWasmModule = function(_module) { + self.todo(false, "Need a verifyWasmModule implementation on workers"); + self.continueToNextStep(); + } + + self.onmessage = function(_event_) { + let message = _event_.data; + switch (message.op) { + case "load": + info("Worker: loading " + JSON.stringify(message.files)); + self.importScripts(message.files); + self.postMessage({ op: "loaded" }); + break; + + case "start": + self.wasmSupported = message.wasmSupported; + executeSoon(function() { + info("Worker: starting tests"); + testGenerator.next(); + }); + break; + + case "clearAllDatabasesDone": + info("Worker: all databases are cleared"); + if (self._clearAllDatabasesCallback) { + self._clearAllDatabasesCallback(); + } + break; + + case "getWasmBinaryDone": + info("Worker: get wasm binary done"); + testGenerator.send(message.wasmBinary); + break; + + default: + throw new Error("Received a bad message from parent: " + + JSON.stringify(message)); + } + }; + + self.postMessage({ op: "ready" }); +} diff --git a/dom/indexedDB/test/leaving_page_iframe.html b/dom/indexedDB/test/leaving_page_iframe.html new file mode 100644 index 000000000..690e5ff6e --- /dev/null +++ b/dom/indexedDB/test/leaving_page_iframe.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<head> + <script> +var db; +function startDBWork() { + indexedDB.open(parent.location, 1).onupgradeneeded = function(e) { + db = e.target.result; + var trans = e.target.transaction; + if (db.objectStoreNames.contains("mystore")) { + db.deleteObjectStore("mystore"); + } + var store = db.createObjectStore("mystore"); + store.add({ hello: "world" }, 42); + e.target.onsuccess = madeMod; + }; +} + +function madeMod() { + var trans = db.transaction(["mystore"], "readwrite"); + var store = trans. + objectStore("mystore"); + trans.oncomplete = function() { + parent.postMessage("didcommit", "*"); + } + + store.put({ hello: "officer" }, 42).onsuccess = function(e) { + // Make this transaction run until the end of time or until the page is + // navigated away, whichever comes first. + function doGet() { + store.get(42).onsuccess = doGet; + } + doGet(); + document.location = "about:blank"; + } + +} + </script> +</head> +<body onload="startDBWork();"> + This is page one. +</body> +</html> diff --git a/dom/indexedDB/test/mochitest-intl-api.ini b/dom/indexedDB/test/mochitest-intl-api.ini new file mode 100644 index 000000000..8ec4a172c --- /dev/null +++ b/dom/indexedDB/test/mochitest-intl-api.ini @@ -0,0 +1,10 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] + +[test_create_locale_aware_index.html] +[test_locale_aware_indexes.html] +[test_locale_aware_index_getAll.html] +[test_locale_aware_index_getAllObjects.html] diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini new file mode 100644 index 000000000..4ab55a9dc --- /dev/null +++ b/dom/indexedDB/test/mochitest.ini @@ -0,0 +1,274 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +support-files = + bfcache_iframe1.html + bfcache_iframe2.html + blob_worker_crash_iframe.html + error_events_abort_transactions_iframe.html + event_propagation_iframe.html + exceptions_in_events_iframe.html + file.js + file_app_isolation.html + file_app_isolation.js + helpers.js + leaving_page_iframe.html + service_worker.js + service_worker_client.html + third_party_iframe1.html + third_party_iframe2.html + unit/test_abort_deleted_index.js + unit/test_abort_deleted_objectStore.js + unit/test_add_put.js + unit/test_add_twice_failure.js + unit/test_advance.js + unit/test_autoIncrement.js + unit/test_autoIncrement_indexes.js + unit/test_blob_file_backed.js + unit/test_blocked_order.js + unit/test_clear.js + unit/test_complex_keyPaths.js + unit/test_count.js + unit/test_create_index.js + unit/test_create_index_with_integer_keys.js + unit/test_create_locale_aware_index.js + unit/test_create_objectStore.js + unit/test_cursor_mutation.js + unit/test_cursor_update_updates_indexes.js + unit/test_cursors.js + unit/test_database_onclose.js + unit/test_deleteDatabase.js + unit/test_deleteDatabase_interactions.js + unit/test_deleteDatabase_onblocked.js + unit/test_deleteDatabase_onblocked_duringVersionChange.js + unit/test_event_source.js + unit/test_filehandle_append_read_data.js + unit/test_getAll.js + unit/test_globalObjects_ipc.js + unit/test_globalObjects_other.js + unit/test_globalObjects_xpc.js + unit/test_global_data.js + unit/test_index_empty_keyPath.js + unit/test_index_getAll.js + unit/test_index_getAllObjects.js + unit/test_index_object_cursors.js + unit/test_index_update_delete.js + unit/test_indexes.js + unit/test_indexes_bad_values.js + unit/test_indexes_funny_things.js + unit/test_invalid_cursor.js + unit/test_invalid_version.js + unit/test_invalidate.js + unit/test_key_requirements.js + unit/test_keys.js + unit/test_locale_aware_indexes.js + unit/test_locale_aware_index_getAll.js + unit/test_locale_aware_index_getAllObjects.js + unit/test_lowDiskSpace.js + unit/test_maximal_serialized_object_size.js + unit/test_multientry.js + unit/test_names_sorted.js + unit/test_objectCursors.js + unit/test_objectStore_getAllKeys.js + unit/test_objectStore_inline_autoincrement_key_added_on_put.js + unit/test_objectStore_openKeyCursor.js + unit/test_objectStore_remove_values.js + unit/test_object_identity.js + unit/test_odd_result_order.js + unit/test_open_empty_db.js + unit/test_open_for_principal.js + unit/test_open_objectStore.js + unit/test_optionalArguments.js + unit/test_overlapping_transactions.js + unit/test_persistenceType.js + unit/test_put_get_values.js + unit/test_put_get_values_autoIncrement.js + unit/test_readonly_transactions.js + unit/test_readwriteflush_disabled.js + unit/test_remove_index.js + unit/test_rename_index.js + unit/test_rename_index_errors.js + unit/test_remove_objectStore.js + unit/test_rename_objectStore.js + unit/test_rename_objectStore_errors.js + unit/test_request_readyState.js + unit/test_setVersion.js + unit/test_setVersion_abort.js + unit/test_setVersion_events.js + unit/test_setVersion_exclusion.js + unit/test_setVersion_throw.js + unit/test_storage_manager_estimate.js + unit/test_success_events_after_abort.js + unit/test_table_locks.js + unit/test_table_rollback.js + unit/test_temporary_storage.js + unit/test_traffic_jam.js + unit/test_transaction_abort.js + unit/test_transaction_abort_hang.js + unit/test_transaction_duplicate_store_names.js + unit/test_transaction_error.js + unit/test_transaction_lifetimes.js + unit/test_transaction_lifetimes_nested.js + unit/test_transaction_ordering.js + unit/test_unique_index_update.js + unit/test_view_put_get_values.js + unit/test_wasm_cursors.js + unit/test_wasm_getAll.js + unit/test_wasm_index_getAllObjects.js + unit/test_wasm_indexes.js + unit/test_wasm_put_get_values.js + unit/test_writer_starvation.js + +[test_abort_deleted_index.html] +[test_abort_deleted_objectStore.html] +[test_add_put.html] +[test_add_twice_failure.html] +[test_advance.html] +[test_app_isolation_inproc.html] +# The app isolation tests are only supposed to run in the main process. +skip-if = e10s +[test_app_isolation_oop.html] +# The app isolation tests are only supposed to run in the main process. +skip-if = e10s +[test_autoIncrement.html] +[test_autoIncrement_indexes.html] +[test_bfcache.html] +[test_blob_archive.html] +[test_blob_file_backed.html] +[test_blob_simple.html] +[test_blob_worker_crash.html] +[test_blob_worker_xhr_post.html] +[test_blob_worker_xhr_post_multifile.html] +[test_blob_worker_xhr_read.html] +[test_blob_worker_xhr_read_slice.html] +[test_blocked_order.html] +[test_bug937006.html] +[test_clear.html] +[test_complex_keyPaths.html] +[test_count.html] +[test_create_index.html] +[test_create_index_with_integer_keys.html] +[test_create_objectStore.html] +[test_cursor_mutation.html] +[test_cursor_update_updates_indexes.html] +[test_cursors.html] +[test_database_onclose.html] +[test_deleteDatabase.html] +[test_deleteDatabase_interactions.html] +[test_deleteDatabase_onblocked.html] +[test_deleteDatabase_onblocked_duringVersionChange.html] +[test_error_events_abort_transactions.html] +[test_event_propagation.html] +[test_event_source.html] +[test_exceptions_in_events.html] +[test_file_array.html] +[test_file_cross_database_copying.html] +[test_file_delete.html] +[test_file_os_delete.html] +[test_file_put_deleted.html] +[test_file_put_get_object.html] +[test_file_put_get_values.html] +[test_file_replace.html] +[test_file_resurrection_delete.html] +[test_file_resurrection_transaction_abort.html] +[test_file_sharing.html] +[test_file_transaction_abort.html] +[test_filehandle_append_read_data.html] +[test_filehandle_compat.html] +[test_filehandle_disabled_pref.html] +[test_filehandle_getFile.html] +[test_filehandle_iteration.html] +[test_filehandle_lifetimes.html] +[test_filehandle_lifetimes_nested.html] +[test_filehandle_location.html] +[test_filehandle_ordering.html] +[test_filehandle_overlapping.html] +[test_filehandle_progress_events.html] +[test_filehandle_readonly_exceptions.html] +[test_filehandle_request_readyState.html] +[test_filehandle_serialization.html] +[test_filehandle_store_snapshot.html] +[test_filehandle_stream_tracking.html] +[test_filehandle_success_events_after_abort.html] +[test_filehandle_truncate.html] +[test_filehandle_workers.html] +[test_filehandle_write_read_data.html] +[test_getAll.html] +[test_globalObjects_content.html] +[test_global_data.html] +[test_index_empty_keyPath.html] +[test_index_getAll.html] +[test_index_getAllObjects.html] +[test_index_object_cursors.html] +[test_index_update_delete.html] +[test_indexes.html] +[test_indexes_bad_values.html] +[test_indexes_funny_things.html] +[test_invalid_cursor.html] +[test_invalid_version.html] +[test_invalidate.html] +# disabled for the moment +skip-if = true +[test_key_requirements.html] +[test_keys.html] +[test_leaving_page.html] +[test_lowDiskSpace.html] +[test_maximal_serialized_object_size.html] +[test_message_manager_ipc.html] +# This test is only supposed to run in the main process. +skip-if = e10s +[test_multientry.html] +[test_names_sorted.html] +[test_objectCursors.html] +[test_objectStore_getAllKeys.html] +[test_objectStore_inline_autoincrement_key_added_on_put.html] +[test_objectStore_openKeyCursor.html] +[test_objectStore_remove_values.html] +[test_object_identity.html] +[test_odd_result_order.html] +[test_open_empty_db.html] +[test_open_for_principal.html] +[test_open_objectStore.html] +[test_optionalArguments.html] +[test_overlapping_transactions.html] +[test_persistenceType.html] +[test_put_get_values.html] +[test_put_get_values_autoIncrement.html] +[test_readonly_transactions.html] +[test_readwriteflush_disabled.html] +[test_remove_index.html] +[test_rename_index.html] +[test_rename_index_errors.html] +[test_remove_objectStore.html] +[test_rename_objectStore.html] +[test_rename_objectStore_errors.html] +[test_request_readyState.html] +[test_sandbox.html] +[test_serviceworker.html] +[test_setVersion.html] +[test_setVersion_abort.html] +[test_setVersion_events.html] +[test_setVersion_exclusion.html] +[test_setVersion_throw.html] +[test_storage_manager_estimate.html] +[test_success_events_after_abort.html] +[test_table_locks.html] +[test_table_rollback.html] +[test_third_party.html] +[test_traffic_jam.html] +[test_transaction_abort.html] +[test_transaction_abort_hang.html] +[test_transaction_duplicate_store_names.html] +[test_transaction_error.html] +[test_transaction_lifetimes.html] +[test_transaction_lifetimes_nested.html] +[test_transaction_ordering.html] +[test_unique_index_update.html] +[test_view_put_get_values.html] +[test_wasm_cursors.html] +[test_wasm_getAll.html] +[test_wasm_index_getAllObjects.html] +[test_wasm_indexes.html] +[test_wasm_put_get_values.html] diff --git a/dom/indexedDB/test/service_worker.js b/dom/indexedDB/test/service_worker.js new file mode 100644 index 000000000..eb8fd7f66 --- /dev/null +++ b/dom/indexedDB/test/service_worker.js @@ -0,0 +1,10 @@ +onmessage = function(e) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("Error: no clients are currently controlled.\n"); + return; + } + res[0].postMessage(indexedDB ? { available: true } : + { available: false }); + }); +}; diff --git a/dom/indexedDB/test/service_worker_client.html b/dom/indexedDB/test/service_worker_client.html new file mode 100644 index 000000000..c1c98eaab --- /dev/null +++ b/dom/indexedDB/test/service_worker_client.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> +<title>controlled page</title> +<script class="testbody" type="text/javascript"> + if (!parent) { + info("service_worker_client.html should not be launched directly!"); + } + + window.onload = function() { + navigator.serviceWorker.onmessage = function(msg) { + // Forward messages coming from the service worker to the test page. + parent.postMessage(msg.data, "*"); + }; + navigator.serviceWorker.ready.then(function(swr) { + parent.postMessage("READY", "*"); + }); + } +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/indexedDB/test/test_abort_deleted_index.html b/dom/indexedDB/test/test_abort_deleted_index.html new file mode 100644 index 000000000..2c5eb6cbc --- /dev/null +++ b/dom/indexedDB/test/test_abort_deleted_index.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Abort Deleted Index Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_abort_deleted_index.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_abort_deleted_objectStore.html b/dom/indexedDB/test/test_abort_deleted_objectStore.html new file mode 100644 index 000000000..a45e52d52 --- /dev/null +++ b/dom/indexedDB/test/test_abort_deleted_objectStore.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Abort Deleted ObjectStore Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_abort_deleted_objectStore.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_add_put.html b/dom/indexedDB/test/test_add_put.html new file mode 100644 index 000000000..9ba08ac55 --- /dev/null +++ b/dom/indexedDB/test/test_add_put.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_add_put.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_add_twice_failure.html b/dom/indexedDB/test/test_add_twice_failure.html new file mode 100644 index 000000000..1b4ef4c6c --- /dev/null +++ b/dom/indexedDB/test/test_add_twice_failure.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_add_twice_failure.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_advance.html b/dom/indexedDB/test/test_advance.html new file mode 100644 index 000000000..04ffc9a1f --- /dev/null +++ b/dom/indexedDB/test/test_advance.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_advance.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_app_isolation_inproc.html b/dom/indexedDB/test/test_app_isolation_inproc.html new file mode 100644 index 000000000..571189877 --- /dev/null +++ b/dom/indexedDB/test/test_app_isolation_inproc.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=756645 +--> +<head> + <title>Test for IndexedDB app isolation (unique process)</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript;version=1.7" src="file_app_isolation.js"> +</script> +</pre> +</body> +</html> diff --git a/dom/indexedDB/test/test_app_isolation_oop.html b/dom/indexedDB/test/test_app_isolation_oop.html new file mode 100644 index 000000000..571189877 --- /dev/null +++ b/dom/indexedDB/test/test_app_isolation_oop.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=756645 +--> +<head> + <title>Test for IndexedDB app isolation (unique process)</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript;version=1.7" src="file_app_isolation.js"> +</script> +</pre> +</body> +</html> diff --git a/dom/indexedDB/test/test_autoIncrement.html b/dom/indexedDB/test/test_autoIncrement.html new file mode 100644 index 000000000..23c89f3e7 --- /dev/null +++ b/dom/indexedDB/test/test_autoIncrement.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_autoIncrement.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_autoIncrement_indexes.html b/dom/indexedDB/test/test_autoIncrement_indexes.html new file mode 100644 index 000000000..b162e548c --- /dev/null +++ b/dom/indexedDB/test/test_autoIncrement_indexes.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_autoIncrement_indexes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_bfcache.html b/dom/indexedDB/test/test_bfcache.html new file mode 100644 index 000000000..5cb737384 --- /dev/null +++ b/dom/indexedDB/test/test_bfcache.html @@ -0,0 +1,67 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="text/javascript;version=1.7"> + var gOrigMaxTotalViewers = undefined; + function setCachePref(enabled) { + if (enabled) { + is(typeof gOrigMaxTotalViewers, "undefined", + "don't double-enable bfcache"); + SpecialPowers.setBoolPref("browser.sessionhistory.cache_subframes", + true); + gOrigMaxTotalViewers = + SpecialPowers.getIntPref("browser.sessionhistory.max_total_viewers"); + SpecialPowers.setIntPref("browser.sessionhistory.max_total_viewers", + 10); + } + else { + is(typeof gOrigMaxTotalViewers, "number", + "don't double-disable bfcache"); + SpecialPowers.setIntPref("browser.sessionhistory.max_total_viewers", + gOrigMaxTotalViewers); + gOrigMaxTotalViewers = undefined; + try { + SpecialPowers.clearUserPref("browser.sessionhistory.cache_subframes"); + } catch (e) { /* Pref didn't exist, meh */ } + } + } + + function testSteps() + { + var iframe = $("iframe"); + setCachePref(true); + window.onmessage = grabEventAndContinueHandler; + + iframe.src = "bfcache_iframe1.html"; + var event = yield undefined; + is(event.data, "go", "set up database successfully"); + + iframe.src = "bfcache_iframe2.html"; + res = JSON.parse((yield).data); + is(res.version, 2, "version was set correctly"); + is(res.storeCount, 1, "correct set of stores"); + ok(!("blockedFired" in res), "blocked shouldn't fire"); + is(res.value, JSON.stringify({ hello: "world" }), + "correct value found in store"); + + setCachePref(false); + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"> + <iframe id="iframe"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/test_blob_archive.html b/dom/indexedDB/test/test_blob_archive.html new file mode 100644 index 000000000..add1ad7ee --- /dev/null +++ b/dom/indexedDB/test/test_blob_archive.html @@ -0,0 +1,127 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + + function testSteps() + { + const BLOB_DATA = + "504B03040A00000000002E6BF14000000000000000000000000005001C00746573742F" + + "555409000337CA055039CA055075780B000104E803000004E8030000504B0304140000" + + "0008002D6BF1401780E15015000000580200000A001C00746573742F612E7478745554" + + "09000336CA05503ACA055075780B000104E803000004E8030000CB48CDC9C95728CF2F" + + "CA49E1CA18658FB2A9C40600504B03040A00000000002F88EC40662E84701000000010" + + "0000000A001C00746573742F622E74787455540900035A65FF4F42C5055075780B0001" + + "04E803000004E803000068656C6C6F20776F726C642C2032210A504B01021E030A0000" + + "0000002E6BF140000000000000000000000000050018000000000000001000FD410000" + + "0000746573742F555405000337CA055075780B000104E803000004E8030000504B0102" + + "1E031400000008002D6BF1401780E15015000000580200000A00180000000000010000" + + "00B4813F000000746573742F612E747874555405000336CA055075780B000104E80300" + + "0004E8030000504B01021E030A00000000002F88EC40662E847010000000100000000A" + + "0018000000000001000000B48198000000746573742F622E74787455540500035A65FF" + + "4F75780B000104E803000004E8030000504B05060000000003000300EB000000EC0000" + + "000000"; + + const TEST_FILE_1 = "test/a.txt"; + const TEST_FILE_2 = "test/b.txt"; + + let TEST_FILE_1_CONTENTS = ""; + for (let i = 0; i < 50; i++) { + TEST_FILE_1_CONTENTS += "hello world\n"; + } + const TEST_FILE_2_CONTENTS = "hello world, 2!\n"; + + let binaryData = new Uint8Array(BLOB_DATA.length / 2); + for (let i = 0, len = BLOB_DATA.length / 2; i < len; i++) { + let hex = BLOB_DATA[i * 2] + BLOB_DATA[i * 2 + 1]; + binaryData[i] = parseInt(hex, 16); + } + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + let index = objectStore.createIndex("foo", "index"); + + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let data = new Blob([binaryData]); + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(data).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let key = event.target.result; + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let archiveReader = new ArchiveReader(event.target.result); + ok(archiveReader, "Got an ArchiveReader"); + + request = archiveReader.getFilenames(); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + is(event.target.result.length, 2, "Got 2 archive items"); + is(event.target.result[0], TEST_FILE_1, + "First file is '" + TEST_FILE_1 + "'"); + is(event.target.result[1], TEST_FILE_2, + "Second file is '" + TEST_FILE_2 + "'"); + + request = archiveReader.getFile(TEST_FILE_1); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + let fileReader = new FileReader(); + fileReader.readAsText(event.target.result); + fileReader.onload = grabEventAndContinueHandler; + fileReader.onerror = errorHandler; + event = yield undefined; + + // Don't use is() because it prints out 100 lines of text... + ok(event.target.result == TEST_FILE_1_CONTENTS, "Correct text"); + + request = archiveReader.getFile(TEST_FILE_2); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + fileReader = new FileReader(); + fileReader.readAsText(event.target.result); + fileReader.onload = grabEventAndContinueHandler; + fileReader.onerror = errorHandler; + event = yield undefined; + + // Don't use is() because it prints out a newline... + ok(event.target.result == TEST_FILE_2_CONTENTS, "Correct text"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_file_backed.html b/dom/indexedDB/test/test_blob_file_backed.html new file mode 100644 index 000000000..3c3f103d6 --- /dev/null +++ b/dom/indexedDB/test/test_blob_file_backed.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>IndexedDB Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_blob_file_backed.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_simple.html b/dom/indexedDB/test/test_blob_simple.html new file mode 100644 index 000000000..e7e440719 --- /dev/null +++ b/dom/indexedDB/test/test_blob_simple.html @@ -0,0 +1,281 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + info("Setting up test fixtures: create an IndexedDB database and object store."); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + let index = objectStore.createIndex("foo", "index"); + + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + + info("Let's create a blob and store it in IndexedDB twice."); + + const BLOB_DATA = ["fun ", "times ", "all ", "around!"]; + const INDEX_KEY = 5; + let blob = new Blob(BLOB_DATA, { type: "text/plain" }); + let data = { blob: blob, index: INDEX_KEY }; + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(data).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Added blob to database once"); + + let key = event.target.result; + + objectStore.add(data).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Added blob to database twice"); + + info("Let's retrieve the blob again and verify the contents is the same."); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Got blob from database"); + + let fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(event.target.result.blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + + info("Let's retrieve it again, create an object URL for the blob, load" + + "it via an XMLHttpRequest, and verify the contents is the same."); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Got blob from database"); + + let blobURL = URL.createObjectURL(event.target.result.blob); + + let xhr = new XMLHttpRequest(); + xhr.open("GET", blobURL); + xhr.onload = grabEventAndContinueHandler; + xhr.send(); + yield undefined; + + URL.revokeObjectURL(blobURL); + + is(xhr.responseText, BLOB_DATA.join(""), "Correct responseText"); + + + info("Retrieve both blob entries from the database and verify contents."); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.mozGetAll().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, 2, "Got right number of items"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(event.target.result[0].blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + let cursorResults = []; + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + info("Got item from cursor"); + cursorResults.push(cursor.value); + cursor.continue(); + } + else { + info("Finished cursor"); + continueToNextStep(); + } + }; + yield undefined; + + is(cursorResults.length, 2, "Got right number of items"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(cursorResults[0].blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + + info("Retrieve blobs from database via index and verify contents."); + + index = db.transaction("foo").objectStore("foo").index("foo"); + index.get(INDEX_KEY).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Got blob from database"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(event.target.result.blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + index = db.transaction("foo").objectStore("foo").index("foo"); + index.mozGetAll().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, 2, "Got right number of items"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(event.target.result[0].blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + cursorResults = []; + + index = db.transaction("foo").objectStore("foo").index("foo"); + index.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + info("Got item from cursor"); + cursorResults.push(cursor.value); + cursor.continue(); + } + else { + info("Finished cursor"); + continueToNextStep(); + } + }; + yield undefined; + + is(cursorResults.length, 2, "Got right number of items"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(cursorResults[0].blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(cursorResults[1].blob); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + + info("Slice the the retrieved blob and verify its contents."); + + let slice = cursorResults[1].blob.slice(0, BLOB_DATA[0].length); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(slice); + event = yield undefined; + + is(event.target.result, BLOB_DATA[0], "Correct text"); + + + info("Send blob to a worker, read its contents there, and verify results."); + + function workerScript() { + onmessage = function(event) { + var reader = new FileReaderSync(); + postMessage(reader.readAsText(event.data)); + + var slice = event.data.slice(1, 2); + postMessage(reader.readAsText(slice)); + + } + } + + let url = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let worker = new Worker(url); + worker.postMessage(slice); + worker.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data, BLOB_DATA[0], "Correct text"); + event = yield undefined; + + is(event.data, BLOB_DATA[0][1], "Correct text"); + + + info("Store a blob back in the database, and keep holding on to the " + + "blob, verifying that it still can be read."); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let blobFromDB = event.target.result.blob; + info("Got blob from database"); + + let txn = db.transaction("foo", "readwrite"); + txn.objectStore("foo").put(event.target.result, key); + txn.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + info("Stored blob back into database"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(blobFromDB); + event = yield undefined; + + is(event.target.result, BLOB_DATA.join(""), "Correct text"); + + blobURL = URL.createObjectURL(blobFromDB); + + xhr = new XMLHttpRequest(); + xhr.open("GET", blobURL); + xhr.onload = grabEventAndContinueHandler; + xhr.send(); + yield undefined; + + URL.revokeObjectURL(blobURL); + + is(xhr.responseText, BLOB_DATA.join(""), "Correct responseText"); + + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_worker_crash.html b/dom/indexedDB/test/test_blob_worker_crash.html new file mode 100644 index 000000000..849915b86 --- /dev/null +++ b/dom/indexedDB/test/test_blob_worker_crash.html @@ -0,0 +1,61 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Blob Worker Crash Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + /* + * This tests ensures that if the last live reference to a Blob is on the + * worker and the database has already been shutdown, that there is no crash + * when the owning page gets cleaned up which causes the termination of the + * worker which in turn garbage collects during its shutdown. + * + * We do the IndexedDB stuff in the iframe so we can kill it as part of our + * test. Doing it out here is no good. + */ + + function testSteps() + { + info("Open iframe, wait for it to do its IndexedDB stuff."); + + let iframe = document.getElementById("iframe1"); + window.addEventListener("message", grabEventAndContinueHandler, false); + // Put it in a different origin to be safe + iframe.src = //"http://example.org" + + window.location.pathname.replace( + "test_blob_worker_crash.html", + "blob_worker_crash_iframe.html"); + + let event = yield unexpectedSuccessHandler; + is(event.data.result, "ready", "worker initialized correctly"); + + info("Trigger a GC to clean-up the iframe's main-thread IndexedDB"); + scheduleGC(); + yield undefined; + + info("Kill the iframe, forget about it, trigger a GC."); + iframe.parentNode.removeChild(iframe); + iframe = null; + scheduleGC(); + yield undefined; + + info("If we are still alive, then we win!"); + ok('Did not crash / trigger an assert!'); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + <iframe id="iframe1"></iframe> +</html> diff --git a/dom/indexedDB/test/test_blob_worker_xhr_post.html b/dom/indexedDB/test/test_blob_worker_xhr_post.html new file mode 100644 index 000000000..a421c4256 --- /dev/null +++ b/dom/indexedDB/test/test_blob_worker_xhr_post.html @@ -0,0 +1,113 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const BLOB_DATA = ["fun ", "times ", "all ", "around!"]; + const BLOB_TYPE = "text/plain"; + const BLOB_SIZE = BLOB_DATA.join("").length; + + info("Setting up"); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + ok(db, "Created database"); + + info("Creating objectStore"); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "Opened database"); + + let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE }); + + info("Adding blob to database"); + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(blob).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let blobKey = event.target.result; + ok(blobKey, "Got a key for the blob"); + + info("Getting blob from the database"); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + blob = event.target.result; + + ok(blob instanceof Blob, "Got a blob"); + is(blob.size, BLOB_SIZE, "Correct size"); + is(blob.type, BLOB_TYPE, "Correct type"); + + let slice = blob.slice(0, BLOB_DATA[0].length, BLOB_TYPE); + + ok(slice instanceof Blob, "Slice returned a blob"); + is(slice.size, BLOB_DATA[0].length, "Correct size for slice"); + is(slice.type, BLOB_TYPE, "Correct type for slice"); + + info("Sending slice to a worker"); + + function workerScript() { + onmessage = function(event) { + var blob = event.data; + var xhr = new XMLHttpRequest(); + // We just want to make sure the error case doesn't fire; it's fine for + // us to just want a 404. + xhr.open('POST', 'http://mochi.test:8888/does-not-exist', true); + xhr.onload = function() { + postMessage({ status: xhr.status }); + }; + xhr.onerror = function() { + postMessage({ status: 'error' }); + } + xhr.send(blob); + } + } + + let workerScriptUrl = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let xhrWorker = new Worker(workerScriptUrl); + xhrWorker.postMessage(slice); + xhrWorker.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data.status, 404, "XHR generated the expected 404"); + xhrWorker.terminate(); + + URL.revokeObjectURL(workerScriptUrl); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html b/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html new file mode 100644 index 000000000..c739d2586 --- /dev/null +++ b/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html @@ -0,0 +1,113 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + /** + * Create a composite/multi-file Blob on the worker, then post it as an XHR + * payload and ensure that we don't hang/generate an assertion/etc. but + * instead generate the expected 404. This test is basically the same as + * test_blob_worker_xhr_post.html except for the composite Blob. + */ + function testSteps() + { + const BLOB_DATA = ["fun ", "times ", "all ", "around!"]; + const BLOB_TYPE = "text/plain"; + const BLOB_SIZE = BLOB_DATA.join("").length; + + info("Setting up"); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + ok(db, "Created database"); + + info("Creating objectStore"); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "Opened database"); + + let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE }); + + info("Adding blob to database"); + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(blob).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let blobKey = event.target.result; + ok(blobKey, "Got a key for the blob"); + + info("Getting blob from the database"); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + blob = event.target.result; + + ok(blob instanceof Blob, "Got a blob"); + is(blob.size, BLOB_SIZE, "Correct size"); + is(blob.type, BLOB_TYPE, "Correct type"); + + function workerScript() { + onmessage = function(event) { + var blob = event.data; + var compositeBlob = new Blob(["preceding string. ", blob], + { type: "text/plain" }); + var xhr = new XMLHttpRequest(); + // We just want to make sure the error case doesn't fire; it's fine for + // us to just want a 404. + xhr.open('POST', 'http://mochi.test:8888/does-not-exist', true); + xhr.onload = function() { + postMessage({ status: xhr.status }); + }; + xhr.onerror = function() { + postMessage({ status: 'error' }); + } + xhr.send(compositeBlob); + } + } + + let workerScriptUrl = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let xhrWorker = new Worker(workerScriptUrl); + xhrWorker.postMessage(blob); + xhrWorker.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data.status, 404, "XHR generated the expected 404"); + xhrWorker.terminate(); + + URL.revokeObjectURL(workerScriptUrl); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_worker_xhr_read.html b/dom/indexedDB/test/test_blob_worker_xhr_read.html new file mode 100644 index 000000000..920dbffae --- /dev/null +++ b/dom/indexedDB/test/test_blob_worker_xhr_read.html @@ -0,0 +1,114 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Blob Read From Worker</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + /** + * Create an IndexedDB-backed Blob, send it to the worker, try and read the + * contents of the Blob from the worker using an XHR. Ideally, we don't + * deadlock the main thread. + */ + function testSteps() + { + const BLOB_DATA = ["Green"]; + const BLOB_TYPE = "text/plain"; + const BLOB_SIZE = BLOB_DATA.join("").length; + + info("Setting up"); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + ok(db, "Created database"); + + info("Creating objectStore"); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "Opened database"); + + let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE }); + + info("Adding blob to database"); + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(blob).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let blobKey = event.target.result; + ok(blobKey, "Got a key for the blob"); + + info("Getting blob from the database"); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + blob = event.target.result; + + ok(blob instanceof Blob, "Got a blob"); + is(blob.size, BLOB_SIZE, "Correct size"); + is(blob.type, BLOB_TYPE, "Correct type"); + + info("Sending blob to a worker"); + + function workerScript() { + onmessage = function(event) { + var blob = event.data; + var blobUrl = URL.createObjectURL(blob); + var xhr = new XMLHttpRequest(); + xhr.open('GET', blobUrl, true); + xhr.responseType = 'text'; + xhr.onload = function() { + postMessage({ data: xhr.response }); + URL.revokeObjectURL(blobUrl); + }; + xhr.onerror = function() { + postMessage({ data: null }); + URL.revokeObjectURL(blobUrl); + } + xhr.send(); + } + } + + let workerScriptUrl = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let xhrWorker = new Worker(workerScriptUrl); + xhrWorker.postMessage(blob); + xhrWorker.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data.data, "Green", "XHR returned expected payload."); + xhrWorker.terminate(); + + URL.revokeObjectURL(workerScriptUrl); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html b/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html new file mode 100644 index 000000000..564b63f11 --- /dev/null +++ b/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html @@ -0,0 +1,116 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Blob Read From Worker</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + /** + * Create an IndexedDB-backed Blob, send it to the worker, try and read the + * *SLICED* contents of the Blob from the worker using an XHR. This is + * (as of the time of writing this) basically the same as + * test_blob_worker_xhr_read.html but with slicing added. + */ + function testSteps() + { + const BLOB_DATA = ["Green"]; + const BLOB_TYPE = "text/plain"; + const BLOB_SIZE = BLOB_DATA.join("").length; + + info("Setting up"); + + let request = indexedDB.open(window.location.pathname, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + ok(db, "Created database"); + + info("Creating objectStore"); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "Opened database"); + + let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE }); + + info("Adding blob to database"); + + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + objectStore.add(blob).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let blobKey = event.target.result; + ok(blobKey, "Got a key for the blob"); + + info("Getting blob from the database"); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + blob = event.target.result; + + ok(blob instanceof Blob, "Got a blob"); + is(blob.size, BLOB_SIZE, "Correct size"); + is(blob.type, BLOB_TYPE, "Correct type"); + + info("Sending blob to a worker"); + + function workerScript() { + onmessage = function(event) { + var blob = event.data; + var slicedBlob = blob.slice(0, 3, "text/plain"); + var blobUrl = URL.createObjectURL(slicedBlob); + var xhr = new XMLHttpRequest(); + xhr.open('GET', blobUrl, true); + xhr.responseType = 'text'; + xhr.onload = function() { + postMessage({ data: xhr.response }); + URL.revokeObjectURL(blobUrl); + }; + xhr.onerror = function() { + postMessage({ data: null }); + URL.revokeObjectURL(blobUrl); + } + xhr.send(); + } + } + + let workerScriptUrl = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let xhrWorker = new Worker(workerScriptUrl); + xhrWorker.postMessage(blob); + xhrWorker.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data.data, "Gre", "XHR returned expected sliced payload."); + xhrWorker.terminate(); + + URL.revokeObjectURL(workerScriptUrl); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_blocked_order.html b/dom/indexedDB/test/test_blocked_order.html new file mode 100644 index 000000000..9b82995a3 --- /dev/null +++ b/dom/indexedDB/test/test_blocked_order.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>IndexedDB Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_blocked_order.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_bug847147.html b/dom/indexedDB/test/test_bug847147.html new file mode 100644 index 000000000..9eb0ed3dd --- /dev/null +++ b/dom/indexedDB/test/test_bug847147.html @@ -0,0 +1,57 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_lifetimes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +<script> + +var win; +var r1; + +function e() +{ + win = window.open("data:text/html,<body onload='opener.f()'>1", "_blank", ""); +} + +function f() +{ + setTimeout(function() { + r1 = win.document.documentElement; + win.location.replace("data:text/html,<body onload='opener.g()'>2"); + }, 0); +} + +function g() +{ + r1.appendChild(document.createElement("iframe")); + setTimeout(function() { + win.location = "data:text/html,<body onload='opener.h()'>3"; + }, 0); +} + +function h() +{ + win.close(); + ok(true, "This test is looking for assertions so this is irrelevant."); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</head> + +<body onload="e();"> +<button onclick="e();">Start test</button> +</body> +</html> diff --git a/dom/indexedDB/test/test_bug937006.html b/dom/indexedDB/test/test_bug937006.html new file mode 100644 index 000000000..6a95488e2 --- /dev/null +++ b/dom/indexedDB/test/test_bug937006.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>Bug 937006 - "Hit MOZ_CRASH(Failed to get caller.)" using setTimeout on IndexedDB call</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +</head> +<body onload="runTest();"> + <script type="text/javascript;version=1.7"> + + function runTest() { + // doing this IDBRequest should not be able to retrieve the filename and + // line number. + SimpleTest.requestFlakyTimeout("untriaged"); + setTimeout(indexedDB.deleteDatabase.bind(indexedDB), 0, 'x'); + setTimeout(function() { + ok(true, "Still alive"); + SimpleTest.finish(); + }, 10); + } + + </script> +</body> +</html> diff --git a/dom/indexedDB/test/test_clear.html b/dom/indexedDB/test/test_clear.html new file mode 100644 index 000000000..6d1a1f159 --- /dev/null +++ b/dom/indexedDB/test/test_clear.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_clear.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_complex_keyPaths.html b/dom/indexedDB/test/test_complex_keyPaths.html new file mode 100644 index 000000000..83ff438c2 --- /dev/null +++ b/dom/indexedDB/test/test_complex_keyPaths.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_complex_keyPaths.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_count.html b/dom/indexedDB/test/test_count.html new file mode 100644 index 000000000..ebe14b3d5 --- /dev/null +++ b/dom/indexedDB/test/test_count.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_count.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_create_index.html b/dom/indexedDB/test/test_create_index.html new file mode 100644 index 000000000..43b5a6529 --- /dev/null +++ b/dom/indexedDB/test/test_create_index.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_create_index.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_create_index_with_integer_keys.html b/dom/indexedDB/test/test_create_index_with_integer_keys.html new file mode 100644 index 000000000..f032a8b1d --- /dev/null +++ b/dom/indexedDB/test/test_create_index_with_integer_keys.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_create_index_with_integer_keys.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_create_locale_aware_index.html b/dom/indexedDB/test/test_create_locale_aware_index.html new file mode 100644 index 000000000..4053dcd90 --- /dev/null +++ b/dom/indexedDB/test/test_create_locale_aware_index.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_create_locale_aware_index.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_create_objectStore.html b/dom/indexedDB/test/test_create_objectStore.html new file mode 100644 index 000000000..09660a627 --- /dev/null +++ b/dom/indexedDB/test/test_create_objectStore.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_create_objectStore.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_cursor_mutation.html b/dom/indexedDB/test/test_cursor_mutation.html new file mode 100644 index 000000000..fe1ca873c --- /dev/null +++ b/dom/indexedDB/test/test_cursor_mutation.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_cursor_mutation.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_cursor_update_updates_indexes.html b/dom/indexedDB/test/test_cursor_update_updates_indexes.html new file mode 100644 index 000000000..ecc7fc654 --- /dev/null +++ b/dom/indexedDB/test/test_cursor_update_updates_indexes.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_cursor_update_updates_indexes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_cursors.html b/dom/indexedDB/test/test_cursors.html new file mode 100644 index 000000000..9e803053d --- /dev/null +++ b/dom/indexedDB/test/test_cursors.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_cursors.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_database_onclose.html b/dom/indexedDB/test/test_database_onclose.html new file mode 100644 index 000000000..5a10b64ad --- /dev/null +++ b/dom/indexedDB/test/test_database_onclose.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database DeleteDatabase Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_database_onclose.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_deleteDatabase.html b/dom/indexedDB/test/test_deleteDatabase.html new file mode 100644 index 000000000..a4b8f3eba --- /dev/null +++ b/dom/indexedDB/test/test_deleteDatabase.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database DeleteDatabase Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_deleteDatabase_interactions.html b/dom/indexedDB/test/test_deleteDatabase_interactions.html new file mode 100644 index 000000000..947e07f39 --- /dev/null +++ b/dom/indexedDB/test/test_deleteDatabase_interactions.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database DeleteDatabase Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_interactions.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_deleteDatabase_onblocked.html b/dom/indexedDB/test/test_deleteDatabase_onblocked.html new file mode 100644 index 000000000..817182c30 --- /dev/null +++ b/dom/indexedDB/test/test_deleteDatabase_onblocked.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Onblocked Test During Deleting Database</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_onblocked.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_deleteDatabase_onblocked_duringVersionChange.html b/dom/indexedDB/test/test_deleteDatabase_onblocked_duringVersionChange.html new file mode 100644 index 000000000..dd6a00217 --- /dev/null +++ b/dom/indexedDB/test/test_deleteDatabase_onblocked_duringVersionChange.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Onblocked Test During Version Change Transaction</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_onblocked_duringVersionChange.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_error_events_abort_transactions.html b/dom/indexedDB/test/test_error_events_abort_transactions.html new file mode 100644 index 000000000..63af5fdd6 --- /dev/null +++ b/dom/indexedDB/test/test_error_events_abort_transactions.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function runTest() { + SimpleTest.waitForExplicitFinish(); + + function messageListener(event) { + eval(event.data); + } + + window.addEventListener("message", messageListener, false); + } + </script> + +</head> + +<body onload="runTest();"> + <iframe src="error_events_abort_transactions_iframe.html"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/test_event_propagation.html b/dom/indexedDB/test/test_event_propagation.html new file mode 100644 index 000000000..0456e32d1 --- /dev/null +++ b/dom/indexedDB/test/test_event_propagation.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function runTest() { + SimpleTest.waitForExplicitFinish(); + + function messageListener(event) { + eval(event.data); + } + + window.addEventListener("message", messageListener, false); + } + </script> + +</head> + +<body onload="runTest();"> + <iframe src="event_propagation_iframe.html"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/test_event_source.html b/dom/indexedDB/test/test_event_source.html new file mode 100644 index 000000000..6156a964f --- /dev/null +++ b/dom/indexedDB/test/test_event_source.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_event_source.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_exceptions_in_events.html b/dom/indexedDB/test/test_exceptions_in_events.html new file mode 100644 index 000000000..dadf063fa --- /dev/null +++ b/dom/indexedDB/test/test_exceptions_in_events.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function runTest() { + SimpleTest.waitForExplicitFinish(); + + function messageListener(event) { + eval(event.data); + } + + window.addEventListener("message", messageListener, false); + } + </script> + +</head> + +<body onload="runTest();"> + <iframe src="exceptions_in_events_iframe.html"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/test_file_array.html b/dom/indexedDB/test/test_file_array.html new file mode 100644 index 000000000..580790d97 --- /dev/null +++ b/dom/indexedDB/test/test_file_array.html @@ -0,0 +1,87 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const b1 = getRandomBlob(10000); + + const b2 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(12000), + getRandomBlob(17000), getRandomBlob(16000), getRandomBlob(16000), + getRandomBlob(8000) + ]; + + const b3 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(9000)]; + + const objectStoreData = [ + { key: 1, blobs: [ b1, b1, b1, b1, b1, b1, b1, b1, b1, b1 ], + expectedFileIds: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, + { key: 2, blobs: [ b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6] ], + expectedFileIds: [2, 3, 4, 5, 6, 7, 8] }, + { key: 3, blobs: [ b3[0], b3[0], b3[1], b3[2], b3[2], b3[0], b3[0] ], + expectedFileIds: [9, 9, 10, 11, 11, 9, 9] } + ]; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + for (let data of objectStoreData) { + objectStore.add(data.blobs, data.key); + } + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + for (let data of objectStoreData) { + objectStore = db.transaction([objectStoreName]) + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlobArray(event.target.result, data.blobs, data.expectedFileIds); + yield undefined; + } + + is(bufferCache.length, 11, "Correct length"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_cross_database_copying.html b/dom/indexedDB/test/test_file_cross_database_copying.html new file mode 100644 index 000000000..bf65a3a6b --- /dev/null +++ b/dom/indexedDB/test/test_file_cross_database_copying.html @@ -0,0 +1,108 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const databaseInfo = [ + { name: window.location.pathname + "1" }, + { name: window.location.pathname + "2" } + ]; + + const objectStoreName = "Blobs"; + + const fileData = { key: 1, file: getRandomFile("random.bin", 100000) }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + let databases = []; + for (let info of databaseInfo) { + let request = indexedDB.open(info.name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + objectStore.add(fileData.file, fileData.key); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + databases.push(db); + } + + let refResult; + for (let db of databases) { + let request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + verifyBlob(result, fileData.file, 1); + yield undefined; + + if (!refResult) { + refResult = result; + continue; + } + + isnot(getFilePath(result), getFilePath(refResult), "Different os files"); + } + + for (let i = 1; i < databases.length; i++) { + let db = databases[i]; + + let objectStore = db.transaction([objectStoreName], READ_WRITE) + .objectStore(objectStoreName); + + request = objectStore.add(refResult, 2); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 2, "Got correct key"); + + request = objectStore.get(2); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + verifyBlob(result, refResult, 2); + yield undefined; + + isnot(getFilePath(result), getFilePath(refResult), "Different os files"); + } + + is(bufferCache.length, 2, "Correct length"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_delete.html b/dom/indexedDB/test/test_file_delete.html new file mode 100644 index 000000000..8110c5a8c --- /dev/null +++ b/dom/indexedDB/test/test_file_delete.html @@ -0,0 +1,137 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const fileData1 = { key: 1, file: getRandomFile("random1.bin", 110000) }; + const fileData2 = { key: 2, file: getRandomFile("random2.bin", 120000) }; + const fileData3 = { key: 3, file: getRandomFile("random3.bin", 130000) }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + objectStore.add(fileData1.file, fileData1.key); + objectStore.add(fileData2.file, fileData2.key); + objectStore.add(fileData3.file, fileData3.key); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let trans = db.transaction([objectStoreName], READ_WRITE); + trans.objectStore(objectStoreName).delete(fileData1.key); + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Got correct event type"); + + is(getFileDBRefCount(name, 1), 0, "Correct db ref count"); + + fileData1.file = null; + fileData2.file = null; + fileData3.file = null; + } + + scheduleGC(); + yield undefined; + + ok(!hasFileInfo(name, 1), "Correct ref count"); + ok(hasFileInfo(name, 2), "Correct ref count"); + ok(hasFileInfo(name, 3), "Correct ref count"); + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.get(fileData2.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + ok(result, "Got result"); + + objectStore.delete(fileData2.key); + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Got correct event type"); + + is(getFileDBRefCount(name, 2), 0, "Correct db ref count"); + + + trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + objectStore.delete(fileData3.key); + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Got correct event type"); + + is(getFileDBRefCount(name, 3), -1, "Correct db ref count"); + + event = null; + result = null; + } + + scheduleGC(); + yield undefined; + + ok(!hasFileInfo(name, 1), "Correct ref count"); + ok(!hasFileInfo(name, 2), "Correct ref count"); + ok(!hasFileInfo(name, 3), "Correct ref count"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_os_delete.html b/dom/indexedDB/test/test_file_os_delete.html new file mode 100644 index 000000000..f4bf7801b --- /dev/null +++ b/dom/indexedDB/test/test_file_os_delete.html @@ -0,0 +1,109 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + getCurrentUsage(grabFileUsageAndContinueHandler); + let startUsage = yield undefined; + + const fileData1 = { + key: 1, + obj: { id: 1, file: getRandomFile("random.bin", 100000) } + }; + const fileData2 = { + key: 2, + obj: { id: 1, file: getRandomFile("random.bin", 100000) } + }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + objectStore.createIndex("index", "id", { unique: true }); + + objectStore.add(fileData1.obj, fileData1.key); + + request = objectStore.add(fileData2.obj, fileData2.key); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + getCurrentUsage(grabFileUsageAndContinueHandler); + let usage = yield undefined; + + is(usage, startUsage + fileData1.obj.file.size + fileData2.obj.file.size, + "Correct file usage"); + + let trans = db.transaction([objectStoreName], READ_WRITE); + trans.objectStore(objectStoreName).delete(fileData1.key); + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Got correct event type"); + + getCurrentUsage(grabFileUsageAndContinueHandler); + usage = yield undefined; + + is(usage, startUsage + fileData1.obj.file.size + fileData2.obj.file.size, + "OS files exists"); + + fileData1.obj.file = null; + fileData2.obj.file = null; + } + + scheduleGC(); + yield undefined; + + // Flush pending file deletions before checking usage. + flushPendingFileDeletions(); + + getCurrentUsage(grabFileUsageAndContinueHandler); + let endUsage = yield undefined; + + is(endUsage, startUsage, "OS files deleted"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_put_deleted.html b/dom/indexedDB/test/test_file_put_deleted.html new file mode 100644 index 000000000..f205a5b03 --- /dev/null +++ b/dom/indexedDB/test/test_file_put_deleted.html @@ -0,0 +1,156 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + /** + * Test that a put of a file-backed Blob/File whose backing file has been + * deleted results in a failure of that put failure. + * + * In order to create a file-backed Blob and ensure that we actually try and + * copy its contents (rather than triggering a reference-count increment), we + * use two separate databases. This test is derived from + * test_file_cross_database_copying.html. + */ + function testSteps() + { + const READ_WRITE = "readwrite"; + + const databaseInfo = [ + { name: window.location.pathname + "1", source: true }, + { name: window.location.pathname + "2", source: false } + ]; + + const objectStoreName = "Blobs"; + + const fileData = { key: 1, file: getRandomFile("random.bin", 10000) }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + // Open both databases, put the File in the source. + let databases = []; + for (let info of databaseInfo) { + let request = indexedDB.open(info.name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + // We don't expect any errors yet for either database, but will later on. + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + if (info.source) { + objectStore.add(fileData.file, fileData.key); + } + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + databases.push(db); + } + + // Get a reference to the file-backed File. + let fileBackedFile; + for (let db of databases.slice(0, 1)) { + let request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + verifyBlob(result, fileData.file, 1); + yield undefined; + + fileBackedFile = result; + } + + // Delete the backing file... + let fileFullPath = getFilePath(fileBackedFile); + // (We want to chop off the profile root and the resulting path component + // must not start with a directory separator.) + let fileRelPath = + fileFullPath.substring(fileFullPath.search(/[/\\]storage[/\\]default[/\\]/) + 1); + info("trying to delete: " + fileRelPath); + // by using the existing SpecialPowers mechanism to create files and clean + // them up. We clobber our existing content, then trigger deletion to + // clean up after it. + SpecialPowers.createFiles( + [{ name: fileRelPath, data: '' }], + grabEventAndContinueHandler, errorCallbackHandler); + yield undefined; + // This is async without a callback because it's intended for cleanup. + // Since IDB is PBackground, we can't depend on serial ordering, so we need + // to use another async action. + SpecialPowers.removeFiles(); + SpecialPowers.executeAfterFlushingMessageQueue(grabEventAndContinueHandler); + yield undefined; + // The file is now deleted! + + // Try and put the file-backed Blob in the database, expect failure on the + // request and transaction. + info("attempt to store deleted file-backed blob"); // context for NS_WARN_IF + for (let i = 1; i < databases.length; i++) { + let db = databases[i]; + + let trans = db.transaction([objectStoreName], READ_WRITE); + let objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(fileBackedFile, 2); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = expectedErrorHandler("UnknownError"); + trans.onsuccess = unexpectedSuccessHandler; + trans.onerror = expectedErrorHandler("UnknownError"); + // the database will also throw an error. + db.onerror = expectedErrorHandler("UnknownError"); + event = yield undefined; + event = yield undefined; + event = yield undefined; + // the database shouldn't throw any more errors now. + db.onerror = errorHandler; + } + + // Ensure there's nothing with that key in the target database. + info("now that the transaction failed, make sure our put got rolled back"); + for (let i = 1; i < databases.length; i++) { + let db = databases[i]; + + let objectStore = db.transaction([objectStoreName], "readonly") + .objectStore(objectStoreName); + + // Attempt to fetch the key to verify there's nothing in the DB rather + // than the value which could return undefined as a misleading error. + request = objectStore.getKey(2); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + event = yield undefined; + + let result = event.target.result; + is(result, undefined, "no key found"); // (the get returns undefined) + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_put_get_object.html b/dom/indexedDB/test/test_file_put_get_object.html new file mode 100644 index 000000000..7b96f5687 --- /dev/null +++ b/dom/indexedDB/test/test_file_put_get_object.html @@ -0,0 +1,90 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const blob = getRandomBlob(1000); + const file = getRandomFile("random.bin", 100000); + + const objectData1 = { key: 1, object: { foo: blob, bar: blob } }; + const objectData2 = { key: 2, object: { foo: file, bar: file } }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + objectStore.add(objectData1.object, objectData1.key); + objectStore.add(objectData2.object, objectData2.key); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + objectStore = db.transaction([objectStoreName]) + .objectStore(objectStoreName); + request = objectStore.get(objectData1.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + + verifyBlob(result.foo, blob, 1); + yield undefined; + + verifyBlob(result.bar, blob, 1); + yield undefined; + + objectStore = db.transaction([objectStoreName]) + .objectStore(objectStoreName); + request = objectStore.get(objectData2.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + result = event.target.result; + + verifyBlob(result.foo, file, 2); + yield undefined; + + verifyBlob(result.bar, file, 2); + yield undefined; + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_put_get_values.html b/dom/indexedDB/test/test_file_put_get_values.html new file mode 100644 index 000000000..3f18264d6 --- /dev/null +++ b/dom/indexedDB/test/test_file_put_get_values.html @@ -0,0 +1,104 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const blobData = { key: 1, blob: getRandomBlob(10000) }; + const fileData = { key: 2, file: getRandomFile("random.bin", 100000) }; + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let objectStore = db.transaction([objectStoreName], READ_WRITE) + .objectStore(objectStoreName); + request = objectStore.add(blobData.blob, blobData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, blobData.key, "Got correct key"); + + request = objectStore.get(blobData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, blobData.blob, 1); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(blobData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, blobData.blob, 1); + yield undefined; + + objectStore = db.transaction([objectStoreName], READ_WRITE) + .objectStore(objectStoreName); + request = objectStore.add(fileData.file, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, fileData.key, "Got correct key"); + + request = objectStore.get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, fileData.file, 2); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, fileData.file, 2); + yield undefined; + + is(bufferCache.length, 2, "Correct length"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_replace.html b/dom/indexedDB/test/test_file_replace.html new file mode 100644 index 000000000..a7cc5e6d0 --- /dev/null +++ b/dom/indexedDB/test/test_file_replace.html @@ -0,0 +1,70 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const blobData = { key: 42, blobs: [] }; + + for (let i = 0; i < 100; i++) { + blobData.blobs[i] = getRandomBlob(i); + } + + SpecialPowers.pushPrefEnv({ set: [["dom.indexedDB.dataThreshold", -1]] }, + continueToNextStep); + yield undefined; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + for (let i = 0; i < blobData.blobs.length; i++) { + objectStore.put(blobData.blobs[i], blobData.key); + } + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + for (let id = 1; id <= 100; id++) { + let refs = {}; + let dbRefs = {}; + let hasFileInfo = utils.getFileReferences(name, id, null, refs, dbRefs); + ok(hasFileInfo, "Has file info"); + is(refs.value, 1, "Correct ref count"); + is(dbRefs.value, id / 100 >> 0, "Correct db ref count"); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_resurrection_delete.html b/dom/indexedDB/test/test_file_resurrection_delete.html new file mode 100644 index 000000000..e3d852929 --- /dev/null +++ b/dom/indexedDB/test/test_file_resurrection_delete.html @@ -0,0 +1,133 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const fileData = { key: 1, file: getRandomFile("random.bin", 100000) }; + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + objectStore.add(fileData.file, fileData.key); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + objectStore.delete(fileData.key); + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(getFileDBRefCount(name, 1), 0, "Correct db ref count"); + + trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(fileData.file, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(getFileDBRefCount(name, 1), 1, "Correct db ref count"); + + fileData.file = null; + } + + scheduleGC(); + yield undefined; + + is(getFileRefCount(name, 1), 0, "Correct ref count"); + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + ok(result, "Got result"); + + objectStore.delete(fileData.key); + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(getFileDBRefCount(name, 1), 0, "Correct db ref count"); + + trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(result, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(getFileDBRefCount(name, 1), 1, "Correct db ref count"); + + event = null; + result = null; + } + + scheduleGC(); + yield undefined; + + is(getFileRefCount(name, 1), 0, "Correct ref count"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_resurrection_transaction_abort.html b/dom/indexedDB/test/test_file_resurrection_transaction_abort.html new file mode 100644 index 000000000..1265833e4 --- /dev/null +++ b/dom/indexedDB/test/test_file_resurrection_transaction_abort.html @@ -0,0 +1,92 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const fileData = { key: 1, file: getRandomFile("random.bin", 100000) }; + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(fileData.file, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + request = objectStore.get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + ok(result, "Got result"); + + trans.onabort = grabEventAndContinueHandler; + trans.abort(); + event = yield undefined; + + is(getFileDBRefCount(name, 1), 0, "Correct db ref count"); + + trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(result, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(getFileDBRefCount(name, 1), 1, "Correct db ref count"); + + fileData.file = null; + } + + scheduleGC(); + yield undefined; + + is(getFileRefCount(name, 1), 0, "Correct ref count"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_sharing.html b/dom/indexedDB/test/test_file_sharing.html new file mode 100644 index 000000000..2f03689f3 --- /dev/null +++ b/dom/indexedDB/test/test_file_sharing.html @@ -0,0 +1,103 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreInfo = [ + { name: "Blobs", options: { } }, + { name: "Other Blobs", options: { } } + ]; + + const fileData = { key: 1, file: getRandomFile("random.bin", 100000) }; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + for (let info of objectStoreInfo) { + let objectStore = db.createObjectStore(info.name, info.options); + objectStore.add(fileData.file, fileData.key); + } + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let refResult; + for (let info of objectStoreInfo) { + let objectStore = db.transaction([info.name]) + .objectStore(info.name); + + request = objectStore.get(fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + verifyBlob(result, fileData.file, 1); + yield undefined; + + if (!refResult) { + refResult = result; + continue; + } + + is(getFilePath(result), getFilePath(refResult), "The same os file"); + } + + for (let i = 1; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + + let objectStore = db.transaction([info.name], READ_WRITE) + .objectStore(info.name); + + request = objectStore.add(refResult, 2); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 2, "Got correct key"); + + request = objectStore.get(2); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + verifyBlob(result, refResult, 1); + yield undefined; + + is(getFilePath(result), getFilePath(refResult), "The same os file"); + } + + is(bufferCache.length, 2, "Correct length"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_file_transaction_abort.html b/dom/indexedDB/test/test_file_transaction_abort.html new file mode 100644 index 000000000..8c08c6517 --- /dev/null +++ b/dom/indexedDB/test/test_file_transaction_abort.html @@ -0,0 +1,77 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const fileData = { key: 1, file: getRandomFile("random.bin", 100000) }; + + { + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(fileData.file, fileData.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, fileData.key, "Got correct key"); + + trans.onabort = grabEventAndContinueHandler; + trans.abort(); + event = yield undefined; + + is(event.type, "abort", "Got correct event type"); + + is(getFileDBRefCount(name, 1), 0, "Correct db ref count"); + + fileData.file = null; + } + + scheduleGC(); + yield undefined; + + ok(!hasFileInfo(name, 1), "Correct ref count"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_append_read_data.html b/dom/indexedDB/test/test_filehandle_append_read_data.html new file mode 100644 index 000000000..984fb915d --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_append_read_data.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_filehandle_append_read_data.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_compat.html b/dom/indexedDB/test/test_filehandle_compat.html new file mode 100644 index 000000000..667fcc99e --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_compat.html @@ -0,0 +1,52 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.mozCreateFileHandle("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let fileHandle = event.target.result; + fileHandle.onerror = errorHandler; + + let lockedFile = fileHandle.open(); + ok(lockedFile.fileHandle === fileHandle, "Correct property"); + + request = lockedFile.getMetadata({ size: true }); + ok(request.lockedFile === lockedFile, "Correct property"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_disabled_pref.html b/dom/indexedDB/test/test_filehandle_disabled_pref.html new file mode 100644 index 000000000..d695e10ee --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_disabled_pref.html @@ -0,0 +1,204 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" + src="/tests/SimpleTest/SimpleTest.js"> + </script> + + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const databaseName = window.location.pathname; + const databaseVersion = 1; + const objectStoreName = "foo"; + const mutableFileName = "bar"; + const mutableFileKey = 42; + + info("opening database"); + + let request = indexedDB.open(databaseName, databaseVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + info("creating object store"); + + let db = event.target.result; + db.onerror = errorHandler; + db.onversionchange = function(event) { + is(event.oldVersion, databaseVersion, "got correct oldVersion"); + is(event.newVersion, null, "got correct newVersion"); + db.close(); + }; + + let objectStore = db.createObjectStore(objectStoreName, + { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("creating mutable file"); + + request = db.createMutableFile(mutableFileName); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + + verifyMutableFile(mutableFile, getFile(mutableFileName, "", "")); + yield undefined; + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + info("adding mutable file"); + + request = objectStore.add(mutableFile, mutableFileKey); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("getting mutable file"); + + request = objectStore.get(mutableFileKey); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyMutableFile(event.target.result, + getFile(mutableFileName, "", "")); + yield undefined; + + info("opening database"); + + request = indexedDB.open(databaseName, databaseVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let db2 = event.target.result; + db2.onerror = errorHandler; + db2.onversionchange = function(event) { + is(event.oldVersion, databaseVersion, "got correct oldVersion"); + is(event.newVersion, null, "got correct newVersion"); + db2.close(); + }; + + objectStore = db2.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + info("adding mutable file"); + + request = objectStore.add(mutableFile); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("getting mutable file"); + + request = objectStore.get(event.target.result); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyMutableFile(event.target.result, + getFile(mutableFileName, "", "")); + yield undefined; + + info("setting file handle pref"); + + SpecialPowers.pushPrefEnv({ set: [["dom.fileHandle.enabled", false]] }, + continueToNextStep); + yield undefined; + + info("opening database"); + + request = indexedDB.open(databaseName, databaseVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let db3 = event.target.result; + db3.onerror = errorHandler; + db3.onversionchange = function(event) { + is(event.oldVersion, databaseVersion, "got correct oldVersion"); + is(event.newVersion, null, "got correct newVersion"); + db3.close(); + }; + + info("creating mutable file"); + + try { + db3.createMutableFile(mutableFileName); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + objectStore = db3.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + info("adding mutable file"); + + try { + objectStore.add(mutableFile); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "DataCloneError", "Good error."); + is(e.code, DOMException.DATA_CLONE_ERR, "Good error code."); + } + + info("getting mutable file"); + + request = objectStore.get(mutableFileKey); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + try { + let result = request.result; + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + info("deleting database"); + + request = indexedDB.deleteDatabase(databaseName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + info("resetting file handle pref"); + + SpecialPowers.popPrefEnv(continueToNextStep); + yield undefined; + + finishTest(); + yield undefined; + } + </script> + + <script type="text/javascript;version=1.7" src="helpers.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + + </head> + + <body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_getFile.html b/dom/indexedDB/test/test_filehandle_getFile.html new file mode 100644 index 000000000..f17689f1d --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_getFile.html @@ -0,0 +1,53 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + request = mutableFile.getFile(); + ok(request instanceof DOMRequest, "Correct interface"); + ok(!(request instanceof IDBFileRequest), "Correct interface"); + ok(!('fileHandle' in request), "Property should not exist"); + ok(request.fileHandle === undefined, "Property should not exist"); + ok(!('lockedFile' in request), "Property should not exist"); + ok(request.lockedFile === undefined, "Property should not exist"); + ok(!('onprogress' in request), "Property should not exist"); + ok(request.onprogress === undefined, "Property should not exist"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_iteration.html b/dom/indexedDB/test/test_filehandle_iteration.html new file mode 100644 index 000000000..ebbc8b8be --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_iteration.html @@ -0,0 +1,77 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const dbName = window.location.pathname; + const dbVersion = 1; + const objectStoreName = "foo"; + const entryCount = 10; + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + db.createObjectStore(objectStoreName, { autoIncrement: true }); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + request = db.createMutableFile("bar"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + + let trans = db.transaction(objectStoreName, "readwrite"); + let objectStore = trans.objectStore(objectStoreName); + + for (let i = 0; i < entryCount; i++) { + request = objectStore.add(mutableFile); + } + + let seenEntryCount = 0; + + request = objectStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + seenEntryCount++; + cursor.continue(); + } else { + continueToNextStep(); + } + } + yield undefined; + + is(seenEntryCount, entryCount, "Correct entry count"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_lifetimes.html b/dom/indexedDB/test/test_filehandle_lifetimes.html new file mode 100644 index 000000000..6e4946821 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_lifetimes.html @@ -0,0 +1,57 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open(); + continueToNextStep(); + yield undefined; + + try { + fileHandle.getMetadata({ size: true }); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "FileHandleInactiveError", "Good error."); + is(e.code, 0, "Good error code."); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html new file mode 100644 index 000000000..78fc235e8 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html @@ -0,0 +1,69 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open(); + + let fileHandle2; + + let comp = SpecialPowers.wrap(SpecialPowers.Components); + let thread = comp.classes["@mozilla.org/thread-manager;1"] + .getService(comp.interfaces.nsIThreadManager) + .currentThread; + + let eventHasRun; + + thread.dispatch(function() { + eventHasRun = true; + + fileHandle2 = mutableFile.open(); + }, SpecialPowers.Ci.nsIThread.DISPATCH_NORMAL); + + while (!eventHasRun) { + thread.processNextEvent(false); + } + + ok(fileHandle2, "Non-null fileHandle2"); + + continueToNextStep(); + yield undefined; + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_location.html b/dom/indexedDB/test/test_filehandle_location.html new file mode 100644 index 000000000..332fc9af1 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_location.html @@ -0,0 +1,104 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + is(fileHandle.location, 0, "Correct location"); + + fileHandle.location = 100000; + is(fileHandle.location, 100000, "Correct location"); + + fileHandle.location = null; + ok(fileHandle.location === null, "Correct location"); + + try { + fileHandle.readAsArrayBuffer(1); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + try { + fileHandle.readAsText(1); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + try { + fileHandle.write({}); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + request = fileHandle.append("foo"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(fileHandle.location === null, "Correct location"); + + try { + fileHandle.truncate(); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + request = fileHandle.truncate(0); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(fileHandle.location, 0, "Correct location"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_ordering.html b/dom/indexedDB/test/test_filehandle_ordering.html new file mode 100644 index 000000000..0f402ed07 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_ordering.html @@ -0,0 +1,62 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle1 = mutableFile.open("readwrite"); + let fileHandle2 = mutableFile.open("readwrite"); + + let request1 = fileHandle2.write("2"); + let request2 = fileHandle1.write("1"); + + fileHandle1.oncomplete = grabEventAndContinueHandler; + fileHandle2.oncomplete = grabEventAndContinueHandler; + + yield undefined; + yield undefined; + + let fileHandle3 = mutableFile.open("readonly"); + let request3 = fileHandle3.readAsText(1); + request3.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.target.result, "2", "File handles were ordered properly."); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_overlapping.html b/dom/indexedDB/test/test_filehandle_overlapping.html new file mode 100644 index 000000000..9f4cf8e6d --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_overlapping.html @@ -0,0 +1,73 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + for (let i = 0; i < 50; i++) { + let stepNumber = 0; + + request = mutableFile.open("readwrite").append("string1"); + request.onsuccess = function(event) { + is(stepNumber, 1, "This callback came first"); + stepNumber++; + event.target.fileHandle.oncomplete = grabEventAndContinueHandler; + } + + request = mutableFile.open("readwrite").append("string2"); + request.onsuccess = function(event) { + is(stepNumber, 2, "This callback came second"); + stepNumber++; + event.target.fileHandle.oncomplete = grabEventAndContinueHandler; + } + + request = mutableFile.open("readwrite").append("string3"); + request.onsuccess = function(event) { + is(stepNumber, 3, "This callback came third"); + stepNumber++; + event.target.fileHandle.oncomplete = grabEventAndContinueHandler; + } + + stepNumber++; + yield undefined; yield undefined; yield undefined;; + + is(stepNumber, 4, "All callbacks received"); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_progress_events.html b/dom/indexedDB/test/test_filehandle_progress_events.html new file mode 100644 index 000000000..0d2c7f7cb --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_progress_events.html @@ -0,0 +1,79 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + var testBuffer = getRandomBuffer(100000); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + + let sum = 0; + + request = fileHandle.write(testBuffer); + request.onprogress = function(event) { + let loaded = event.loaded; + let total = event.total; + ok(loaded >= 0 && loaded <= total, "Correct loaded progress"); + is(total, testBuffer.byteLength, "Correct total progress"); + sum += event.loaded - sum; + } + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(sum, testBuffer.byteLength, "Correct loaded progress sum"); + + sum = 0; + + fileHandle.location = 0; + request = fileHandle.readAsArrayBuffer(testBuffer.byteLength); + request.onprogress = function(event) { + let loaded = event.loaded; + let total = event.total; + ok(loaded >= 0 && loaded <= total, "Correct loaded progress"); + is(total, testBuffer.byteLength, "Correct total progress"); + sum += event.loaded - sum; + } + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(sum, testBuffer.byteLength, "Correct loaded progress sum"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html new file mode 100644 index 000000000..566e5361b --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html @@ -0,0 +1,81 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + request = mutableFile.open("readwrite").write({}); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.fileHandle.mode, "readwrite", "Correct mode"); + + try { + mutableFile.open().write({}); + ok(false, "Writing to a readonly file handle should fail!"); + } + catch (e) { + ok(true, "Writing to a readonly file handle failed"); + } + + try { + mutableFile.open().append({}); + ok(false, "Appending to a readonly file handle should fail!"); + } + catch (e) { + ok(true, "Appending to a readonly file handle failed"); + } + + try { + mutableFile.open().truncate({}); + ok(false, "Truncating a readonly file handle should fail!"); + } + catch (e) { + ok(true, "Truncating a readonly file handle failed"); + } + + try { + mutableFile.open().flush({}); + ok(false, "Flushing a readonly file handle should fail!"); + } + catch (e) { + ok(true, "Flushing a readonly file handle failed"); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_request_readyState.html b/dom/indexedDB/test/test_filehandle_request_readyState.html new file mode 100644 index 000000000..d9cea030c --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_request_readyState.html @@ -0,0 +1,69 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + is(request.readyState, "pending", "Correct readyState"); + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(request.readyState, "done", "Correct readyState"); + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + is(request.readyState, "pending", "Correct readyState"); + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(request.readyState, "done", "Correct readyState"); + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + request = fileHandle.write("string"); + is(request.readyState, "pending", "Correct readyState"); + + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(request.readyState, "done", "Correct readyState"); + + fileHandle.location = 0; + request = fileHandle.readAsText(6); + request.onsuccess = grabEventAndContinueHandler; + is(request.readyState, "pending", "Correct readyState"); + event = yield undefined; + + ok(event.target.result, "Got something"); + is(request.readyState, "done", "Correct readyState"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_serialization.html b/dom/indexedDB/test/test_filehandle_serialization.html new file mode 100644 index 000000000..703a40e75 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_serialization.html @@ -0,0 +1,101 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const databaseInfo = [ + { name: window.location.pathname + "1" }, + { name: window.location.pathname + "2" } + ]; + + const objectStoreName = "Blobs"; + + const testFile = getRandomFile("random.bin", 100000); + + let databases = []; + for (let info of databaseInfo) { + let request = indexedDB.open(info.name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + databases.push(db); + } + + let db1 = databases[0]; + + let request = db1.createMutableFile("random.bin", "binary/random"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + is(getFileId(mutableFile), 1, "Correct file id"); + is(mutableFile.name, "random.bin", "Correct name"); + is(mutableFile.type, "binary/random", "Correct type"); + + let trans = db1.transaction([objectStoreName], READ_WRITE); + let objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(mutableFile, 42); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + request = objectStore.get(42); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + is(getFileId(result), 1, "Correct file id"); + is(result.name, mutableFile.name, "Correct name"); + is(result.type, mutableFile.type, "Correct type"); + + let db2 = databases[1]; + + trans = db2.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + try { + objectStore.add(mutableFile, 42); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "DataCloneError", "Good error."); + is(e.code, DOMException.DATA_CLONE_ERR, "Good error code."); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_store_snapshot.html b/dom/indexedDB/test/test_filehandle_store_snapshot.html new file mode 100644 index 000000000..82d14256f --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_store_snapshot.html @@ -0,0 +1,98 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const READ_WRITE = "readwrite"; + + const name = window.location.pathname; + + const objectStoreName = "Blobs"; + + const testFile = getRandomFile("random.bin", 100000); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + request = db.createMutableFile("random.bin", "binary/random"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + + is(getFileId(mutableFile), 1, "Correct file id"); + + request = fileHandle.write(testFile); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + request = mutableFile.getFile(); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let file = event.target.result; + + let trans = db.transaction([objectStoreName], READ_WRITE); + objectStore = trans.objectStore(objectStoreName); + + request = objectStore.add(file, 42); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + // At this moment, the file should not be readable anymore. + let reader = new FileReader(); + try { + reader.readAsArrayBuffer(file); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "FileHandleInactiveError", "Good error."); + is(e.code, 0, "Good error code."); + } + + request = objectStore.get(42); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, testFile, 2); + yield undefined; + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_stream_tracking.html b/dom/indexedDB/test/test_filehandle_stream_tracking.html new file mode 100644 index 000000000..f85ff6fc3 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_stream_tracking.html @@ -0,0 +1,112 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + var testBuffer = getRandomBuffer(100000); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + + request = fileHandle.write(testBuffer); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + request = mutableFile.getFile(); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let file = event.target.result; + + let resultBuffer1; + let resultBuffer2; + + let reader1 = new FileReader(); + reader1.readAsArrayBuffer(file); + reader1.onerror = errorHandler; + reader1.onload = function(event) + { + resultBuffer1 = event.target.result; + + let reader = new FileReader(); + try { + reader.readAsArrayBuffer(file); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "FileHandleInactiveError", "Good error."); + is(e.code, 0, "Good error code."); + } + + if (resultBuffer2) { + testGenerator.next(); + } + } + + let reader2 = new FileReader(); + reader2.readAsArrayBuffer(file); + reader2.onerror = errorHandler; + reader2.onload = function(event) + { + resultBuffer2 = event.target.result; + + let reader = new FileReader(); + try { + reader.readAsArrayBuffer(file); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "FileHandleInactiveError", "Good error."); + is(e.code, 0, "Good error code."); + } + + if (resultBuffer1) { + testGenerator.next(); + } + } + + yield undefined; + + ok(compareBuffers(resultBuffer1, testBuffer), "Correct data"); + ok(compareBuffers(resultBuffer2, testBuffer), "Correct data"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html new file mode 100644 index 000000000..5b0c4de4e --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html @@ -0,0 +1,74 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open(); + + fileHandle.oncomplete = unexpectedSuccessHandler; + fileHandle.onabort = grabEventAndContinueHandler; + + let sawError = false; + + request = fileHandle.getMetadata({ size: true }); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = function(event) { + is(event.target.error.name, "AbortError", "Good error"); + sawError = true; + event.stopPropagation(); + } + + fileHandle.abort(); + + event = yield undefined; + + is(event.type, "abort", "Got abort event"); + is(sawError, true, "Saw getMetadata() error"); + + // Make sure the success event isn't queued somehow. + let comp = SpecialPowers.wrap(SpecialPowers.Components); + var thread = comp.classes["@mozilla.org/thread-manager;1"] + .getService(comp.interfaces.nsIThreadManager) + .currentThread; + while (thread.hasPendingEvents()) { + thread.processNextEvent(false); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_truncate.html b/dom/indexedDB/test/test_filehandle_truncate.html new file mode 100644 index 000000000..4dfe331f3 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_truncate.html @@ -0,0 +1,91 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + var testBuffer = getRandomBuffer(100000); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.bin"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + request = fileHandle.write(testBuffer); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(fileHandle.location, 100000, "Correct location"); + + for (let i = 0; i < 10; i++) { + let location = fileHandle.location - 10000; + fileHandle.location = location; + + request = fileHandle.truncate(); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(fileHandle.location, location, "Correct location"); + + request = fileHandle.getMetadata({ size: true }); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.size, location, "Correct size"); + } + + request = fileHandle.write(testBuffer); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let location = fileHandle.location; + for (let i = 0; i < 10; i++) { + location -= 10000; + + request = fileHandle.truncate(location); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(fileHandle.location, location, "Correct location"); + + request = fileHandle.getMetadata({ size: true }); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.size, location, "Correct size"); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_workers.html b/dom/indexedDB/test/test_filehandle_workers.html new file mode 100644 index 000000000..632ab24fd --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_workers.html @@ -0,0 +1,151 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + let testBuffer = getRandomBuffer(100000); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = errorHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + db.createObjectStore("Foo", { }); + + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + + function dummyWorkerScript() { + onmessage = function(event) { + throw("Shouldn't be called!"); + } + } + + let url = + URL.createObjectURL(new Blob(["(", dummyWorkerScript.toSource(), ")()"])); + + let worker1 = new Worker(url); + try { + worker1.postMessage(mutableFile); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "DataCloneError", "Good error."); + is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.") + } + + mutableFile.onerror = errorHandler; + + let fileHandle = mutableFile.open("readwrite"); + + request = fileHandle.write(testBuffer); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + request = mutableFile.getFile(); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let file = event.target.result; + + let worker2 = new Worker(url); + URL.revokeObjectURL(url); + try { + worker2.postMessage(file); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got exception."); + is(e.name, "DataCloneError", "Good error."); + is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.") + } + + let objectStore = + db.transaction("Foo", "readwrite").objectStore("Foo"); + + request = objectStore.add(mutableFile, 42); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + function workerScript() { + onmessage = function(event) { + var name = event.data; + var request = indexedDB.open(name, 1); + request.onsuccess = function(event) { + var db = event.target.result; + let objectStore = db.transaction("Foo").objectStore("Foo"); + request = objectStore.get(42); + request.onsuccess = function(event) { + try { + let result = request.result; + postMessage("error"); + } + catch (e) { + postMessage("success"); + } + } + request.onerror = function(event) { + postMessage("error"); + } + } + request.onerror = function(event) { + postMessage("error"); + } + } + } + + url = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let worker3 = new Worker(url); + URL.revokeObjectURL(url); + worker3.postMessage(name); + worker3.onmessage = grabEventAndContinueHandler; + event = yield undefined; + + is(event.data, "success", "Good response."); + + todo(false, "Terminate all workers at the end of the test to work around bug 1340941."); + worker1.terminate(); + worker2.terminate(); + worker3.terminate(); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_filehandle_write_read_data.html b/dom/indexedDB/test/test_filehandle_write_read_data.html new file mode 100644 index 000000000..036d57117 --- /dev/null +++ b/dom/indexedDB/test/test_filehandle_write_read_data.html @@ -0,0 +1,110 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis."; + for (let i = 0; i < 5; i++) { + testString += testString; + } + + var testBuffer = getRandomBuffer(100000); + + var testBlob = new Blob([testBuffer], {type: "binary/random"}); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let location = 0; + + let fileHandle = mutableFile.open("readwrite"); + is(fileHandle.location, location, "Correct location"); + + request = fileHandle.write(testString); + location += testString.length; + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location = 0; + request = fileHandle.readAsText(testString.length); + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let resultString = event.target.result; + ok(resultString == testString, "Correct string data"); + + request = fileHandle.write(testBuffer); + location += testBuffer.byteLength; + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location -= testBuffer.byteLength; + request = fileHandle.readAsArrayBuffer(testBuffer.byteLength); + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let resultBuffer = event.target.result; + ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data"); + + request = fileHandle.write(testBlob); + location += testBlob.size; + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location -= testBlob.size; + request = fileHandle.readAsArrayBuffer(testBlob.size); + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + resultBuffer = event.target.result; + ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data"); + + request = fileHandle.getMetadata({ size: true }); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + is(result.size, location, "Correct size"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_getAll.html b/dom/indexedDB/test/test_getAll.html new file mode 100644 index 000000000..770d6fcb7 --- /dev/null +++ b/dom/indexedDB/test/test_getAll.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_getAll.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_getFileId.html b/dom/indexedDB/test/test_getFileId.html new file mode 100644 index 000000000..c36b9bc1f --- /dev/null +++ b/dom/indexedDB/test/test_getFileId.html @@ -0,0 +1,32 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>File Handle Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + let id = getFileId(null); + ok(id == -1, "Correct id"); + + id = getFileId(getRandomBlob(100)); + ok(id == -1, "Correct id"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_globalObjects_chrome.xul b/dom/indexedDB/test/test_globalObjects_chrome.xul new file mode 100644 index 000000000..47e967d96 --- /dev/null +++ b/dom/indexedDB/test/test_globalObjects_chrome.xul @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Mozilla Bug 832883" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="runTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <script type="application/javascript;version=1.7"> + <![CDATA[ + function testSteps() { + const name = window.location.pathname; + + // Test for IDBKeyRange and indexedDB availability in chrome windows. + var keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + var request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + ok(db, "Got database"); + + finishTest(); + yield undefined; + } + ]]> + </script> + + <script type="text/javascript;version=1.7" src="chromeHelpers.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=832883" + target="_blank">Mozilla Bug 832883</a> + </body> +</window> diff --git a/dom/indexedDB/test/test_globalObjects_content.html b/dom/indexedDB/test/test_globalObjects_content.html new file mode 100644 index 000000000..8a91a9ca3 --- /dev/null +++ b/dom/indexedDB/test/test_globalObjects_content.html @@ -0,0 +1,38 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + + // Test for IDBKeyRange and indexedDB availability in content windows. + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + ok(db, "Got database"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_globalObjects_other.xul b/dom/indexedDB/test/test_globalObjects_other.xul new file mode 100644 index 000000000..eb180a9b4 --- /dev/null +++ b/dom/indexedDB/test/test_globalObjects_other.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Mozilla Bug 832883" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="runTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <script type="application/javascript;version=1.7"> + <![CDATA[ + function testSteps() { + // Test for IDBKeyRange and indexedDB availability in bootstrap files. + let test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"]. + createInstance(Ci.nsISupports).wrappedJSObject; + test.ok = ok; + test.finishTest = continueToNextStep; + test.runTest(); + yield undefined; + + Cu.import("resource://gre/modules/AddonManager.jsm"); + AddonManager.getAddonByID("indexedDB-test@mozilla.org", + grabEventAndContinueHandler); + let addon = yield undefined; + addon.uninstall(); + + Cu.import("resource://gre/modules/Services.jsm"); + for (var stage of [ "install", "startup", "shutdown", "uninstall" ]) { + for (var symbol of [ "IDBKeyRange", "indexedDB" ]) { + let pref; + try { + pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage + + "." + symbol); + } + catch(ex) { + pref = false; + } + ok(pref, "Symbol '" + symbol + "' present during '" + stage + "'"); + } + } + + finishTest(); + yield undefined; + } + + window.runTest = function() { + SimpleTest.waitForExplicitFinish(); + + testGenerator.next(); + } + ]]> + </script> + + <script type="text/javascript;version=1.7" src="chromeHelpers.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=832883" + target="_blank">Mozilla Bug 832883</a> + </body> +</window> diff --git a/dom/indexedDB/test/test_global_data.html b/dom/indexedDB/test/test_global_data.html new file mode 100644 index 000000000..0a83b7428 --- /dev/null +++ b/dom/indexedDB/test/test_global_data.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_global_data.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_index_empty_keyPath.html b/dom/indexedDB/test/test_index_empty_keyPath.html new file mode 100644 index 000000000..2c67c0edf --- /dev/null +++ b/dom/indexedDB/test/test_index_empty_keyPath.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_index_empty_keyPath.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_index_getAll.html b/dom/indexedDB/test/test_index_getAll.html new file mode 100644 index 000000000..7db8c7078 --- /dev/null +++ b/dom/indexedDB/test/test_index_getAll.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_index_getAll.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_index_getAllObjects.html b/dom/indexedDB/test/test_index_getAllObjects.html new file mode 100644 index 000000000..fe7343ecc --- /dev/null +++ b/dom/indexedDB/test/test_index_getAllObjects.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_index_getAllObjects.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_index_object_cursors.html b/dom/indexedDB/test/test_index_object_cursors.html new file mode 100644 index 000000000..81f2d6ca5 --- /dev/null +++ b/dom/indexedDB/test/test_index_object_cursors.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_index_object_cursors.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_index_update_delete.html b/dom/indexedDB/test/test_index_update_delete.html new file mode 100644 index 000000000..5e7888fc9 --- /dev/null +++ b/dom/indexedDB/test/test_index_update_delete.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_index_update_delete.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_indexes.html b/dom/indexedDB/test/test_indexes.html new file mode 100644 index 000000000..7c7400db2 --- /dev/null +++ b/dom/indexedDB/test/test_indexes.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_indexes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_indexes_bad_values.html b/dom/indexedDB/test/test_indexes_bad_values.html new file mode 100644 index 000000000..54479ad2c --- /dev/null +++ b/dom/indexedDB/test/test_indexes_bad_values.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_indexes_bad_values.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_indexes_funny_things.html b/dom/indexedDB/test/test_indexes_funny_things.html new file mode 100644 index 000000000..a253d5f11 --- /dev/null +++ b/dom/indexedDB/test/test_indexes_funny_things.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_indexes_funny_things.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_invalid_cursor.html b/dom/indexedDB/test/test_invalid_cursor.html new file mode 100644 index 000000000..778675725 --- /dev/null +++ b/dom/indexedDB/test/test_invalid_cursor.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>IndexedDB Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_invalid_cursor.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_invalid_version.html b/dom/indexedDB/test/test_invalid_version.html new file mode 100644 index 000000000..ec83665ac --- /dev/null +++ b/dom/indexedDB/test/test_invalid_version.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_invalid_version.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_invalidate.html b/dom/indexedDB/test/test_invalidate.html new file mode 100644 index 000000000..45651953c --- /dev/null +++ b/dom/indexedDB/test/test_invalidate.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>IndexedDB Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_invalidate.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_key_requirements.html b/dom/indexedDB/test/test_key_requirements.html new file mode 100644 index 000000000..4945403f4 --- /dev/null +++ b/dom/indexedDB/test/test_key_requirements.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_key_requirements.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_keys.html b/dom/indexedDB/test/test_keys.html new file mode 100644 index 000000000..4657ce9c0 --- /dev/null +++ b/dom/indexedDB/test/test_keys.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_keys.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_leaving_page.html b/dom/indexedDB/test/test_leaving_page.html new file mode 100644 index 000000000..6eb46f50e --- /dev/null +++ b/dom/indexedDB/test/test_leaving_page.html @@ -0,0 +1,49 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Leaving Page Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> + +<body onload="runTest();"> + <iframe id="inner"></iframe> + <a id="a" href="leaving_page_iframe.html"></a> + + <script type="text/javascript;version=1.7"> + onmessage = function(e) { + ok(false, "gotmessage: " + e.data); + } + + function testSteps() + { + var iframe = $("inner"); + iframe.src = "leaving_page_iframe.html"; + iframe.onload = continueToNextStep; + yield undefined; + is(iframe.contentWindow.location.href, $("a").href, + "should navigate to iframe page"); + yield undefined; + is(iframe.contentWindow.location.href, "about:blank", + "should nagivate to about:blank"); + + let request = indexedDB.open(location, 1); + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.transaction(["mystore"]).objectStore("mystore").get(42).onsuccess = + grabEventAndContinueHandler; + event = yield undefined; + is(event.target.result.hello, "world", "second modification rolled back"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</html> diff --git a/dom/indexedDB/test/test_locale_aware_index_getAll.html b/dom/indexedDB/test/test_locale_aware_index_getAll.html new file mode 100644 index 000000000..f7c62635d --- /dev/null +++ b/dom/indexedDB/test/test_locale_aware_index_getAll.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_locale_aware_index_getAll.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_locale_aware_index_getAllObjects.html b/dom/indexedDB/test/test_locale_aware_index_getAllObjects.html new file mode 100644 index 000000000..d5d8c5c54 --- /dev/null +++ b/dom/indexedDB/test/test_locale_aware_index_getAllObjects.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_locale_aware_index_getAllObjects.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_locale_aware_indexes.html b/dom/indexedDB/test/test_locale_aware_indexes.html new file mode 100644 index 000000000..994f6c6f7 --- /dev/null +++ b/dom/indexedDB/test/test_locale_aware_indexes.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_locale_aware_indexes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_lowDiskSpace.html b/dom/indexedDB/test/test_lowDiskSpace.html new file mode 100644 index 000000000..cffd46549 --- /dev/null +++ b/dom/indexedDB/test/test_lowDiskSpace.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Low Disk Space Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_lowDiskSpace.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_maximal_serialized_object_size.html b/dom/indexedDB/test/test_maximal_serialized_object_size.html new file mode 100644 index 000000000..efb4e98e3 --- /dev/null +++ b/dom/indexedDB/test/test_maximal_serialized_object_size.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Test Maximal Size of a Serialized Object</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_maximal_serialized_object_size.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_message_manager_ipc.html b/dom/indexedDB/test/test_message_manager_ipc.html new file mode 100644 index 000000000..f89cb9f89 --- /dev/null +++ b/dom/indexedDB/test/test_message_manager_ipc.html @@ -0,0 +1,343 @@ +<!DOCTYPE html> +<html> + <head> + <title>Test for sending IndexedDB Blobs through MessageManager</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body onload="setup();"> + <script type="application/javascript;version=1.7"> +"use strict"; + +function childFrameScript() { + "use strict"; + + const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + + const mmName = "test:idb-and-mm"; + + const dbName = "test_message_manager_ipc.html - CHILD"; + const dbVersion = 1; + const objStoreName = "bar"; + const key = 1; + + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + + Cu.importGlobalProperties(["indexedDB"]); + + function info(msg) { + sendAsyncMessage(mmName, { op: "info", msg: msg }); + } + + function ok(condition, name, diag) { + sendAsyncMessage(mmName, + { op: "ok", + condition: condition, + name: name, + diag: diag }); + } + + function is(a, b, name) { + let pass = a == b; + let diag = pass ? "" : "got " + a + ", expected " + b; + ok(pass, name, diag); + } + + function finish(result) { + sendAsyncMessage(mmName, { op: "done", result: result }); + } + + function grabAndContinue(arg) { + testGenerator.send(arg); + } + + function errorHandler(event) { + ok(false, + event.target + " received error event: '" + event.target.error.name + + "'"); + finish(); + } + + function testSteps() { + addMessageListener(mmName, grabAndContinue); + let message = yield undefined; + + let blob = message.data; + + ok(blob instanceof Ci.nsIDOMBlob, "Message manager sent a blob"); + is(blob.size, blobText.length, "Blob has correct length"); + is(blob.type, blobType, "Blob has correct type"); + + info("Reading blob"); + + let reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(blob); + + yield undefined; + + is(reader.result, blobText, "Blob has correct data"); + + let slice = blob.slice(0, blobData[0].length, blobType); + + ok(slice instanceof Ci.nsIDOMBlob, "Slice returned a blob"); + is(slice.size, blobData[0].length, "Slice has correct length"); + is(slice.type, blobType, "Slice has correct type"); + + info("Reading slice"); + + reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(slice); + + yield undefined; + + is(reader.result, blobData[0], "Slice has correct data"); + + info("Deleting database"); + + let req = indexedDB.deleteDatabase(dbName); + req.onerror = errorHandler; + req.onsuccess = grabAndContinue; + + let event = yield undefined; + is(event.type, "success", "Got success event"); + + info("Opening database"); + + req = indexedDB.open(dbName, dbVersion); + req.onerror = errorHandler; + req.onupgradeneeded = grabAndContinue; + req.onsuccess = grabAndContinue; + + event = yield undefined; + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + event.target.result.createObjectStore(objStoreName); + + event = yield undefined; + is(event.type, "success", "Got success event"); + + let db = event.target.result; + + info("Storing blob from message manager in database"); + + let objectStore = + db.transaction(objStoreName, "readwrite").objectStore(objStoreName); + req = objectStore.add(blob, key); + req.onerror = errorHandler; + req.onsuccess = grabAndContinue; + + event = yield undefined; + + info("Getting blob from database"); + + objectStore = db.transaction(objStoreName).objectStore(objStoreName); + req = objectStore.get(key); + req.onerror = errorHandler; + req.onsuccess = grabAndContinue; + + event = yield undefined; + + blob = event.target.result; + + ok(blob instanceof Ci.nsIDOMBlob, "Database gave us a blob"); + is(blob.size, blobText.length, "Blob has correct length"); + is(blob.type, blobType, "Blob has correct type"); + + info("Reading blob"); + + reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(blob); + + yield undefined; + + is(reader.result, blobText, "Blob has correct data"); + + info("Storing slice from message manager in database"); + + objectStore = + db.transaction(objStoreName, "readwrite").objectStore(objStoreName); + req = objectStore.put(slice, key); + req.onerror = errorHandler; + req.onsuccess = grabAndContinue; + + event = yield undefined; + + info("Getting slice from database"); + + objectStore = db.transaction(objStoreName).objectStore(objStoreName); + req = objectStore.get(key); + req.onerror = errorHandler; + req.onsuccess = grabAndContinue; + + event = yield undefined; + + slice = event.target.result; + + ok(slice instanceof Ci.nsIDOMBlob, "Database gave us a blob"); + is(slice.size, blobData[0].length, "Slice has correct length"); + is(slice.type, blobType, "Slice has correct type"); + + info("Reading Slice"); + + reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(slice); + + yield undefined; + + is(reader.result, blobData[0], "Slice has correct data"); + + info("Sending blob and slice from database to message manager"); + finish([blob, slice]); + + yield undefined; + } + + let testGenerator = testSteps(); + testGenerator.next(); +} + +function parentFrameScript(mm) { + const messageName = "test:idb-and-mm"; + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + const blob = new Blob(blobData, { type: blobType }); + + function grabAndContinue(arg) { + testGenerator.send(arg); + } + + function testSteps() { + let result = yield undefined; + + is(Array.isArray(result), true, "Child delivered an array of results"); + is(result.length, 2, "Child delivered two results"); + + let blob = result[0]; + is(blob instanceof Blob, true, "Child delivered a blob"); + is(blob.size, blobText.length, "Blob has correct size"); + is(blob.type, blobType, "Blob has correct type"); + + let slice = result[1]; + is(slice instanceof Blob, true, "Child delivered a slice"); + is(slice.size, blobData[0].length, "Slice has correct size"); + is(slice.type, blobType, "Slice has correct type"); + + info("Reading blob"); + + let reader = new FileReader(); + reader.onload = grabAndContinue; + reader.readAsText(blob); + yield undefined; + + is(reader.result, blobText, "Blob has correct data"); + + info("Reading slice"); + + reader = new FileReader(); + reader.onload = grabAndContinue; + reader.readAsText(slice); + yield undefined; + + is(reader.result, blobData[0], "Slice has correct data"); + + slice = blob.slice(0, blobData[0].length, blobType); + + is(slice instanceof Blob, true, "Made a new slice from blob"); + is(slice.size, blobData[0].length, "Second slice has correct size"); + is(slice.type, blobType, "Second slice has correct type"); + + info("Reading second slice"); + + reader = new FileReader(); + reader.onload = grabAndContinue; + reader.readAsText(slice); + yield undefined; + + is(reader.result, blobData[0], "Second slice has correct data"); + + SimpleTest.finish(); + yield undefined; + } + + let testGenerator = testSteps(); + testGenerator.next(); + + mm.addMessageListener(messageName, function(message) { + let data = message.data; + switch (data.op) { + case "info": { + info(data.msg); + break; + } + + case "ok": { + ok(data.condition, data.name, data.diag); + break; + } + + case "done": { + testGenerator.send(data.result); + break; + } + + default: { + ok(false, "Unknown op: " + data.op); + SimpleTest.finish(); + } + } + }); + + mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();", + false); + + mm.sendAsyncMessage(messageName, blob); +} + +function setup() { + info("Got load event"); + + SpecialPowers.pushPrefEnv( + { set: [ ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["browser.pagethumbnails.capturing_disabled", true] ] }, + function() { + info("Prefs set"); + + SpecialPowers.pushPermissions( + [ { type: "browser", allow: true, context: document } ], + function() { + info("Permissions set"); + + let iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = + "data:text/html,<!DOCTYPE HTML><html><body></body></html>"; + + iframe.addEventListener("mozbrowserloadend", function() { + info("Starting tests"); + + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe) + parentFrameScript(mm); + }); + + document.body.appendChild(iframe); + } + ); + } + ); +} + +SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> diff --git a/dom/indexedDB/test/test_multientry.html b/dom/indexedDB/test/test_multientry.html new file mode 100644 index 000000000..8523e30b5 --- /dev/null +++ b/dom/indexedDB/test/test_multientry.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_multientry.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_names_sorted.html b/dom/indexedDB/test/test_names_sorted.html new file mode 100644 index 000000000..42bffbc14 --- /dev/null +++ b/dom/indexedDB/test/test_names_sorted.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_names_sorted.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_objectCursors.html b/dom/indexedDB/test/test_objectCursors.html new file mode 100644 index 000000000..e05634a2a --- /dev/null +++ b/dom/indexedDB/test/test_objectCursors.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_objectCursors.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_objectStore_getAllKeys.html b/dom/indexedDB/test/test_objectStore_getAllKeys.html new file mode 100644 index 000000000..7ae98882d --- /dev/null +++ b/dom/indexedDB/test/test_objectStore_getAllKeys.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_objectStore_getAllKeys.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html b/dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html new file mode 100644 index 000000000..a66147232 --- /dev/null +++ b/dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_objectStore_inline_autoincrement_key_added_on_put.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_objectStore_openKeyCursor.html b/dom/indexedDB/test/test_objectStore_openKeyCursor.html new file mode 100644 index 000000000..4c6437071 --- /dev/null +++ b/dom/indexedDB/test/test_objectStore_openKeyCursor.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_objectStore_openKeyCursor.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_objectStore_remove_values.html b/dom/indexedDB/test/test_objectStore_remove_values.html new file mode 100644 index 000000000..a18349236 --- /dev/null +++ b/dom/indexedDB/test/test_objectStore_remove_values.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_objectStore_remove_values.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_object_identity.html b/dom/indexedDB/test/test_object_identity.html new file mode 100644 index 000000000..1eda2e7f0 --- /dev/null +++ b/dom/indexedDB/test/test_object_identity.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_object_identity.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_odd_result_order.html b/dom/indexedDB/test/test_odd_result_order.html new file mode 100644 index 000000000..b95315343 --- /dev/null +++ b/dom/indexedDB/test/test_odd_result_order.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_odd_result_order.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_open_empty_db.html b/dom/indexedDB/test/test_open_empty_db.html new file mode 100644 index 000000000..84a16733a --- /dev/null +++ b/dom/indexedDB/test/test_open_empty_db.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_open_empty_db.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_open_for_principal.html b/dom/indexedDB/test/test_open_for_principal.html new file mode 100644 index 000000000..96bc2c43e --- /dev/null +++ b/dom/indexedDB/test/test_open_for_principal.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + is("open" in indexedDB, true, "open() defined"); + is("openForPrincipal" in indexedDB, false, "openForPrincipal() not defined"); + + is("deleteDatabase" in indexedDB, true, "deleteDatabase() defined"); + is("deleteForPrincipal" in indexedDB, false, "deleteForPrincipal() not defined"); + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_open_objectStore.html b/dom/indexedDB/test/test_open_objectStore.html new file mode 100644 index 000000000..f83fd9e92 --- /dev/null +++ b/dom/indexedDB/test/test_open_objectStore.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_open_objectStore.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_optionalArguments.html b/dom/indexedDB/test/test_optionalArguments.html new file mode 100644 index 000000000..ae15f2da2 --- /dev/null +++ b/dom/indexedDB/test/test_optionalArguments.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_optionalArguments.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_overlapping_transactions.html b/dom/indexedDB/test/test_overlapping_transactions.html new file mode 100644 index 000000000..6371b04bc --- /dev/null +++ b/dom/indexedDB/test/test_overlapping_transactions.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_overlapping_transactions.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_persistenceType.html b/dom/indexedDB/test/test_persistenceType.html new file mode 100644 index 000000000..cc44ffb77 --- /dev/null +++ b/dom/indexedDB/test/test_persistenceType.html @@ -0,0 +1,93 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + function testSteps() + { + const name = window.location.pathname; + const version = 1; + const storages = ["persistent", "temporary", "default"]; + + const objectStoreName = "Foo"; + const data = { key: 1, value: "bar" }; + + try { + indexedDB.open(name, { version: version, storage: "unknown" }); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + for (let storage of storages) { + let request = indexedDB.open(name, { version: version, + storage: storage }); + + if (storage == "persistent" && + SpecialPowers.Services.appinfo.widgetToolkit == "android") { + request.onerror = expectedErrorHandler("InvalidStateError"); + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "error", "Got corrent event type"); + + continue; + } + + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + is(db.name, name, "Correct name"); + is(db.version, version, "Correct version"); + is(db.storage, storage, "Correct persistence type"); + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got no data"); + + request = objectStore.add(data.value, data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.key, "Got correct key"); + } + + finishTest(); + yield undefined; + } + </script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_put_get_values.html b/dom/indexedDB/test/test_put_get_values.html new file mode 100644 index 000000000..57bee9347 --- /dev/null +++ b/dom/indexedDB/test/test_put_get_values.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_put_get_values.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_put_get_values_autoIncrement.html b/dom/indexedDB/test/test_put_get_values_autoIncrement.html new file mode 100644 index 000000000..d468634a2 --- /dev/null +++ b/dom/indexedDB/test/test_put_get_values_autoIncrement.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_put_get_values_autoIncrement.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_readonly_transactions.html b/dom/indexedDB/test/test_readonly_transactions.html new file mode 100644 index 000000000..c6abd48ce --- /dev/null +++ b/dom/indexedDB/test/test_readonly_transactions.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_readonly_transactions.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_readwriteflush_disabled.html b/dom/indexedDB/test/test_readwriteflush_disabled.html new file mode 100644 index 000000000..08523317a --- /dev/null +++ b/dom/indexedDB/test/test_readwriteflush_disabled.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_readwriteflush_disabled.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_remove_index.html b/dom/indexedDB/test/test_remove_index.html new file mode 100644 index 000000000..7e52615d3 --- /dev/null +++ b/dom/indexedDB/test/test_remove_index.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_remove_index.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_remove_objectStore.html b/dom/indexedDB/test/test_remove_objectStore.html new file mode 100644 index 000000000..41bcf87c6 --- /dev/null +++ b/dom/indexedDB/test/test_remove_objectStore.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_remove_objectStore.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_rename_index.html b/dom/indexedDB/test/test_rename_index.html new file mode 100644 index 000000000..dfd77114c --- /dev/null +++ b/dom/indexedDB/test/test_rename_index.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_rename_index.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_rename_index_errors.html b/dom/indexedDB/test/test_rename_index_errors.html new file mode 100644 index 000000000..44a48be7b --- /dev/null +++ b/dom/indexedDB/test/test_rename_index_errors.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_rename_index_errors.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_rename_objectStore.html b/dom/indexedDB/test/test_rename_objectStore.html new file mode 100644 index 000000000..85cbb0cee --- /dev/null +++ b/dom/indexedDB/test/test_rename_objectStore.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_rename_objectStore.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_rename_objectStore_errors.html b/dom/indexedDB/test/test_rename_objectStore_errors.html new file mode 100644 index 000000000..e98fc8d11 --- /dev/null +++ b/dom/indexedDB/test/test_rename_objectStore_errors.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_rename_objectStore_errors.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_request_readyState.html b/dom/indexedDB/test/test_request_readyState.html new file mode 100644 index 000000000..8a21131d7 --- /dev/null +++ b/dom/indexedDB/test/test_request_readyState.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_request_readyState.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_sandbox.html b/dom/indexedDB/test/test_sandbox.html new file mode 100644 index 000000000..a6c627fb1 --- /dev/null +++ b/dom/indexedDB/test/test_sandbox.html @@ -0,0 +1,101 @@ +<!doctype html> +<html> +<head> + <title>indexedDB in JS Sandbox</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link> +</head> +<body> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +// This runs inside a same-origin sandbox. +// The intent being to show that the data store is the same. +function storeValue() { + function createDB_inner() { + var op = indexedDB.open('db'); + op.onupgradeneeded = e => { + var db = e.target.result; + db.createObjectStore('store'); + }; + return new Promise(resolve => { + op.onsuccess = e => resolve(e.target.result); + }); + } + + function add(k, v) { + return createDB_inner().then(db => { + var tx = db.transaction('store', 'readwrite'); + var store = tx.objectStore('store'); + var op = store.add(v, k); + return new Promise((resolve, reject) => { + op.onsuccess = e => resolve(e.target.result); + op.onerror = _ => reject(op.error); + tx.onabort = _ => reject(tx.error); + }); + }); + } + + return add('x', [ 10, {} ]) + .then(_ => step_done(), + _ => ok(false, 'failed to store')); +} + +function createDB_outer() { + var op = indexedDB.open('db'); + op.onupgradeneeded = e => { + ok(false, 'upgrade should not be needed'); + var db = e.target.result; + db.createObjectStore('store'); + }; + return new Promise(resolve => { + op.onsuccess = e => resolve(e.target.result); + }); +} + +function get(k) { + return createDB_outer().then(db => { + var tx = db.transaction('store', 'readonly'); + var store = tx.objectStore('store'); + var op = store.get(k); + return new Promise((resolve, reject) => { + op.onsuccess = e => resolve(e.target.result); + op.onerror = _ => reject(op.error); + tx.onabort = _ => reject(tx.error); + }); + }); +} + +function runInSandbox(sandbox, testFunc) { + is(typeof testFunc, 'function'); + var resolvePromise; + var testPromise = new Promise(r => resolvePromise = r); + SpecialPowers.Cu.exportFunction(_ => resolvePromise(), sandbox, + { defineAs: 'step_done' }); + SpecialPowers.Cu.evalInSandbox('(' + testFunc.toSource() + ')()' + + '.then(step_done);', sandbox); + return testPromise; +} + +// Use the window principal for the sandbox; location.origin is not sufficient. +var sb = new SpecialPowers.Cu.Sandbox(window, + { wantGlobalProperties: ['indexedDB'] }); + +sb.ok = SpecialPowers.Cu.exportFunction(ok, sb); + +Promise.resolve() + .then(_ => runInSandbox(sb, storeValue)) + .then(_ => get('x')) + .then(x => { + ok(x, 'a value should be present'); + is(x.length, 2); + is(x[0], 10); + is(typeof x[1], 'object'); + is(Object.keys(x[1]).length, 0); + }) + .then(_ => SimpleTest.finish()); + +</script> +</body> +</html> diff --git a/dom/indexedDB/test/test_serviceworker.html b/dom/indexedDB/test/test_serviceworker.html new file mode 100644 index 000000000..c37a70ffa --- /dev/null +++ b/dom/indexedDB/test/test_serviceworker.html @@ -0,0 +1,78 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1137245 - Allow IndexedDB usage in ServiceWorkers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var regisration; + function simpleRegister() { + return navigator.serviceWorker.register("service_worker.js", { + scope: 'service_worker_client.html' + }); + } + + function unregister() { + return registration.unregister(); + } + + function testIndexedDBAvailable(sw) { + registration = sw; + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data === "READY") { + sw.active.postMessage("GO"); + return; + } + + if (!("available" in e.data)) { + ok(false, "Something went wrong"); + reject(); + return; + } + + ok(e.data.available, "IndexedDB available in service worker."); + resolve(); + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "service_worker_client.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + simpleRegister() + .then(testIndexedDBAvailable) + .then(unregister) + .then(SimpleTest.finish) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/indexedDB/test/test_setVersion.html b/dom/indexedDB/test/test_setVersion.html new file mode 100644 index 000000000..6af88b063 --- /dev/null +++ b/dom/indexedDB/test/test_setVersion.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_setVersion.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_setVersion_abort.html b/dom/indexedDB/test/test_setVersion_abort.html new file mode 100644 index 000000000..6037148b7 --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_abort.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_setVersion_abort.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_setVersion_events.html b/dom/indexedDB/test/test_setVersion_events.html new file mode 100644 index 000000000..79a982844 --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_events.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_setVersion_events.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_setVersion_exclusion.html b/dom/indexedDB/test/test_setVersion_exclusion.html new file mode 100644 index 000000000..22b645fcc --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_exclusion.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_setVersion_exclusion.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_setVersion_throw.html b/dom/indexedDB/test/test_setVersion_throw.html new file mode 100644 index 000000000..015341b0c --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_throw.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_setVersion_throw.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_storage_manager_estimate.html b/dom/indexedDB/test/test_storage_manager_estimate.html new file mode 100644 index 000000000..1ba36ecef --- /dev/null +++ b/dom/indexedDB/test/test_storage_manager_estimate.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test for StorageManager</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_storage_manager_estimate.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="setup();"></body> + +</html> diff --git a/dom/indexedDB/test/test_success_events_after_abort.html b/dom/indexedDB/test/test_success_events_after_abort.html new file mode 100644 index 000000000..624cc75cc --- /dev/null +++ b/dom/indexedDB/test/test_success_events_after_abort.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_success_events_after_abort.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_table_locks.html b/dom/indexedDB/test/test_table_locks.html new file mode 100644 index 000000000..245d79eb4 --- /dev/null +++ b/dom/indexedDB/test/test_table_locks.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>IndexedDB Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_table_locks.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_table_rollback.html b/dom/indexedDB/test/test_table_rollback.html new file mode 100644 index 000000000..4c8664f7e --- /dev/null +++ b/dom/indexedDB/test/test_table_rollback.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_table_rollback.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_third_party.html b/dom/indexedDB/test/test_third_party.html new file mode 100644 index 000000000..91ea44fc3 --- /dev/null +++ b/dom/indexedDB/test/test_third_party.html @@ -0,0 +1,103 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7"> + const BEHAVIOR_ACCEPT = 0; + const BEHAVIOR_REJECTFOREIGN = 1; + const BEHAVIOR_REJECT = 2; + const BEHAVIOR_LIMITFOREIGN = 3; + + const testData = [ + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_ACCEPT, expectedResult: true }, + { host: "http://example.com", cookieBehavior: BEHAVIOR_ACCEPT, expectedResult: true }, + { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_ACCEPT, expectedResult: true }, + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_ACCEPT, expectedResult: true }, + + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_REJECT, expectedResult: false }, + { host: "http://example.com", cookieBehavior: BEHAVIOR_REJECT, expectedResult: false }, + { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_REJECT, expectedResult: false }, + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_REJECT, expectedResult: false }, + + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResult: true }, + { host: "http://example.com", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResult: false }, + { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResult: false }, + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResult: true }, + + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResult: true }, + { host: "http://example.com", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResult: false }, + { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResult: false }, + { host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResult: true } + ]; + + const iframe1Path = + window.location.pathname.replace("test_third_party.html", + "third_party_iframe1.html"); + const iframe2URL = + "http://" + window.location.host + + window.location.pathname.replace("test_third_party.html", + "third_party_iframe2.html"); + + let testIndex = 0; + let testRunning = false; + + function iframeLoaded() { + let message = { source: "parent", href: iframe2URL }; + let iframe = document.getElementById("iframe1"); + iframe.contentWindow.postMessage(message.toSource(), "*"); + } + + function setiframe() { + let iframe = document.getElementById("iframe1"); + + if (!testRunning) { + testRunning = true; + iframe.addEventListener("load", iframeLoaded, false); + } + SpecialPowers.pushPrefEnv({ + 'set': [["network.cookie.cookieBehavior", testData[testIndex].cookieBehavior]] + }, () => { + iframe.src = testData[testIndex].host + iframe1Path; + }); + // SpecialPowers.setIntPref("network.cookie.cookieBehavior", testData[testIndex].cookieBehavior); + } + + function messageListener(event) { + let message = eval(event.data); + + is(message.source, "iframe", "Good source"); + is(message.result, testData[testIndex].expectedResult, "Good result"); + + if (testIndex < testData.length - 1) { + testIndex++; + setiframe(); + return; + } + + SimpleTest.finish(); + } + + function runTest() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.addPermission("indexedDB", true, document); + + window.addEventListener("message", messageListener, false); + setiframe(); + } + </script> + +</head> + +<body onload="runTest();"> + <iframe id="iframe1"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/test_traffic_jam.html b/dom/indexedDB/test/test_traffic_jam.html new file mode 100644 index 000000000..8973b5021 --- /dev/null +++ b/dom/indexedDB/test/test_traffic_jam.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_traffic_jam.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_abort.html b/dom/indexedDB/test/test_transaction_abort.html new file mode 100644 index 000000000..849d43938 --- /dev/null +++ b/dom/indexedDB/test/test_transaction_abort.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_abort.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_abort_hang.html b/dom/indexedDB/test/test_transaction_abort_hang.html new file mode 100644 index 000000000..3a0fcfb4e --- /dev/null +++ b/dom/indexedDB/test/test_transaction_abort_hang.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_abort_hang.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_duplicate_store_names.html b/dom/indexedDB/test/test_transaction_duplicate_store_names.html new file mode 100644 index 000000000..c7187521e --- /dev/null +++ b/dom/indexedDB/test/test_transaction_duplicate_store_names.html @@ -0,0 +1,16 @@ +<!-- +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Test for Bug 1013221</title> + + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_duplicate_store_names.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> +<body onload="runTest();"></body> +</html> diff --git a/dom/indexedDB/test/test_transaction_error.html b/dom/indexedDB/test/test_transaction_error.html new file mode 100644 index 000000000..39a05ae35 --- /dev/null +++ b/dom/indexedDB/test/test_transaction_error.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_error.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_lifetimes.html b/dom/indexedDB/test/test_transaction_lifetimes.html new file mode 100644 index 000000000..0917f74e7 --- /dev/null +++ b/dom/indexedDB/test/test_transaction_lifetimes.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_lifetimes.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_lifetimes_nested.html b/dom/indexedDB/test/test_transaction_lifetimes_nested.html new file mode 100644 index 000000000..5f22c4ed5 --- /dev/null +++ b/dom/indexedDB/test/test_transaction_lifetimes_nested.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_lifetimes_nested.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_transaction_ordering.html b/dom/indexedDB/test/test_transaction_ordering.html new file mode 100644 index 000000000..ff5e6a6ed --- /dev/null +++ b/dom/indexedDB/test/test_transaction_ordering.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_transaction_ordering.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_unique_index_update.html b/dom/indexedDB/test/test_unique_index_update.html new file mode 100644 index 000000000..03d00a459 --- /dev/null +++ b/dom/indexedDB/test/test_unique_index_update.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_unique_index_update.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_view_put_get_values.html b/dom/indexedDB/test/test_view_put_get_values.html new file mode 100644 index 000000000..e98bf0b52 --- /dev/null +++ b/dom/indexedDB/test/test_view_put_get_values.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_view_put_get_values.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_wasm_cursors.html b/dom/indexedDB/test/test_wasm_cursors.html new file mode 100644 index 000000000..dabf1b101 --- /dev/null +++ b/dom/indexedDB/test/test_wasm_cursors.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_wasm_cursors.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_wasm_getAll.html b/dom/indexedDB/test/test_wasm_getAll.html new file mode 100644 index 000000000..ba3539a9f --- /dev/null +++ b/dom/indexedDB/test/test_wasm_getAll.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_wasm_getAll.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_wasm_index_getAllObjects.html b/dom/indexedDB/test/test_wasm_index_getAllObjects.html new file mode 100644 index 000000000..a2f812835 --- /dev/null +++ b/dom/indexedDB/test/test_wasm_index_getAllObjects.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_wasm_index_getAllObjects.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_wasm_indexes.html b/dom/indexedDB/test/test_wasm_indexes.html new file mode 100644 index 000000000..fe9f2071d --- /dev/null +++ b/dom/indexedDB/test/test_wasm_indexes.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_wasm_indexes.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_wasm_put_get_values.html b/dom/indexedDB/test/test_wasm_put_get_values.html new file mode 100644 index 000000000..5769ffdc2 --- /dev/null +++ b/dom/indexedDB/test/test_wasm_put_get_values.html @@ -0,0 +1,20 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_wasm_put_get_values.js"></script> + <script type="text/javascript;version=1.7" src="file.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/test_writer_starvation.html b/dom/indexedDB/test/test_writer_starvation.html new file mode 100644 index 000000000..857721747 --- /dev/null +++ b/dom/indexedDB/test/test_writer_starvation.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Property Test</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript;version=1.7" src="unit/test_writer_starvation.js"></script> + <script type="text/javascript;version=1.7" src="helpers.js"></script> + +</head> + +<body onload="runTest();"></body> + +</html> diff --git a/dom/indexedDB/test/third_party_iframe1.html b/dom/indexedDB/test/third_party_iframe1.html new file mode 100644 index 000000000..c828acc37 --- /dev/null +++ b/dom/indexedDB/test/third_party_iframe1.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + function messageListener(event) { + let message = eval(event.data); + + if (message.source == "parent") { + document.getElementById("iframe2").src = message.href; + } + else if (message.source == "iframe") { + parent.postMessage(event.data, "*"); + } + } + </script> + +</head> + +<body onload="window.addEventListener('message', messageListener, false);"> + <iframe id="iframe2"></iframe> +</body> + +</html> diff --git a/dom/indexedDB/test/third_party_iframe2.html b/dom/indexedDB/test/third_party_iframe2.html new file mode 100644 index 000000000..bcb260819 --- /dev/null +++ b/dom/indexedDB/test/third_party_iframe2.html @@ -0,0 +1,34 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Indexed Database Test</title> + + <script type="text/javascript;version=1.7"> + function report(result) { + let message = { source: "iframe" }; + message.result = result; + window.parent.postMessage(message.toSource(), "*"); + } + + function runIndexedDBTest() { + try { + let request = indexedDB.open(window.location.pathname, 1); + request.onsuccess = function(event) { + report(!!(event.target.result instanceof IDBDatabase)); + } + } + catch (e) { + report(false); + } + } + </script> + +</head> + +<body onload="runIndexedDBTest();"> +</body> + +</html> diff --git a/dom/indexedDB/test/unit/GlobalObjectsChild.js b/dom/indexedDB/test/unit/GlobalObjectsChild.js new file mode 100644 index 000000000..5351ff2f1 --- /dev/null +++ b/dom/indexedDB/test/unit/GlobalObjectsChild.js @@ -0,0 +1,38 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function ok(cond, msg) { + dump("ok(" + cond + ", \"" + msg + "\")"); + do_check_true(!!cond, Components.stack.caller); +} + +function finishTest() +{ + do_execute_soon(function() { + do_test_finished(); + }); +} + +function run_test() { + const name = "Splendid Test"; + + Cu.importGlobalProperties(["indexedDB"]); + + do_test_pending(); + + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = function(event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); + } + request.onsuccess = function(event) { + let db = event.target.result; + ok(db, "Got database"); + finishTest(); + } +} diff --git a/dom/indexedDB/test/unit/GlobalObjectsComponent.js b/dom/indexedDB/test/unit/GlobalObjectsComponent.js new file mode 100644 index 000000000..44bc1afe9 --- /dev/null +++ b/dom/indexedDB/test/unit/GlobalObjectsComponent.js @@ -0,0 +1,43 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.importGlobalProperties(["indexedDB"]); + +function GlobalObjectsComponent() { + this.wrappedJSObject = this; +} + +GlobalObjectsComponent.prototype = +{ + classID: Components.ID("{949ebf50-e0da-44b9-8335-cbfd4febfdcc}"), + + QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports]), + + runTest: function() { + const name = "Splendid Test"; + + let ok = this.ok; + let finishTest = this.finishTest; + + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = function(event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); + } + request.onsuccess = function(event) { + let db = event.target.result; + ok(db, "Got database"); + finishTest(); + } + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GlobalObjectsComponent]); diff --git a/dom/indexedDB/test/unit/GlobalObjectsComponent.manifest b/dom/indexedDB/test/unit/GlobalObjectsComponent.manifest new file mode 100644 index 000000000..be0a28bc1 --- /dev/null +++ b/dom/indexedDB/test/unit/GlobalObjectsComponent.manifest @@ -0,0 +1,2 @@ +component {949ebf50-e0da-44b9-8335-cbfd4febfdcc} GlobalObjectsComponent.js +contract @mozilla.org/dom/indexeddb/GlobalObjectsComponent;1 {949ebf50-e0da-44b9-8335-cbfd4febfdcc} diff --git a/dom/indexedDB/test/unit/GlobalObjectsModule.jsm b/dom/indexedDB/test/unit/GlobalObjectsModule.jsm new file mode 100644 index 000000000..fe214f722 --- /dev/null +++ b/dom/indexedDB/test/unit/GlobalObjectsModule.jsm @@ -0,0 +1,36 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.importGlobalProperties(["indexedDB"]); + +this.EXPORTED_SYMBOLS = [ + "GlobalObjectsModule" +]; + +this.GlobalObjectsModule = function GlobalObjectsModule() { +} + +GlobalObjectsModule.prototype = { + runTest: function() { + const name = "Splendid Test"; + + let ok = this.ok; + let finishTest = this.finishTest; + + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = function(event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); + } + request.onsuccess = function(event) { + let db = event.target.result; + ok(db, "Got database"); + finishTest(); + } + } +} diff --git a/dom/indexedDB/test/unit/GlobalObjectsSandbox.js b/dom/indexedDB/test/unit/GlobalObjectsSandbox.js new file mode 100644 index 000000000..094510271 --- /dev/null +++ b/dom/indexedDB/test/unit/GlobalObjectsSandbox.js @@ -0,0 +1,22 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function runTest() { + const name = "Splendid Test"; + + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = function(event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + finishTest(); + } + request.onsuccess = function(event) { + let db = event.target.result; + ok(db, "Got database"); + finishTest(); + } +} diff --git a/dom/indexedDB/test/unit/bug1056939_profile.zip b/dom/indexedDB/test/unit/bug1056939_profile.zip Binary files differnew file mode 100644 index 000000000..db3cfe624 --- /dev/null +++ b/dom/indexedDB/test/unit/bug1056939_profile.zip diff --git a/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip b/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip Binary files differnew file mode 100644 index 000000000..68bb4749f --- /dev/null +++ b/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip diff --git a/dom/indexedDB/test/unit/getUsage_profile.zip b/dom/indexedDB/test/unit/getUsage_profile.zip Binary files differnew file mode 100644 index 000000000..e484edd0c --- /dev/null +++ b/dom/indexedDB/test/unit/getUsage_profile.zip diff --git a/dom/indexedDB/test/unit/idbSubdirUpgrade1_profile.zip b/dom/indexedDB/test/unit/idbSubdirUpgrade1_profile.zip Binary files differnew file mode 100644 index 000000000..b7e434171 --- /dev/null +++ b/dom/indexedDB/test/unit/idbSubdirUpgrade1_profile.zip diff --git a/dom/indexedDB/test/unit/idbSubdirUpgrade2_profile.zip b/dom/indexedDB/test/unit/idbSubdirUpgrade2_profile.zip Binary files differnew file mode 100644 index 000000000..264c68d86 --- /dev/null +++ b/dom/indexedDB/test/unit/idbSubdirUpgrade2_profile.zip diff --git a/dom/indexedDB/test/unit/metadata2Restore_profile.zip b/dom/indexedDB/test/unit/metadata2Restore_profile.zip Binary files differnew file mode 100644 index 000000000..e5302b36c --- /dev/null +++ b/dom/indexedDB/test/unit/metadata2Restore_profile.zip diff --git a/dom/indexedDB/test/unit/metadataRestore_profile.zip b/dom/indexedDB/test/unit/metadataRestore_profile.zip Binary files differnew file mode 100644 index 000000000..a01d49166 --- /dev/null +++ b/dom/indexedDB/test/unit/metadataRestore_profile.zip diff --git a/dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip b/dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip Binary files differnew file mode 100644 index 000000000..4c89acf0a --- /dev/null +++ b/dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip diff --git a/dom/indexedDB/test/unit/oldDirectories_profile.zip b/dom/indexedDB/test/unit/oldDirectories_profile.zip Binary files differnew file mode 100644 index 000000000..09209d351 --- /dev/null +++ b/dom/indexedDB/test/unit/oldDirectories_profile.zip diff --git a/dom/indexedDB/test/unit/schema18upgrade_profile.zip b/dom/indexedDB/test/unit/schema18upgrade_profile.zip Binary files differnew file mode 100644 index 000000000..e13cce9d2 --- /dev/null +++ b/dom/indexedDB/test/unit/schema18upgrade_profile.zip diff --git a/dom/indexedDB/test/unit/schema21upgrade_profile.zip b/dom/indexedDB/test/unit/schema21upgrade_profile.zip Binary files differnew file mode 100644 index 000000000..d08f88ea5 --- /dev/null +++ b/dom/indexedDB/test/unit/schema21upgrade_profile.zip diff --git a/dom/indexedDB/test/unit/schema23upgrade_profile.zip b/dom/indexedDB/test/unit/schema23upgrade_profile.zip Binary files differnew file mode 100644 index 000000000..888d24434 --- /dev/null +++ b/dom/indexedDB/test/unit/schema23upgrade_profile.zip diff --git a/dom/indexedDB/test/unit/snappyUpgrade_profile.zip b/dom/indexedDB/test/unit/snappyUpgrade_profile.zip Binary files differnew file mode 100644 index 000000000..f9635fc9f --- /dev/null +++ b/dom/indexedDB/test/unit/snappyUpgrade_profile.zip diff --git a/dom/indexedDB/test/unit/storagePersistentUpgrade_profile.zip b/dom/indexedDB/test/unit/storagePersistentUpgrade_profile.zip Binary files differnew file mode 100644 index 000000000..b1082106b --- /dev/null +++ b/dom/indexedDB/test/unit/storagePersistentUpgrade_profile.zip diff --git a/dom/indexedDB/test/unit/test_abort_deleted_index.js b/dom/indexedDB/test/unit/test_abort_deleted_index.js new file mode 100644 index 000000000..8bd1f6ae2 --- /dev/null +++ b/dom/indexedDB/test/unit/test_abort_deleted_index.js @@ -0,0 +1,78 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName = "test store"; + const indexName_ToBeDeleted = "test index to be deleted"; + + info("Create index in v1."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore(storeName, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore.name, "Correct object store name"); + + // create index to be deleted later in v2. + objectStore.createIndex(indexName_ToBeDeleted, "foo"); + ok(objectStore.index(indexName_ToBeDeleted), "Index created."); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Delete index in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName); + let index = objectStore.index(indexName_ToBeDeleted); + ok(index, "index is valid."); + objectStore.deleteIndex(indexName_ToBeDeleted); + + // Aborting the transaction. + request.onerror = expectedErrorHandler("AbortError"); + txn.abort(); + try { + index.get('foo'); + ok(false, "TransactionInactiveError shall be thrown right after a deletion of an index is aborted."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "TransactionInactiveError shall be thrown right after a deletion of an index is aborted."); + } + + yield undefined; + + try { + index.get('foo'); + ok(false, "TransactionInactiveError shall be thrown after the transaction is inactive."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "TransactionInactiveError shall be thrown after the transaction is inactive."); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js b/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js new file mode 100644 index 000000000..98035b3da --- /dev/null +++ b/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js @@ -0,0 +1,74 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName_ToBeDeleted = "test store to be deleted"; + + info("Create objectStore in v1."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + // create objectstore to be deleted later in v2. + db.createObjectStore(storeName_ToBeDeleted, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Delete objectStore in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + let objectStore = txn.objectStore(storeName_ToBeDeleted); + ok(objectStore, "objectStore is available"); + + db.deleteObjectStore(storeName_ToBeDeleted); + + // Aborting the transaction. + request.onerror = expectedErrorHandler("AbortError"); + txn.abort(); + try { + objectStore.get('foo'); + ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "correct error"); + } + + yield undefined; + + try { + objectStore.get('foo'); + ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "correct error"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_add_put.js b/dom/indexedDB/test/unit/test_add_put.js new file mode 100644 index 000000000..923d19d7e --- /dev/null +++ b/dom/indexedDB/test/unit/test_add_put.js @@ -0,0 +1,165 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + let openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + let trans = event.target.transaction; + + for (let autoincrement of [true, false]) { + for (let keypath of [false, true, "missing", "invalid"]) { + for (let method of ["put", "add"]) { + for (let explicit of [true, false, undefined, "invalid"]) { + for (let existing of [true, false]) { + let speccedNoKey = (keypath == false || keypath == "missing") && + !explicit; + + // We can't do 'existing' checks if we use autogenerated key + if (speccedNoKey && autoincrement && existing) { + continue; + } + + // Create store + if (db.objectStoreNames.contains("mystore")) + db.deleteObjectStore("mystore"); + let store = db.createObjectStore("mystore", + { autoIncrement: autoincrement, + keyPath: (keypath ? "id" : null) }); + + test = " for test " + JSON.stringify({ autoincrement: autoincrement, + keypath: keypath, + method: method, + explicit: explicit === undefined ? "undefined" : explicit, + existing: existing }); + + // Insert "existing" data if needed + if (existing) { + if (keypath) + store.add({ existing: "data", id: 5 }).onsuccess = grabEventAndContinueHandler; + else + store.add({ existing: "data" }, 5).onsuccess = grabEventAndContinueHandler; + + let e = yield undefined; + is(e.type, "success", "success inserting existing" + test); + is(e.target.result, 5, "inserted correct key" + test); + } + + // Set up value to be inserted + let value = { theObj: true }; + if (keypath === true) { + value.id = 5; + } + else if (keypath === "invalid") { + value.id = /x/; + } + + // Which arguments are passed to function + args = [value]; + if (explicit === true) { + args.push(5); + } + else if (explicit === undefined) { + args.push(undefined); + } + else if (explicit === "invalid") { + args.push(/x/); + } + + let expected = expectedResult(method, keypath, explicit, autoincrement, existing); + + let valueJSON = JSON.stringify(value); + + ok(true, "making call" + test); + + // Make function call for throwing functions + if (expected === "throw") { + try { + store[method].apply(store, args); + ok(false, "should have thrown" + test); + } + catch (ex) { + ok(true, "did throw" + test); + ok(ex instanceof DOMException, "Got a DOMException" + test); + is(ex.name, "DataError", "expect a DataError" + test); + is(ex.code, 0, "expect zero" + test); + is(JSON.stringify(value), valueJSON, "call didn't modify value" + test); + } + continue; + } + + // Make non-throwing function call + let req = store[method].apply(store, args); + is(JSON.stringify(value), valueJSON, "call didn't modify value" + test); + + req.onsuccess = req.onerror = grabEventAndContinueHandler; + let e = yield undefined; + + // Figure out what key we used + let key = 5; + if (autoincrement && speccedNoKey) { + key = 1; + } + + // Adjust value if expected + if (autoincrement && keypath && speccedNoKey) { + value.id = key; + } + + // Check result + if (expected === "error") { + is(e.type, "error", "write should fail" + test); + e.preventDefault(); + e.stopPropagation(); + continue; + } + + is(e.type, "success", "write should succeed" + test); + is(e.target.result, key, "write should return correct key" + test); + + store.get(key).onsuccess = grabEventAndContinueHandler; + e = yield undefined; + is(e.type, "success", "read back should succeed" + test); + is(JSON.stringify(e.target.result), + JSON.stringify(value), + "read back should return correct value" + test); + } + } + } + } + } + + + function expectedResult(method, keypath, explicit, autoincrement, existing) { + if (keypath && explicit) + return "throw"; + if (!keypath && !explicit && !autoincrement) + return "throw"; + if (keypath == "invalid") + return "throw"; + if (keypath == "missing" && !autoincrement) + return "throw"; + if (explicit == "invalid") + return "throw"; + + if (method == "add" && existing) + return "error"; + + return "success"; + } + + openRequest.onsuccess = grabEventAndContinueHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_add_twice_failure.js b/dom/indexedDB/test/unit/test_add_twice_failure.js new file mode 100644 index 000000000..80a4e5c55 --- /dev/null +++ b/dom/indexedDB/test/unit/test_add_twice_failure.js @@ -0,0 +1,43 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = request.result; + + ok(event.target === request, "Good event target"); + + let objectStore = db.createObjectStore("foo", { keyPath: null }); + let key = 10; + + request = objectStore.add({}, key); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(request.result, key, "Correct key"); + + request = objectStore.add({}, key); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // Wait for success. + yield undefined; + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_advance.js b/dom/indexedDB/test/unit/test_advance.js new file mode 100644 index 000000000..3187a8f5e --- /dev/null +++ b/dom/indexedDB/test/unit/test_advance.js @@ -0,0 +1,192 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const dataCount = 30; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + event.target.onsuccess = continueToNextStep; + + let objectStore = db.createObjectStore("", { keyPath: "key" }); + objectStore.createIndex("", "index"); + + for (let i = 0; i < dataCount; i++) { + objectStore.add({ key: i, index: i }); + } + yield undefined; + + function getObjectStore() { + return db.transaction("").objectStore(""); + } + + function getIndex() { + return db.transaction("").objectStore("").index(""); + } + + let count = 0; + + getObjectStore().openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, dataCount, "Saw all data"); + + count = 0; + + getObjectStore().openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + if (count) { + count++; + cursor.continue(); + } + else { + count = 10; + cursor.advance(10); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, dataCount, "Saw all data"); + + count = 0; + + getIndex().openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + if (count) { + count++; + cursor.continue(); + } + else { + count = 10; + cursor.advance(10); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, dataCount, "Saw all data"); + + count = 0; + + getIndex().openKeyCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + if (count) { + count++; + cursor.continue(); + } + else { + count = 10; + cursor.advance(10); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, dataCount, "Saw all data"); + + count = 0; + + getObjectStore().openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + if (count == 0) { + cursor.advance(dataCount + 1); + } + else { + ok(false, "Should never get here!"); + cursor.continue(); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, 0, "Saw all data"); + + count = dataCount - 1; + + getObjectStore().openCursor(null, "prev").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + count--; + if (count == dataCount - 2) { + cursor.advance(10); + count -= 9; + } + else { + cursor.continue(); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, -1, "Saw all data"); + + count = dataCount - 1; + + getObjectStore().openCursor(null, "prev").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.primaryKey, count, "Got correct object"); + if (count == dataCount - 1) { + cursor.advance(dataCount + 1); + } + else { + ok(false, "Should never get here!"); + cursor.continue(); + } + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(count, dataCount - 1, "Saw all data"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_autoIncrement.js b/dom/indexedDB/test/unit/test_autoIncrement.js new file mode 100644 index 000000000..f2ea09822 --- /dev/null +++ b/dom/indexedDB/test/unit/test_autoIncrement.js @@ -0,0 +1,400 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "Need to implement a gc() function for worker tests"; + +if (!this.window) { + this.runTest = function() { + todo(false, "Test disabled in xpcshell test suite for now"); + finishTest(); + } +} + +var testGenerator = testSteps(); + +function genCheck(key, value, test, options) { + return function(event) { + is(JSON.stringify(event.target.result), JSON.stringify(key), + "correct returned key in " + test); + if (options && options.store) { + is(event.target.source, options.store, "correct store in " + test); + } + if (options && options.trans) { + is(event.target.transaction, options.trans, "correct transaction in " + test); + } + + event.target.source.get(key).onsuccess = function(event) { + is(JSON.stringify(event.target.result), JSON.stringify(value), + "correct stored value in " + test); + continueToNextStepSync(); + } + } +} + +function testSteps() +{ + const dbname = this.window ? window.location.pathname : "Splendid Test"; + const RW = "readwrite"; + let c1 = 1; + let c2 = 1; + + let openRequest = indexedDB.open(dbname, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + let trans = event.target.transaction; + + // Create test stores + let store1 = db.createObjectStore("store1", { autoIncrement: true }); + let store2 = db.createObjectStore("store2", { autoIncrement: true, keyPath: "id" }); + let store3 = db.createObjectStore("store3", { autoIncrement: false }); + is(store1.autoIncrement, true, "store1 .autoIncrement"); + is(store2.autoIncrement, true, "store2 .autoIncrement"); + is(store3.autoIncrement, false, "store3 .autoIncrement"); + + store1.createIndex("unique1", "unique", { unique: true }); + store2.createIndex("unique1", "unique", { unique: true }); + + // Test simple inserts + let test = " for test simple insert" + store1.add({ foo: "value1" }).onsuccess = + genCheck(c1++, { foo: "value1" }, "first" + test); + store1.add({ foo: "value2" }).onsuccess = + genCheck(c1++, { foo: "value2" }, "second" + test); + + yield undefined; + yield undefined; + + store2.put({ bar: "value1" }).onsuccess = + genCheck(c2, { bar: "value1", id: c2 }, "first in store2" + test, + { store: store2 }); + c2++; + store1.put({ foo: "value3" }).onsuccess = + genCheck(c1++, { foo: "value3" }, "third" + test, + { store: store1 }); + + yield undefined; + yield undefined; + + store2.get(IDBKeyRange.lowerBound(c2)).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + is(event.target.result, undefined, "no such value" + test); + + // Close version_change transaction + openRequest.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target, openRequest, "succeeded to open" + test); + is(event.type, "success", "succeeded to open" + test); + + // Test inserting explicit keys + test = " for test explicit keys"; + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 1 }, 100).onsuccess = + genCheck(100, { explicit: 1 }, "first" + test); + c1 = 101; + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 2 }).onsuccess = + genCheck(c1++, { explicit: 2 }, "second" + test); + yield undefined; yield undefined; + + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 3 }, 200).onsuccess = + genCheck(200, { explicit: 3 }, "third" + test); + c1 = 201; + trans.objectStore("store1").add({ explicit: 4 }).onsuccess = + genCheck(c1++, { explicit: 4 }, "fourth" + test); + yield undefined; yield undefined; + + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 5 }, 150).onsuccess = + genCheck(150, { explicit: 5 }, "fifth" + test); + yield undefined; + trans.objectStore("store1").add({ explicit: 6 }).onsuccess = + genCheck(c1++, { explicit: 6 }, "sixth" + test); + yield undefined; + + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 7 }, "key").onsuccess = + genCheck("key", { explicit: 7 }, "seventh" + test); + yield undefined; + trans.objectStore("store1").add({ explicit: 8 }).onsuccess = + genCheck(c1++, { explicit: 8 }, "eighth" + test); + yield undefined; + + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 7 }, [100000]).onsuccess = + genCheck([100000], { explicit: 7 }, "seventh" + test); + yield undefined; + trans.objectStore("store1").add({ explicit: 8 }).onsuccess = + genCheck(c1++, { explicit: 8 }, "eighth" + test); + yield undefined; + + trans = db.transaction("store1", RW); + trans.objectStore("store1").add({ explicit: 9 }, -100000).onsuccess = + genCheck(-100000, { explicit: 9 }, "ninth" + test); + yield undefined; + trans.objectStore("store1").add({ explicit: 10 }).onsuccess = + genCheck(c1++, { explicit: 10 }, "tenth" + test); + yield undefined; + + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit2: 1, id: 300 }).onsuccess = + genCheck(300, { explicit2: 1, id: 300 }, "first store2" + test); + c2 = 301; + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit2: 2 }).onsuccess = + genCheck(c2, { explicit2: 2, id: c2 }, "second store2" + test); + c2++; + yield undefined; yield undefined; + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit2: 3, id: 400 }).onsuccess = + genCheck(400, { explicit2: 3, id: 400 }, "third store2" + test); + c2 = 401; + trans.objectStore("store2").add({ explicit2: 4 }).onsuccess = + genCheck(c2, { explicit2: 4, id: c2 }, "fourth store2" + test); + c2++; + yield undefined; yield undefined; + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit: 5, id: 150 }).onsuccess = + genCheck(150, { explicit: 5, id: 150 }, "fifth store2" + test); + yield undefined; + trans.objectStore("store2").add({ explicit: 6 }).onsuccess = + genCheck(c2, { explicit: 6, id: c2 }, "sixth store2" + test); + c2++; + yield undefined; + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit: 7, id: "key" }).onsuccess = + genCheck("key", { explicit: 7, id: "key" }, "seventh store2" + test); + yield undefined; + trans.objectStore("store2").add({ explicit: 8 }).onsuccess = + genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test); + c2++; + yield undefined; + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit: 7, id: [100000] }).onsuccess = + genCheck([100000], { explicit: 7, id: [100000] }, "seventh store2" + test); + yield undefined; + trans.objectStore("store2").add({ explicit: 8 }).onsuccess = + genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test); + c2++; + yield undefined; + + trans = db.transaction("store2", RW); + trans.objectStore("store2").add({ explicit: 9, id: -100000 }).onsuccess = + genCheck(-100000, { explicit: 9, id: -100000 }, "ninth store2" + test); + yield undefined; + trans.objectStore("store2").add({ explicit: 10 }).onsuccess = + genCheck(c2, { explicit: 10, id: c2 }, "tenth store2" + test); + c2++; + yield undefined; + + + // Test separate transactions doesn't generate overlapping numbers + test = " for test non-overlapping counts"; + trans = db.transaction("store1", RW); + trans2 = db.transaction("store1", RW); + trans2.objectStore("store1").put({ over: 2 }).onsuccess = + genCheck(c1 + 1, { over: 2 }, "first" + test, + { trans: trans2 }); + trans.objectStore("store1").put({ over: 1 }).onsuccess = + genCheck(c1, { over: 1 }, "second" + test, + { trans: trans }); + c1 += 2; + yield undefined; yield undefined; + + trans = db.transaction("store2", RW); + trans2 = db.transaction("store2", RW); + trans2.objectStore("store2").put({ over: 2 }).onsuccess = + genCheck(c2 + 1, { over: 2, id: c2 + 1 }, "third" + test, + { trans: trans2 }); + trans.objectStore("store2").put({ over: 1 }).onsuccess = + genCheck(c2, { over: 1, id: c2 }, "fourth" + test, + { trans: trans }); + c2 += 2; + yield undefined; yield undefined; + + // Test that error inserts doesn't increase generator + test = " for test error inserts"; + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ unique: 1 }, -1); + trans.objectStore("store2").add({ unique: 1, id: "unique" }); + + trans.objectStore("store1").add({ error: 1, unique: 1 }). + addEventListener("error", new ExpectError("ConstraintError", true)); + trans.objectStore("store1").add({ error: 2 }).onsuccess = + genCheck(c1++, { error: 2 }, "first" + test); + yield undefined; yield undefined; + + trans.objectStore("store2").add({ error: 3, unique: 1 }). + addEventListener("error", new ExpectError("ConstraintError", true)); + trans.objectStore("store2").add({ error: 4 }).onsuccess = + genCheck(c2, { error: 4, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + trans.objectStore("store1").add({ error: 5, unique: 1 }, 100000). + addEventListener("error", new ExpectError("ConstraintError", true)); + trans.objectStore("store1").add({ error: 6 }).onsuccess = + genCheck(c1++, { error: 6 }, "third" + test); + yield undefined; yield undefined; + + trans.objectStore("store2").add({ error: 7, unique: 1, id: 100000 }). + addEventListener("error", new ExpectError("ConstraintError", true)); + trans.objectStore("store2").add({ error: 8 }).onsuccess = + genCheck(c2, { error: 8, id: c2 }, "fourth" + test); + c2++; + yield undefined; yield undefined; + + // Test that aborts doesn't increase generator + test = " for test aborted transaction"; + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ abort: 1 }).onsuccess = + genCheck(c1, { abort: 1 }, "first" + test); + trans.objectStore("store2").put({ abort: 2 }).onsuccess = + genCheck(c2, { abort: 2, id: c2 }, "second" + test); + yield undefined; yield undefined; + + trans.objectStore("store1").add({ abort: 3 }, 500).onsuccess = + genCheck(500, { abort: 3 }, "third" + test); + trans.objectStore("store2").put({ abort: 4, id: 600 }).onsuccess = + genCheck(600, { abort: 4, id: 600 }, "fourth" + test); + yield undefined; yield undefined; + + trans.objectStore("store1").add({ abort: 5 }).onsuccess = + genCheck(501, { abort: 5 }, "fifth" + test); + trans.objectStore("store2").put({ abort: 6 }).onsuccess = + genCheck(601, { abort: 6, id: 601 }, "sixth" + test); + yield undefined; yield undefined; + + trans.abort(); + trans.onabort = grabEventAndContinueHandler; + event = yield + is(event.type, "abort", "transaction aborted"); + is(event.target, trans, "correct transaction aborted"); + + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ abort: 1 }).onsuccess = + genCheck(c1++, { abort: 1 }, "re-first" + test); + trans.objectStore("store2").put({ abort: 2 }).onsuccess = + genCheck(c2, { abort: 2, id: c2 }, "re-second" + test); + c2++; + yield undefined; yield undefined; + + // Test that delete doesn't decrease generator + test = " for test delete items" + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ delete: 1 }).onsuccess = + genCheck(c1++, { delete: 1 }, "first" + test); + trans.objectStore("store2").put({ delete: 2 }).onsuccess = + genCheck(c2, { delete: 2, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + trans.objectStore("store1").delete(c1 - 1).onsuccess = + grabEventAndContinueHandler; + trans.objectStore("store2").delete(c2 - 1).onsuccess = + grabEventAndContinueHandler; + yield undefined; yield undefined; + + trans.objectStore("store1").add({ delete: 3 }).onsuccess = + genCheck(c1++, { delete: 3 }, "first" + test); + trans.objectStore("store2").put({ delete: 4 }).onsuccess = + genCheck(c2, { delete: 4, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + trans.objectStore("store1").delete(c1 - 1).onsuccess = + grabEventAndContinueHandler; + trans.objectStore("store2").delete(c2 - 1).onsuccess = + grabEventAndContinueHandler; + yield undefined; yield undefined; + + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ delete: 5 }).onsuccess = + genCheck(c1++, { delete: 5 }, "first" + test); + trans.objectStore("store2").put({ delete: 6 }).onsuccess = + genCheck(c2, { delete: 6, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + // Test that clears doesn't decrease generator + test = " for test clear stores"; + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ clear: 1 }).onsuccess = + genCheck(c1++, { clear: 1 }, "first" + test); + trans.objectStore("store2").put({ clear: 2 }).onsuccess = + genCheck(c2, { clear: 2, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + trans.objectStore("store1").clear().onsuccess = + grabEventAndContinueHandler; + trans.objectStore("store2").clear().onsuccess = + grabEventAndContinueHandler; + yield undefined; yield undefined; + + trans.objectStore("store1").add({ clear: 3 }).onsuccess = + genCheck(c1++, { clear: 3 }, "third" + test); + trans.objectStore("store2").put({ clear: 4 }).onsuccess = + genCheck(c2, { clear: 4, id: c2 }, "forth" + test); + c2++; + yield undefined; yield undefined; + + trans.objectStore("store1").clear().onsuccess = + grabEventAndContinueHandler; + trans.objectStore("store2").clear().onsuccess = + grabEventAndContinueHandler; + yield undefined; yield undefined; + + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").add({ clear: 5 }).onsuccess = + genCheck(c1++, { clear: 5 }, "fifth" + test); + trans.objectStore("store2").put({ clear: 6 }).onsuccess = + genCheck(c2, { clear: 6, id: c2 }, "sixth" + test); + c2++; + yield undefined; yield undefined; + + + // Test that close/reopen doesn't decrease generator + test = " for test clear stores"; + trans = db.transaction(["store1", "store2"], RW); + trans.objectStore("store1").clear().onsuccess = + grabEventAndContinueHandler; + trans.objectStore("store2").clear().onsuccess = + grabEventAndContinueHandler; + yield undefined; yield undefined; + db.close(); + + gc(); + + openRequest = indexedDB.open(dbname, 2); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + db = event.target.result; + trans = event.target.transaction; + + trans.objectStore("store1").add({ reopen: 1 }).onsuccess = + genCheck(c1++, { reopen: 1 }, "first" + test); + trans.objectStore("store2").put({ reopen: 2 }).onsuccess = + genCheck(c2, { reopen: 2, id: c2 }, "second" + test); + c2++; + yield undefined; yield undefined; + + openRequest.onsuccess = grabEventAndContinueHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_autoIncrement_indexes.js b/dom/indexedDB/test/unit/test_autoIncrement_indexes.js new file mode 100644 index 000000000..ce2d5d552 --- /dev/null +++ b/dom/indexedDB/test/unit/test_autoIncrement_indexes.js @@ -0,0 +1,56 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = request.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore("foo", { keyPath: "id", + autoIncrement: true }); + objectStore.createIndex("first","first"); + objectStore.createIndex("second","second"); + objectStore.createIndex("third","third"); + + let data = { first: "foo", second: "foo", third: "foo" }; + + objectStore.add(data).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, "Added entry"); + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + objectStore = db.transaction("foo").objectStore("foo"); + let first = objectStore.index("first"); + let second = objectStore.index("second"); + let third = objectStore.index("third"); + + first.get("foo").onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is (event.target.result.id, 1, "Entry in first"); + + second.get("foo").onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is (event.target.result.id, 1, "Entry in second"); + + third.get("foo").onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is (event.target.result.id, 1, "Entry in third"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_blob_file_backed.js b/dom/indexedDB/test/unit/test_blob_file_backed.js new file mode 100644 index 000000000..664c9e2c9 --- /dev/null +++ b/dom/indexedDB/test/unit/test_blob_file_backed.js @@ -0,0 +1,78 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "This test uses SpecialPowers"; + +var testGenerator = testSteps(); + +function testSteps() +{ + const fileData = "abcdefghijklmnopqrstuvwxyz"; + const fileType = "text/plain"; + + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + const objectStoreName = "foo"; + const objectStoreKey = "10"; + + info("Creating temp file"); + + SpecialPowers.createFiles([{data:fileData, options:{type:fileType}}], function (files) { + testGenerator.next(files[0]); + }); + + let file = yield undefined; + + ok(file instanceof File, "Got a File object"); + is(file.size, fileData.length, "Correct size"); + is(file.type, fileType, "Correct type"); + + let fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(file); + + let event = yield undefined; + + is(fileReader.result, fileData, "Correct data"); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + objectStore.put(file, objectStoreKey); + + event = yield undefined; + + db = event.target.result; + + file = null; + + objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); + objectStore.get(objectStoreKey).onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + file = event.target.result; + + ok(file instanceof File, "Got a File object"); + is(file.size, fileData.length, "Correct size"); + is(file.type, fileType, "Correct type"); + + fileReader = new FileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(file); + + event = yield undefined; + + is(fileReader.result, fileData, "Correct data"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_blocked_order.js b/dom/indexedDB/test/unit/test_blocked_order.js new file mode 100644 index 000000000..1c70853f4 --- /dev/null +++ b/dom/indexedDB/test/unit/test_blocked_order.js @@ -0,0 +1,179 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + const databaseCount = 10; + + // Test 1: Make sure basic versionchange events work and that they don't + // trigger blocked events. + info("Opening " + databaseCount + " databases with version 1"); + + let databases = []; + + for (let i = 0; i < databaseCount; i++) { + let thisIndex = i; + + info("Opening database " + thisIndex); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "success", "Got success event"); + + let db = request.result; + is(db.version, 1, "Got version 1"); + + db.onversionchange = function(event) { + info("Closing database " + thisIndex); + db.close(); + + databases.splice(databases.indexOf(db), 1); + }; + + databases.push(db); + } + + is(databases.length, databaseCount, "Created all databases with version 1"); + + info("Opening database with version 2"); + + let request = indexedDB.open(databaseName, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + request.onblocked = function(event) { + ok(false, "Should not receive a blocked event"); + }; + + let event = yield undefined; + + is(event.type, "success", "Got success event"); + is(databases.length, 0, "All databases with version 1 were closed"); + + let db = request.result; + is(db.version, 2, "Got version 2"); + + info("Deleting database with version 2"); + db.close(); + + request = indexedDB.deleteDatabase(databaseName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + // Test 2: Make sure blocked events aren't delivered until all versionchange + // events have been delivered. + info("Opening " + databaseCount + " databases with version 1"); + + for (let i = 0; i < databaseCount; i++) { + let thisIndex = i; + + info("Opening database " + thisIndex); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "success", "Got success event"); + + let db = request.result; + is(db.version, 1, "Got version 1"); + + db.onversionchange = function(event) { + if (thisIndex == (databaseCount - 1)) { + info("Closing all databases with version 1"); + + for (let j = 0; j < databases.length; j++) { + databases[j].close(); + } + + databases = []; + info("Done closing all databases with version 1"); + } else { + info("Not closing database " + thisIndex); + } + }; + + databases.push(db); + } + + is(databases.length, databaseCount, "Created all databases with version 1"); + + info("Opening database with version 2"); + + request = indexedDB.open(databaseName, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + request.onblocked = function(event) { + ok(false, "Should not receive a blocked event"); + }; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + is(databases.length, 0, "All databases with version 1 were closed"); + + db = request.result; + is(db.version, 2, "Got version 2"); + + info("Deleting database with version 2"); + db.close(); + + request = indexedDB.deleteDatabase(databaseName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + // Test 3: A blocked database left in that state should not hang shutdown. + info("Opening 1 database with version 1"); + + request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + db = request.result; + is(db.version, 1, "Got version 1"); + + info("Opening database with version 2"); + + request = indexedDB.open(databaseName, 2); + request.onerror = function(e) { + e.preventDefault(); + }; + request.onsuccess = errorHandler; + + request.onblocked = grabEventAndContinueHandler; + + event = yield undefined; + ok(true, "Got blocked"); + // Just allow this to remain blocked ... + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_bug1056939.js b/dom/indexedDB/test/unit/test_bug1056939.js new file mode 100644 index 000000000..49bccd9ff --- /dev/null +++ b/dom/indexedDB/test/unit/test_bug1056939.js @@ -0,0 +1,73 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName1 = "upgrade_test"; + const dbName2 = "testing.foobar"; + const dbName3 = "xxxxxxx.xxxxxx"; + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("bug1056939_profile"); + + let request = indexedDB.open(dbName1, 1); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + + request = indexedDB.open(dbName2, 1); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + request = indexedDB.open(dbName3, 1); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + request = indexedDB.open(dbName3, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + request = indexedDB.open(dbName3, 1); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_cleanup_transaction.js b/dom/indexedDB/test/unit/test_cleanup_transaction.js new file mode 100644 index 000000000..070e9015e --- /dev/null +++ b/dom/indexedDB/test/unit/test_cleanup_transaction.js @@ -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; +} diff --git a/dom/indexedDB/test/unit/test_clear.js b/dom/indexedDB/test/unit/test_clear.js new file mode 100644 index 000000000..acce5c261 --- /dev/null +++ b/dom/indexedDB/test/unit/test_clear.js @@ -0,0 +1,97 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const entryCount = 1000; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = request.result; + + event.target.onsuccess = continueToNextStep; + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + let firstKey; + for (let i = 0; i < entryCount; i++) { + request = objectStore.add({}); + request.onerror = errorHandler; + if (!i) { + request.onsuccess = function(event) { + firstKey = event.target.result; + }; + } + } + yield undefined; + + isnot(firstKey, undefined, "got first key"); + + let seenEntryCount = 0; + + request = db.transaction("foo").objectStore("foo").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + seenEntryCount++; + cursor.continue(); + } + else { + continueToNextStep(); + } + } + yield undefined; + + is(seenEntryCount, entryCount, "Correct entry count"); + + try { + db.transaction("foo").objectStore("foo").clear(); + ok(false, "clear should throw on READ_ONLY transactions"); + } + catch (e) { + ok(true, "clear should throw on READ_ONLY transactions"); + } + + request = db.transaction("foo", "readwriteflush") + .objectStore("foo") + .clear(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result === undefined, "Correct event.target.result"); + ok(request.result === undefined, "Correct request.result"); + ok(request === event.target, "Correct event.target"); + + request = db.transaction("foo").objectStore("foo").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function(event) { + let cursor = request.result; + if (cursor) { + ok(false, "Shouldn't have any entries"); + } + continueToNextStep(); + } + yield undefined; + + request = db.transaction("foo", "readwrite") + .objectStore("foo") + .add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + isnot(event.target.result, firstKey, "Got a different key"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_complex_keyPaths.js b/dom/indexedDB/test/unit/test_complex_keyPaths.js new file mode 100644 index 000000000..24375813d --- /dev/null +++ b/dom/indexedDB/test/unit/test_complex_keyPaths.js @@ -0,0 +1,266 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + // Test object stores + + const name = "test_complex_keyPaths"; + const keyPaths = [ + { keyPath: "id", value: { id: 5 }, key: 5 }, + { keyPath: "id", value: { id: "14", iid: 12 }, key: "14" }, + { keyPath: "id", value: { iid: "14", id: 12 }, key: 12 }, + { keyPath: "id", value: {} }, + { keyPath: "id", value: { id: {} } }, + { keyPath: "id", value: { id: /x/ } }, + { keyPath: "id", value: 2 }, + { keyPath: "id", value: undefined }, + { keyPath: "foo.id", value: { foo: { id: 7 } }, key: 7 }, + { keyPath: "foo.id", value: { id: 7, foo: { id: "asdf" } }, key: "asdf" }, + { keyPath: "foo.id", value: { foo: { id: undefined } } }, + { keyPath: "foo.id", value: { foo: 47 } }, + { keyPath: "foo.id", value: {} }, + { keyPath: "", value: "foopy", key: "foopy" }, + { keyPath: "", value: 2, key: 2 }, + { keyPath: "", value: undefined }, + { keyPath: "", value: { id: 12 } }, + { keyPath: "", value: /x/ }, + { keyPath: "foo.bar", value: { baz: 1, foo: { baz2: 2, bar: "xo" } }, key: "xo" }, + { keyPath: "foo.bar.baz", value: { foo: { bar: { bazz: 16, baz: 17 } } }, key: 17 }, + { keyPath: "foo..id", exception: true }, + { keyPath: "foo.", exception: true }, + { keyPath: "fo o", exception: true }, + { keyPath: "foo ", exception: true }, + { keyPath: "foo[bar]",exception: true }, + { keyPath: "foo[1]", exception: true }, + { keyPath: "$('id').stuff", exception: true }, + { keyPath: "foo.2.bar", exception: true }, + { keyPath: "foo. .bar", exception: true }, + { keyPath: ".bar", exception: true }, + { keyPath: [], exception: true }, + + { keyPath: ["foo", "bar"], value: { foo: 1, bar: 2 }, key: [1, 2] }, + { keyPath: ["foo"], value: { foo: 1, bar: 2 }, key: [1] }, + { keyPath: ["foo", "bar", "bar"], value: { foo: 1, bar: "x" }, key: [1, "x", "x"] }, + { keyPath: ["x", "y"], value: { x: [], y: "x" }, key: [[], "x"] }, + { keyPath: ["x", "y"], value: { x: [[1]], y: "x" }, key: [[[1]], "x"] }, + { keyPath: ["x", "y"], value: { x: [[1]], y: new Date(1) }, key: [[[1]], new Date(1)] }, + { keyPath: ["x", "y"], value: { x: [[1]], y: [new Date(3)] }, key: [[[1]], [new Date(3)]] }, + { keyPath: ["x", "y.bar"], value: { x: "hi", y: { bar: "x"} }, key: ["hi", "x"] }, + { keyPath: ["x.y", "y.bar"], value: { x: { y: "hello" }, y: { bar: "nurse"} }, key: ["hello", "nurse"] }, + { keyPath: ["", ""], value: 5, key: [5, 5] }, + { keyPath: ["x", "y"], value: { x: 1 } }, + { keyPath: ["x", "y"], value: { y: 1 } }, + { keyPath: ["x", "y"], value: { x: 1, y: undefined } }, + { keyPath: ["x", "y"], value: { x: null, y: 1 } }, + { keyPath: ["x", "y.bar"], value: { x: null, y: { bar: "x"} } }, + { keyPath: ["x", "y"], value: { x: 1, y: false } }, + { keyPath: ["x", "y", "z"], value: { x: 1, y: false, z: "a" } }, + { keyPath: [".x", "y", "z"], exception: true }, + { keyPath: ["x", "y ", "z"], exception: true }, + ]; + + let openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + + let stores = {}; + + // Test creating object stores and inserting data + for (let i = 0; i < keyPaths.length; i++) { + let info = keyPaths[i]; + + let test = " for objectStore test " + JSON.stringify(info); + let indexName = JSON.stringify(info.keyPath); + if (!stores[indexName]) { + try { + let objectStore = db.createObjectStore(indexName, { keyPath: info.keyPath }); + ok(!("exception" in info), "shouldn't throw" + test); + is(JSON.stringify(objectStore.keyPath), JSON.stringify(info.keyPath), + "correct keyPath property" + test); + ok(objectStore.keyPath === objectStore.keyPath, + "object identity should be preserved"); + stores[indexName] = objectStore; + } catch (e) { + ok("exception" in info, "should throw" + test); + is(e.name, "SyntaxError", "expect a SyntaxError" + test); + ok(e instanceof DOMException, "Got a DOM Exception" + test); + is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test); + continue; + } + } + + let store = stores[indexName]; + + try { + request = store.add(info.value); + ok("key" in info, "successfully created request to insert value" + test); + } catch (e) { + ok(!("key" in info), "threw when attempted to insert" + test); + ok(e instanceof DOMException, "Got a DOMException" + test); + is(e.name, "DataError", "expect a DataError" + test); + is(e.code, 0, "expect zero" + test); + continue; + } + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let e = yield undefined; + is(e.type, "success", "inserted successfully" + test); + is(e.target, request, "expected target" + test); + ok(compareKeys(request.result, info.key), "found correct key" + test); + is(indexedDB.cmp(request.result, info.key), 0, "returned key compares correctly" + test); + + store.get(info.key).onsuccess = grabEventAndContinueHandler; + e = yield undefined; + isnot(e.target.result, undefined, "Did find entry"); + + // Check that cursor.update work as expected + request = store.openCursor(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + e = yield undefined; + let cursor = e.target.result; + request = cursor.update(info.value); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + yield undefined; + ok(true, "Successfully updated cursor" + test); + + // Check that cursor.update throws as expected when key is changed + let newValue = cursor.value; + let destProp = Array.isArray(info.keyPath) ? info.keyPath[0] : info.keyPath; + if (destProp) { + eval("newValue." + destProp + " = 'newKeyValue'"); + } + else { + newValue = 'newKeyValue'; + } + let didThrow; + try { + cursor.update(newValue); + } + catch (ex) { + didThrow = ex; + } + ok(didThrow instanceof DOMException, "Got a DOMException" + test); + is(didThrow.name, "DataError", "expect a DataError" + test); + is(didThrow.code, 0, "expect zero" + test); + + // Clear object store to prepare for next test + store.clear().onsuccess = grabEventAndContinueHandler; + yield undefined; + } + + // Attempt to create indexes and insert data + let store = db.createObjectStore("indexStore"); + let indexes = {}; + for (let i = 0; i < keyPaths.length; i++) { + let info = keyPaths[i]; + let test = " for index test " + JSON.stringify(info); + let indexName = JSON.stringify(info.keyPath); + if (!indexes[indexName]) { + try { + let index = store.createIndex(indexName, info.keyPath); + ok(!("exception" in info), "shouldn't throw" + test); + is(JSON.stringify(index.keyPath), JSON.stringify(info.keyPath), + "index has correct keyPath property" + test); + ok(index.keyPath === index.keyPath, + "object identity should be preserved"); + indexes[indexName] = index; + } catch (e) { + ok("exception" in info, "should throw" + test); + is(e.name, "SyntaxError", "expect a SyntaxError" + test); + ok(e instanceof DOMException, "Got a DOM Exception" + test); + is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test); + continue; + } + } + + let index = indexes[indexName]; + + request = store.add(info.value, 1); + if ("key" in info) { + index.getKey(info.key).onsuccess = grabEventAndContinueHandler; + e = yield undefined; + is(e.target.result, 1, "found value when reading" + test); + } + else { + index.count().onsuccess = grabEventAndContinueHandler; + e = yield undefined; + is(e.target.result, 0, "should be empty" + test); + } + + store.clear().onsuccess = grabEventAndContinueHandler; + yield undefined; + } + + // Autoincrement and complex key paths + let aitests = [{ v: {}, k: 1, res: { foo: { id: 1 }} }, + { v: { value: "x" }, k: 2, res: { value: "x", foo: { id: 2 }} }, + { v: { value: "x", foo: {} }, k: 3, res: { value: "x", foo: { id: 3 }} }, + { v: { v: "x", foo: { x: "y" } }, k: 4, res: { v: "x", foo: { x: "y", id: 4 }} }, + { v: { value: 2, foo: { id: 10 }}, k: 10 }, + { v: { value: 2 }, k: 11, res: { value: 2, foo: { id: 11 }} }, + { v: true, }, + { v: { value: 2, foo: 12 }, }, + { v: { foo: { id: true }}, }, + { v: { foo: { x: 5, id: {} }}, }, + { v: undefined, }, + { v: { foo: undefined }, }, + { v: { foo: { id: undefined }}, }, + { v: null, }, + { v: { foo: null }, }, + { v: { foo: { id: null }}, }, + ]; + + store = db.createObjectStore("gen", { keyPath: "foo.id", autoIncrement: true }); + for (let i = 0; i < aitests.length; ++i) { + let info = aitests[i]; + let test = " for autoIncrement test " + JSON.stringify(info); + + let preValue = JSON.stringify(info.v); + if ("k" in info) { + store.add(info.v).onsuccess = grabEventAndContinueHandler; + is(JSON.stringify(info.v), preValue, "put didn't modify value" + test); + } + else { + try { + store.add(info.v); + ok(false, "should throw" + test); + } + catch(e) { + ok(true, "did throw" + test); + ok(e instanceof DOMException, "Got a DOMException" + test); + is(e.name, "DataError", "expect a DataError" + test); + is(e.code, 0, "expect zero" + test); + + is(JSON.stringify(info.v), preValue, "failing put didn't modify value" + test); + + continue; + } + } + + let e = yield undefined; + is(e.target.result, info.k, "got correct return key" + test); + + store.get(info.k).onsuccess = grabEventAndContinueHandler; + e = yield undefined; + is(JSON.stringify(e.target.result), JSON.stringify(info.res || info.v), + "expected value stored" + test); + } + + openRequest.onsuccess = grabEventAndContinueHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_count.js b/dom/indexedDB/test/unit/test_count.js new file mode 100644 index 000000000..32ba5e950 --- /dev/null +++ b/dom/indexedDB/test/unit/test_count.js @@ -0,0 +1,354 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7738", value: { name: "Mel", height: 66, weight: {} } }, + { key: "237-23-7739", value: { name: "Tom", height: 62, weight: 130 } } + ]; + + const indexData = { + name: "weight", + keyPath: "weight", + options: { unique: false } + }; + + const weightSort = [1, 0, 3, 7, 4, 2]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + objectStore.createIndex(indexData.name, indexData.keyPath, + indexData.options); + + for (let data of objectStoreData) { + objectStore.add(data.value, data.key); + } + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + objectStore = db.transaction(db.objectStoreNames) + .objectStore(objectStoreName); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length, + "Correct number of object store entries for all keys"); + + objectStore.count(null).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length, + "Correct number of object store entries for null key"); + + objectStore.count(objectStoreData[2].key).onsuccess = + grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of object store entries for single existing key"); + + objectStore.count("foo").onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of object store entries for single non-existing key"); + + let keyRange = IDBKeyRange.only(objectStoreData[2].key); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of object store entries for existing only keyRange"); + + keyRange = IDBKeyRange.only("foo"); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of object store entries for non-existing only keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[2].key); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length - 2, + "Correct number of object store entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[2].key, true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length - 3, + "Correct number of object store entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound("foo"); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of object store entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[2].key, false); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 3, + "Correct number of object store entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[2].key, true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 2, + "Correct number of object store entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound("foo", true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length, + "Correct number of object store entries for upperBound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[0].key, + objectStoreData[objectStoreData.length - 1].key); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length, + "Correct number of object store entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[0].key, + objectStoreData[objectStoreData.length - 1].key, + true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length - 1, + "Correct number of object store entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[0].key, + objectStoreData[objectStoreData.length - 1].key, + true, true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length - 2, + "Correct number of object store entries for bound keyRange"); + + keyRange = IDBKeyRange.bound("foo", "foopy", true, true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of object store entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[0].key, "foo", true, true); + objectStore.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreData.length - 1, + "Correct number of object store entries for bound keyRange"); + + let index = objectStore.index(indexData.name); + + index.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for no key"); + + index.count(objectStoreData[7].value.weight).onsuccess = + grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 2, + "Correct number of index entries for duplicate key"); + + index.count(objectStoreData[0].value.weight).onsuccess = + grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of index entries for single key"); + + keyRange = IDBKeyRange.only(objectStoreData[0].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of index entries for only existing keyRange"); + + keyRange = IDBKeyRange.only("foo"); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of index entries for only non-existing keyRange"); + + keyRange = IDBKeyRange.only(objectStoreData[7].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 2, + "Correct number of index entries for only duplicate keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[0]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[1]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 1, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[0]].value.weight - 1); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[0]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 1, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.lowerBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight + 1, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of index entries for lowerBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[weightSort[0]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 1, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[weightSort[0]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 1, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound(objectStoreData[weightSort[weightSort.length - 1]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 1, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.upperBound("foo"); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for upperBound keyRange"); + + keyRange = IDBKeyRange.bound("foo", "foopy"); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, + "Correct number of index entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[weightSort[0]].value.weight, + objectStoreData[weightSort[weightSort.length - 1]].value.weight); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length, + "Correct number of index entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[weightSort[0]].value.weight, + objectStoreData[weightSort[weightSort.length - 1]].value.weight, + true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 1, + "Correct number of index entries for bound keyRange"); + + keyRange = IDBKeyRange.bound(objectStoreData[weightSort[0]].value.weight, + objectStoreData[weightSort[weightSort.length - 1]].value.weight, + true, true); + index.count(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, weightSort.length - 2, + "Correct number of index entries for bound keyRange"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_create_index.js b/dom/indexedDB/test/unit/test_create_index.js new file mode 100644 index 000000000..284cca2b3 --- /dev/null +++ b/dom/indexedDB/test/unit/test_create_index.js @@ -0,0 +1,121 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreInfo = [ + { name: "a", options: { keyPath: "id", autoIncrement: true } }, + { name: "b", options: { keyPath: "id", autoIncrement: false } }, + ]; + const indexInfo = [ + { name: "1", keyPath: "unique_value", options: { unique: true } }, + { name: "2", keyPath: "value", options: { unique: false } }, + { name: "3", keyPath: "value", options: { unique: false } }, + { name: "", keyPath: "value", options: { unique: false } }, + { name: null, keyPath: "value", options: { unique: false } }, + { name: undefined, keyPath: "value", options: { unique: false } }, + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + let objectStore = info.hasOwnProperty("options") ? + db.createObjectStore(info.name, info.options) : + db.createObjectStore(info.name); + + try { + request = objectStore.createIndex("Hola"); + ok(false, "createIndex with no keyPath should throw"); + } + catch(e) { + ok(true, "createIndex with no keyPath should throw"); + } + + let ex; + try { + objectStore.createIndex("Hola", ["foo"], { multiEntry: true }); + } + catch(e) { + ex = e; + } + ok(ex, "createIndex with array keyPath and multiEntry should throw"); + is(ex.name, "InvalidAccessError", "should throw right exception"); + ok(ex instanceof DOMException, "should throw right exception"); + is(ex.code, DOMException.INVALID_ACCESS_ERR, "should throw right exception"); + + try { + objectStore.createIndex("foo", "bar", 10); + ok(false, "createIndex with bad options should throw"); + } + catch(e) { + ok(true, "createIndex with bad options threw"); + } + + ok(objectStore.createIndex("foo", "bar", { foo: "" }), + "createIndex with unknown options should not throw"); + objectStore.deleteIndex("foo"); + + // Test index creation, and that it ends up in indexNames. + let objectStoreName = info.name; + for (let j = 0; j < indexInfo.length; j++) { + let info = indexInfo[j]; + let count = objectStore.indexNames.length; + let index = info.hasOwnProperty("options") ? + objectStore.createIndex(info.name, info.keyPath, + info.options) : + objectStore.createIndex(info.name, info.keyPath); + + let name = info.name; + if (name === null) { + name = "null"; + } + else if (name === undefined) { + name = "undefined"; + } + + is(index.name, name, "correct name"); + is(index.keyPath, info.keyPath, "correct keyPath"); + is(index.unique, info.options.unique, "correct uniqueness"); + + is(objectStore.indexNames.length, count + 1, + "indexNames grew in size"); + let found = false; + for (let k = 0; k < objectStore.indexNames.length; k++) { + if (objectStore.indexNames.item(k) == name) { + found = true; + break; + } + } + ok(found, "Name is on objectStore.indexNames"); + + ok(event.target.transaction, "event has a transaction"); + ok(event.target.transaction.db === db, + "transaction has the right db"); + is(event.target.transaction.mode, "versionchange", + "transaction has the correct mode"); + is(event.target.transaction.objectStoreNames.length, i + 1, + "transaction only has one object store"); + ok(event.target.transaction.objectStoreNames.contains(objectStoreName), + "transaction has the correct object store"); + } + } + + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_create_index_with_integer_keys.js b/dom/indexedDB/test/unit/test_create_index_with_integer_keys.js new file mode 100644 index 000000000..d14b50411 --- /dev/null +++ b/dom/indexedDB/test/unit/test_create_index_with_integer_keys.js @@ -0,0 +1,66 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const data = { id: new Date().getTime(), + num: parseInt(Math.random() * 1000) }; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + event.target.onsuccess = continueToNextStep; + + // Make object store, add data. + let objectStore = db.createObjectStore("foo", { keyPath: "id" }); + objectStore.add(data); + yield undefined; + db.close(); + + request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + event = yield undefined; + + let db2 = event.target.result; + db2.onerror = errorHandler; + + event.target.onsuccess = continueToNextStep; + + // Create index. + event.target.transaction.objectStore("foo").createIndex("foo", "num"); + yield undefined; + + // Make sure our object made it into the index. + let seenCount = 0; + + + db2.transaction("foo").objectStore("foo").index("foo") + .openKeyCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, data.num, "Good key"); + is(cursor.primaryKey, data.id, "Good value"); + seenCount++; + cursor.continue(); + } + else { + continueToNextStep(); + } + }; + yield undefined; + + is(seenCount, 1, "Saw our entry"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_create_locale_aware_index.js b/dom/indexedDB/test/unit/test_create_locale_aware_index.js new file mode 100644 index 000000000..7fc7a4ab5 --- /dev/null +++ b/dom/indexedDB/test/unit/test_create_locale_aware_index.js @@ -0,0 +1,123 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreInfo = [ + { name: "a", options: { keyPath: "id", autoIncrement: true } }, + { name: "b", options: { keyPath: "id", autoIncrement: false } }, + ]; + const indexInfo = [ + { name: "1", keyPath: "unique_value", options: { unique: true, locale: "es-ES" } }, + { name: "2", keyPath: "unique_value", options: { unique: true, locale: null } }, + { name: "3", keyPath: "value", options: { unique: false, locale: "es-ES" } }, + { name: "4", keyPath: "value", options: { unique: false, locale: "es-ES" } }, + { name: "", keyPath: "value", options: { unique: false, locale: "es-ES" } }, + { name: null, keyPath: "value", options: { unique: false, locale: "es-ES" } }, + { name: undefined, keyPath: "value", options: { unique: false, locale: "es-ES" } }, + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + let objectStore = info.hasOwnProperty("options") ? + db.createObjectStore(info.name, info.options) : + db.createObjectStore(info.name); + + try { + request = objectStore.createIndex("Hola"); + ok(false, "createIndex with no keyPath should throw"); + } + catch(e) { + ok(true, "createIndex with no keyPath should throw"); + } + + let ex; + try { + objectStore.createIndex("Hola", ["foo"], { multiEntry: true }); + } + catch(e) { + ex = e; + } + ok(ex, "createIndex with array keyPath and multiEntry should throw"); + is(ex.name, "InvalidAccessError", "should throw right exception"); + ok(ex instanceof DOMException, "should throw right exception"); + is(ex.code, DOMException.INVALID_ACCESS_ERR, "should throw right exception"); + + try { + objectStore.createIndex("foo", "bar", 10); + ok(false, "createIndex with bad options should throw"); + } + catch(e) { + ok(true, "createIndex with bad options threw"); + } + + ok(objectStore.createIndex("foo", "bar", { foo: "" }), + "createIndex with unknown options should not throw"); + objectStore.deleteIndex("foo"); + + // Test index creation, and that it ends up in indexNames. + let objectStoreName = info.name; + for (let j = 0; j < indexInfo.length; j++) { + let info = indexInfo[j]; + let count = objectStore.indexNames.length; + let index = info.hasOwnProperty("options") ? + objectStore.createIndex(info.name, info.keyPath, + info.options) : + objectStore.createIndex(info.name, info.keyPath); + + let name = info.name; + if (name === null) { + name = "null"; + } + else if (name === undefined) { + name = "undefined"; + } + + is(index.name, name, "correct name"); + is(index.keyPath, info.keyPath, "correct keyPath"); + is(index.unique, info.options.unique, "correct uniqueness"); + is(index.locale, info.options.locale, "correct locale"); + + is(objectStore.indexNames.length, count + 1, + "indexNames grew in size"); + let found = false; + for (let k = 0; k < objectStore.indexNames.length; k++) { + if (objectStore.indexNames.item(k) == name) { + found = true; + break; + } + } + ok(found, "Name is on objectStore.indexNames"); + + ok(event.target.transaction, "event has a transaction"); + ok(event.target.transaction.db === db, + "transaction has the right db"); + is(event.target.transaction.mode, "versionchange", + "transaction has the correct mode"); + is(event.target.transaction.objectStoreNames.length, i + 1, + "transaction only has one object store"); + ok(event.target.transaction.objectStoreNames.contains(objectStoreName), + "transaction has the correct object store"); + } + } + + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_create_objectStore.js b/dom/indexedDB/test/unit/test_create_objectStore.js new file mode 100644 index 000000000..215723c26 --- /dev/null +++ b/dom/indexedDB/test/unit/test_create_objectStore.js @@ -0,0 +1,134 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreInfo = [ + { name: "1", options: { keyPath: null } }, + { name: "2", options: { keyPath: null, autoIncrement: true } }, + { name: "3", options: { keyPath: null, autoIncrement: false } }, + { name: "4", options: { keyPath: null } }, + { name: "5", options: { keyPath: "foo" } }, + { name: "6" }, + { name: "7", options: null }, + { name: "8", options: { autoIncrement: true } }, + { name: "9", options: { autoIncrement: false } }, + { name: "10", options: { keyPath: "foo", autoIncrement: false } }, + { name: "11", options: { keyPath: "foo", autoIncrement: true } }, + { name: "" }, + { name: null }, + { name: undefined } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + + let count = db.objectStoreNames.length; + is(count, 0, "correct objectStoreNames length"); + + try { + db.createObjectStore("foo", "bar"); + ok(false, "createObjectStore with bad options should throw"); + } + catch(e) { + ok(true, "createObjectStore with bad options"); + } + + ok(db.createObjectStore("foo", { foo: "" }), + "createObjectStore with unknown options should not throw"); + db.deleteObjectStore("foo"); + + for (let index in objectStoreInfo) { + index = parseInt(index); + const info = objectStoreInfo[index]; + + let objectStore = info.hasOwnProperty("options") ? + db.createObjectStore(info.name, info.options) : + db.createObjectStore(info.name); + + is(db.objectStoreNames.length, index + 1, + "updated objectStoreNames list"); + + let name = info.name; + if (name === null) { + name = "null"; + } + else if (name === undefined) { + name = "undefined"; + } + + let found = false; + for (let i = 0; i <= index; i++) { + if (db.objectStoreNames.item(i) == name) { + found = true; + break; + } + } + is(found, true, "objectStoreNames contains name"); + + is(objectStore.name, name, "Bad name"); + is(objectStore.keyPath, info.options && info.options.keyPath ? + info.options.keyPath : null, + "Bad keyPath"); + if(objectStore.indexNames.length, 0, "Bad indexNames"); + + ok(event.target.transaction, "event has a transaction"); + ok(event.target.transaction.db === db, "transaction has the right db"); + is(event.target.transaction.mode, "versionchange", + "transaction has the correct mode"); + is(event.target.transaction.objectStoreNames.length, index + 1, + "transaction has correct objectStoreNames list"); + found = false; + for (let j = 0; j < event.target.transaction.objectStoreNames.length; + j++) { + if (event.target.transaction.objectStoreNames.item(j) == name) { + found = true; + break; + } + } + is(found, true, "transaction has correct objectStoreNames list"); + } + + // Can't handle autoincrement and empty keypath + let ex; + try { + db.createObjectStore("storefail", { keyPath: "", autoIncrement: true }); + } + catch(e) { + ex = e; + } + ok(ex, "createObjectStore with empty keyPath and autoIncrement should throw"); + is(ex.name, "InvalidAccessError", "should throw right exception"); + ok(ex instanceof DOMException, "should throw right exception"); + is(ex.code, DOMException.INVALID_ACCESS_ERR, "should throw right exception"); + + // Can't handle autoincrement and array keypath + try { + db.createObjectStore("storefail", { keyPath: ["a"], autoIncrement: true }); + } + catch(e) { + ex = e; + } + ok(ex, "createObjectStore with array keyPath and autoIncrement should throw"); + is(ex.name, "InvalidAccessError", "should throw right exception"); + ok(ex instanceof DOMException, "should throw right exception"); + is(ex.code, DOMException.INVALID_ACCESS_ERR, "should throw right exception"); + + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_cursor_cycle.js b/dom/indexedDB/test/unit/test_cursor_cycle.js new file mode 100644 index 000000000..795a9b63d --- /dev/null +++ b/dom/indexedDB/test/unit/test_cursor_cycle.js @@ -0,0 +1,41 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const Bob = { ss: "237-23-7732", name: "Bob" }; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + event.target.onsuccess = continueToNextStep; + + let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); + objectStore.createIndex("name", "name", { unique: true }); + objectStore.add(Bob); + yield undefined; + + db.transaction("foo", "readwrite").objectStore("foo") + .index("name").openCursor().onsuccess = function(event) { + event.target.transaction.oncomplete = continueToNextStep; + let cursor = event.target.result; + if (cursor) { + let objectStore = event.target.transaction.objectStore("foo"); + objectStore.delete(Bob.ss) + .onsuccess = function(event) { cursor.continue(); }; + } + }; + yield undefined; + finishTest(); + + objectStore = null; // Bug 943409 workaround. + + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_cursor_mutation.js b/dom/indexedDB/test/unit/test_cursor_mutation.js new file mode 100644 index 000000000..13e859891 --- /dev/null +++ b/dom/indexedDB/test/unit/test_cursor_mutation.js @@ -0,0 +1,118 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const objectStoreData = [ + // This one will be removed. + { ss: "237-23-7732", name: "Bob" }, + + // These will always be included. + { ss: "237-23-7733", name: "Ann" }, + { ss: "237-23-7734", name: "Ron" }, + { ss: "237-23-7735", name: "Sue" }, + { ss: "237-23-7736", name: "Joe" }, + + // This one will be added. + { ss: "237-23-7737", name: "Pat" } + ]; + + // Post-add and post-remove data ordered by name. + const objectStoreDataNameSort = [ 1, 4, 5, 2, 3 ]; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + event.target.onsuccess = continueToNextStep; + + let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); + objectStore.createIndex("name", "name", { unique: true }); + + for (let i = 0; i < objectStoreData.length - 1; i++) { + objectStore.add(objectStoreData[i]); + } + yield undefined; + + let count = 0; + + let sawAdded = false; + let sawRemoved = false; + + db.transaction("foo").objectStore("foo").openCursor().onsuccess = + function(event) { + event.target.transaction.oncomplete = continueToNextStep; + let cursor = event.target.result; + if (cursor) { + if (cursor.value.name == objectStoreData[0].name) { + sawRemoved = true; + } + if (cursor.value.name == + objectStoreData[objectStoreData.length - 1].name) { + sawAdded = true; + } + cursor.continue(); + count++; + } + }; + yield undefined; + + is(count, objectStoreData.length - 1, "Good initial count"); + is(sawAdded, false, "Didn't see item that is about to be added"); + is(sawRemoved, true, "Saw item that is about to be removed"); + + count = 0; + sawAdded = false; + sawRemoved = false; + + db.transaction("foo", "readwrite").objectStore("foo") + .index("name").openCursor().onsuccess = function(event) { + event.target.transaction.oncomplete = continueToNextStep; + let cursor = event.target.result; + if (cursor) { + if (cursor.value.name == objectStoreData[0].name) { + sawRemoved = true; + } + if (cursor.value.name == + objectStoreData[objectStoreData.length - 1].name) { + sawAdded = true; + } + + is(cursor.value.name, + objectStoreData[objectStoreDataNameSort[count++]].name, + "Correct name"); + + if (count == 1) { + let objectStore = event.target.transaction.objectStore("foo"); + objectStore.delete(objectStoreData[0].ss) + .onsuccess = function(event) { + objectStore.add(objectStoreData[objectStoreData.length - 1]) + .onsuccess = + function(event) { + cursor.continue(); + }; + }; + } + else { + cursor.continue(); + } + } + }; + yield undefined; + + is(count, objectStoreData.length - 1, "Good final count"); + is(sawAdded, true, "Saw item that was added"); + is(sawRemoved, false, "Didn't see item that was removed"); + + finishTest(); + + objectStore = null; // Bug 943409 workaround. + + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js b/dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js new file mode 100644 index 000000000..1ea0bc883 --- /dev/null +++ b/dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js @@ -0,0 +1,99 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const START_DATA = "hi"; + const END_DATA = "bye"; + const objectStoreInfo = [ + { name: "1", options: { keyPath: null }, key: 1, + entry: { data: START_DATA } }, + { name: "2", options: { keyPath: "foo" }, + entry: { foo: 1, data: START_DATA } }, + { name: "3", options: { keyPath: null, autoIncrement: true }, + entry: { data: START_DATA } }, + { name: "4", options: { keyPath: "foo", autoIncrement: true }, + entry: { data: START_DATA } }, + ]; + + for (let i = 0; i < objectStoreInfo.length; i++) { + // Create our object stores. + let info = objectStoreInfo[i]; + + ok(true, "1"); + request = indexedDB.open(name, i + 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let db = event.target.result; + + ok(true, "2"); + let objectStore = info.hasOwnProperty("options") ? + db.createObjectStore(info.name, info.options) : + db.createObjectStore(info.name); + + // Create the indexes on 'data' on the object store. + let index = objectStore.createIndex("data_index", "data", + { unique: false }); + let uniqueIndex = objectStore.createIndex("unique_data_index", "data", + { unique: true }); + // Populate the object store with one entry of data. + request = info.hasOwnProperty("key") ? + objectStore.add(info.entry, info.key) : + objectStore.add(info.entry); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + // Use a cursor to update 'data' to END_DATA. + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "4"); + + let cursor = request.result; + let obj = cursor.value; + obj.data = END_DATA; + request = cursor.update(obj); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "5"); + + // Check both indexes to make sure that they were updated. + request = index.get(END_DATA); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "6"); + ok(obj.data, event.target.result.data, + "Non-unique index was properly updated."); + + request = uniqueIndex.get(END_DATA); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "7"); + ok(obj.data, event.target.result.data, + "Unique index was properly updated."); + + // Wait for success + yield undefined; + + db.close(); + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_cursors.js b/dom/indexedDB/test/unit/test_cursors.js new file mode 100644 index 000000000..48d1eff83 --- /dev/null +++ b/dom/indexedDB/test/unit/test_cursors.js @@ -0,0 +1,383 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const keys = [1, -1, 0, 10, 2000, "q", "z", "two", "b", "a"]; + const sortedKeys = [-1, 0, 1, 10, 2000, "a", "b", "q", "two", "z"]; + + is(keys.length, sortedKeys.length, "Good key setup"); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore("autoIncrement", + { autoIncrement: true }); + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + ok(!event.target.result, "No results"); + testGenerator.next(); + } + yield undefined; + + objectStore = db.createObjectStore("autoIncrementKeyPath", + { keyPath: "foo", + autoIncrement: true }); + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + ok(!event.target.result, "No results"); + testGenerator.next(); + } + yield undefined; + + objectStore = db.createObjectStore("keyPath", { keyPath: "foo" }); + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + ok(!event.target.result, "No results"); + testGenerator.next(); + } + yield undefined; + + objectStore = db.createObjectStore("foo"); + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + ok(!event.target.result, "No results"); + testGenerator.next(); + } + yield undefined; + + let keyIndex = 0; + + for (let i in keys) { + request = objectStore.add("foo", keys[i]); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++keyIndex == keys.length) { + testGenerator.next(); + } + }; + } + yield undefined; + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + cursor.continue(); + + try { + cursor.continue(); + ok(false, "continue twice should throw"); + } + catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "InvalidStateError", "correct error"); + is(e.code, DOMException.INVALID_STATE_ERR, "correct code"); + } + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all added items"); + + keyIndex = 4; + + let range = IDBKeyRange.bound(2000, "q"); + request = objectStore.openCursor(range); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + cursor.continue(); + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 8, "Saw all the expected keys"); + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + if (keyIndex) { + cursor.continue(); + } + else { + cursor.continue("b"); + } + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex += keyIndex ? 1: 6; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all the expected keys"); + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + if (keyIndex) { + cursor.continue(); + } + else { + cursor.continue(10); + } + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex += keyIndex ? 1: 3; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all the expected keys"); + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + if (keyIndex) { + cursor.continue(); + } + else { + cursor.continue("c"); + } + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex += keyIndex ? 1 : 7; + } + else { + ok(cursor === null, "The request result should be null."); + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all the expected keys"); + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + let storedCursor = null; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + storedCursor = cursor; + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + if (keyIndex == 4) { + request = cursor.update("bar"); + request.onerror = errorHandler; + request.onsuccess = function(event) { + keyIndex++; + cursor.continue(); + }; + } + else { + keyIndex++; + cursor.continue(); + } + } + else { + ok(cursor === null, "The request result should be null."); + ok(storedCursor.value === undefined, "The cursor's value should be undefined."); + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all the expected keys"); + + request = objectStore.get(sortedKeys[4]); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, "bar", "Update succeeded"); + + request = objectStore.put("foo", sortedKeys[4]); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + keyIndex = 0; + + let gotRemoveEvent = false; + let retval = false; + + request = objectStore.openCursor(null, "next"); + request.onerror = errorHandler; + storedCursor = null; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + storedCursor = cursor; + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + if (keyIndex == 4) { + request = cursor.delete(); + request.onerror = errorHandler; + request.onsuccess = function(event) { + ok(event.target.result === undefined, "Should be undefined"); + is(keyIndex, 5, "Got result of remove before next continue"); + gotRemoveEvent = true; + }; + } + + keyIndex++; + cursor.continue(); + } + else { + ok(cursor === null, "The request result should be null."); + ok(storedCursor.value === undefined, "The cursor's value should be undefined."); + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, keys.length, "Saw all the expected keys"); + is(gotRemoveEvent, true, "Saw the remove event"); + + request = objectStore.get(sortedKeys[4]); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Entry was deleted"); + + request = objectStore.add("foo", sortedKeys[4]); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + keyIndex = sortedKeys.length - 1; + + request = objectStore.openCursor(null, "prev"); + request.onerror = errorHandler; + storedCursor = null; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + storedCursor = cursor; + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + cursor.continue(); + + is(cursor.key, sortedKeys[keyIndex], "Correct key"); + is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key"); + is(cursor.value, "foo", "Correct value"); + + keyIndex--; + } + else { + ok(cursor === null, "The request result should be null."); + ok(storedCursor.value === undefined, "The cursor's value should be undefined."); + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all added items"); + + // Wait for success + yield undefined; + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_database_close_without_onclose.js b/dom/indexedDB/test/unit/test_database_close_without_onclose.js new file mode 100644 index 000000000..e4ba62335 --- /dev/null +++ b/dom/indexedDB/test/unit/test_database_close_without_onclose.js @@ -0,0 +1,49 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : + "test_database_close_without_onclose.js"; + + const checkpointSleepTimeSec = 10; + + 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"); + + db.onclose = errorHandler; + + db.close(); + setTimeout(continueToNextStepSync, checkpointSleepTimeSec * 1000); + yield undefined; + + ok(true, "The close event should not be fired after closed normally!"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_database_onclose.js b/dom/indexedDB/test/unit/test_database_onclose.js new file mode 100644 index 000000000..28650d835 --- /dev/null +++ b/dom/indexedDB/test/unit/test_database_onclose.js @@ -0,0 +1,245 @@ +/** + * 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; +} diff --git a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js new file mode 100644 index 000000000..ce87f6dac --- /dev/null +++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js @@ -0,0 +1,160 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/default/http+++localhost + { url: "http://localhost", dbName: "dbA", dbVersion: 1 }, + + // This one lives in storage/default/http+++www.mozilla.org + { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }, + + // This one lives in storage/default/http+++www.mozilla.org+8080 + { url: "http://www.mozilla.org:8080", dbName: "dbC", dbVersion: 1 }, + + // This one lives in storage/default/https+++www.mozilla.org + { url: "https://www.mozilla.org", dbName: "dbD", dbVersion: 1 }, + + // This one lives in storage/default/https+++www.mozilla.org+8080 + { url: "https://www.mozilla.org:8080", dbName: "dbE", dbVersion: 1 }, + + // This one lives in storage/permanent/indexeddb+++fx-devtools + { url: "indexeddb://fx-devtools", dbName: "dbF", + dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/permanent/moz-safe-about+home + { url: "moz-safe-about:home", dbName: "dbG", + dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/default/file++++Users+joe+ + { url: "file:///Users/joe/", dbName: "dbH", dbVersion: 1 }, + + // This one lives in storage/default/file++++Users+joe+index.html + { url: "file:///Users/joe/index.html", dbName: "dbI", dbVersion: 1 }, + + // This one lives in storage/default/file++++c++Users+joe+ + { url: "file:///c:/Users/joe/", dbName: "dbJ", dbVersion: 1 }, + + // This one lives in storage/default/file++++c++Users+joe+index.html + { url: "file:///c:/Users/joe/index.html", dbName: "dbK", dbVersion: 1 }, + + // This one lives in storage/permanent/chrome + { dbName: "dbL", dbVersion: 1 }, + + // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net + { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net", + dbName: "dbN", dbVersion: 1 }, + + // This one lives in storage/default/http+++127.0.0.1 + { url: "http://127.0.0.1", dbName: "dbO", dbVersion: 1 }, + + // This one lives in storage/default/file++++ + { url: "file:///", dbName: "dbP", dbVersion: 1 }, + + // This one lives in storage/default/file++++c++ + { url: "file:///c:/", dbName: "dbQ", dbVersion: 1 }, + + // This one lives in storage/default/file++++Users+joe+c+++index.html + { url: "file:///Users/joe/c++/index.html", dbName: "dbR", dbVersion: 1 }, + + // This one lives in storage/default/file++++Users+joe+c+++index.html + { url: "file:///Users/joe/c///index.html", dbName: "dbR", dbVersion: 1 }, + + // This one lives in storage/default/file++++++index.html + { url: "file:///+/index.html", dbName: "dbS", dbVersion: 1 }, + + // This one lives in storage/default/file++++++index.html + { url: "file://///index.html", dbName: "dbS", dbVersion: 1 }, + + // This one lives in storage/permanent/resource+++fx-share-addon-at-mozilla-dot-org-fx-share-addon-data + { url: "resource://fx-share-addon-at-mozilla-dot-org-fx-share-addon-data", + dbName: "dbU", dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/temporary/http+++localhost+81 + // The .metadata file was intentionally removed for this origin directory + // to test restoring during upgrade. + { url: "http://localhost:81", dbName: "dbV", + dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/temporary/http+++localhost+82 + // The .metadata file was intentionally truncated for this origin directory + // to test restoring during upgrade. + { url: "http://localhost:82", dbName: "dbW", + dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/temporary/1007+t+https+++developer.cdn.mozilla.net + { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net", + dbName: "dbY", dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/temporary/http+++localhost + { url: "http://localhost", dbName: "dbZ", + dbOptions: { version: 1, storage: "temporary" } } + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let request; + if ("url" in params) { + let uri = ios.newURI(params.url, null, null); + let principal = + ssm.createCodebasePrincipal(uri, + {appId: params.appId || ssm.NO_APPID, + inIsolatedMozBrowser: params.inIsolatedMozBrowser}); + if ("dbVersion" in params) { + request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbVersion); + } else { + request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbOptions); + } + } else { + if ("dbVersion" in params) { + request = indexedDB.open(params.dbName, params.dbVersion); + } else { + request = indexedDB.open(params.dbName, params.dbOptions); + } + } + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("defaultStorageUpgrade_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_deleteDatabase.js b/dom/indexedDB/test/unit/test_deleteDatabase.js new file mode 100644 index 000000000..7c9f76522 --- /dev/null +++ b/dom/indexedDB/test/unit/test_deleteDatabase.js @@ -0,0 +1,106 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + ok(indexedDB.deleteDatabase, "deleteDatabase function should exist!"); + + let request = indexedDB.open(name, 10); + request.onerror = errorHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + + ok(request 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("stuff"); + + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Expect a success event"); + is(event.target, request, "Event has right target"); + ok(event.target.result instanceof IDBDatabase, "Result should be a database"); + is(db.objectStoreNames.length, 1, "Expect an objectStore here"); + + request = indexedDB.open(name, 10); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "Expect a success event"); + is(event.target, request, "Event has right target"); + ok(event.target.result instanceof IDBDatabase, "Result should be a database"); + let db2 = event.target.result; + is(db2.objectStoreNames.length, 1, "Expect an objectStore here"); + + var onversionchangecalled = false; + + function closeDBs(event) { + onversionchangecalled = true; + ok(event instanceof IDBVersionChangeEvent, "expect a versionchange event"); + is(event.oldVersion, 10, "oldVersion should be 10"); + ok(event.newVersion === null, "newVersion should be null"); + ok(!(event.newVersion === undefined), "newVersion should be null"); + ok(!(event.newVersion === 0), "newVersion should be null"); + db.close(); + db2.close(); + db.onversionchange = unexpectedSuccessHandler; + db2.onversionchange = unexpectedSuccessHandler; + }; + + // The IDB spec doesn't guarantee the order that onversionchange will fire + // on the dbs. + db.onversionchange = closeDBs; + db2.onversionchange = closeDBs; + + request = indexedDB.deleteDatabase(name); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest"); + + event = yield undefined; + ok(onversionchangecalled, "Expected versionchange events"); + is(event.type, "success", "expect a success event"); + is(event.target, request, "event has right target"); + ok(event.target.result === undefined, "event should have no result"); + + request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.target.result.version, 1, "DB has proper version"); + is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores"); + + + request = indexedDB.deleteDatabase("thisDatabaseHadBetterNotExist"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + ok(true, "deleteDatabase on a non-existent database succeeded"); + + request = indexedDB.open("thisDatabaseHadBetterNotExist"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + ok(true, "after deleting a non-existent database, open should work"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_deleteDatabase_interactions.js b/dom/indexedDB/test/unit/test_deleteDatabase_interactions.js new file mode 100644 index 000000000..87f6a6d20 --- /dev/null +++ b/dom/indexedDB/test/unit/test_deleteDatabase_interactions.js @@ -0,0 +1,62 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 10); + request.onerror = errorHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + + ok(request 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("stuff"); + + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Expect a success event"); + is(event.target, request, "Event has right target"); + ok(event.target.result instanceof IDBDatabase, "Result should be a database"); + is(db.objectStoreNames.length, 1, "Expect an objectStore here"); + + db.close(); + + request = indexedDB.deleteDatabase(name); + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest"); + + let openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + + event = yield undefined; + is(event.type, "success", "expect a success event"); + is(event.target, request, "event has right target"); + is(event.target.result, undefined, "event should have no result"); + + openRequest.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.target.result.version, 1, "DB has proper version"); + is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js new file mode 100644 index 000000000..51390612d --- /dev/null +++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js @@ -0,0 +1,83 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const dbVersion = 10; + + let openRequest = indexedDB.open(name, dbVersion); + openRequest.onerror = errorHandler; + openRequest.onblocked = errorHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + + 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.onversionchange = errorHandler; + db.createObjectStore("stuff"); + + 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"); + + db.onversionchange = grabEventAndContinueHandler; + let deletingRequest = indexedDB.deleteDatabase(name); + deletingRequest.onerror = errorHandler; + deletingRequest.onsuccess = errorHandler; + deletingRequest.onblocked = errorHandler; + + event = yield undefined; + + is(event.type, "versionchange", "Expect an versionchange event"); + is(event.target, db, "Event has right target"); + ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event"); + is(event.oldVersion, dbVersion, "Correct old version"); + is(event.newVersion, null, "Correct new version"); + + deletingRequest.onblocked = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "blocked", "Expect an blocked event"); + is(event.target, deletingRequest, "Event has right target"); + ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event"); + is(event.oldVersion, dbVersion, "Correct old version"); + is(event.newVersion, null, "Correct new version"); + + deletingRequest.onsuccess = grabEventAndContinueHandler; + db.close(); + + event = yield undefined; + + is(event.type, "success", "expect a success event"); + is(event.target, deletingRequest, "event has right target"); + is(event.target.result, undefined, "event should have no result"); + + openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + db = event.target.result; + is(db.version, 1, "DB has proper version"); + is(db.objectStoreNames.length, 0, "DB should have no object stores"); + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js new file mode 100644 index 000000000..40ece64c0 --- /dev/null +++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js @@ -0,0 +1,84 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const dbVersion = 10; + + let openRequest = indexedDB.open(name, dbVersion); + openRequest.onerror = errorHandler; + openRequest.onblocked = errorHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + + 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.onversionchange = errorHandler; + db.createObjectStore("stuff"); + + let deletingRequest = indexedDB.deleteDatabase(name); + deletingRequest.onerror = errorHandler; + deletingRequest.onsuccess = errorHandler; + deletingRequest.onblocked = errorHandler; + + 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"); + + db.onversionchange = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "versionchange", "Expect an versionchange event"); + is(event.target, db, "Event has right target"); + ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event"); + is(event.oldVersion, dbVersion, "Correct old version"); + is(event.newVersion, null, "Correct new version"); + + deletingRequest.onblocked = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "blocked", "Expect an blocked event"); + is(event.target, deletingRequest, "Event has right target"); + ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event"); + is(event.oldVersion, dbVersion, "Correct old version"); + is(event.newVersion, null, "Correct new version"); + + deletingRequest.onsuccess = grabEventAndContinueHandler; + db.close(); + + event = yield undefined; + + is(event.type, "success", "expect a success event"); + is(event.target, deletingRequest, "event has right target"); + is(event.target.result, undefined, "event should have no result"); + + openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + db = event.target.result; + is(db.version, 1, "DB has proper version"); + is(db.objectStoreNames.length, 0, "DB should have no object stores"); + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_event_source.js b/dom/indexedDB/test/unit/test_event_source.js new file mode 100644 index 000000000..232a13f0d --- /dev/null +++ b/dom/indexedDB/test/unit/test_event_source.js @@ -0,0 +1,36 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "Objects"; + + var request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + var event = yield undefined; + + is(event.target.source, null, "correct event.target.source"); + + var db = event.target.result; + var objectStore = db.createObjectStore(objectStoreName, + { autoIncrement: true }); + request = objectStore.add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.source === objectStore, "correct event.source"); + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_file_copy_failure.js b/dom/indexedDB/test/unit/test_file_copy_failure.js new file mode 100644 index 000000000..586149233 --- /dev/null +++ b/dom/indexedDB/test/unit/test_file_copy_failure.js @@ -0,0 +1,75 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = "test_file_copy_failure.js"; + const objectStoreName = "Blobs"; + const blob = getBlob(getView(1024)); + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Creating orphaned file"); + + let filesDir = getChromeFilesDir(); + + let journalFile = filesDir.clone(); + journalFile.append("journals"); + journalFile.append("1"); + + let exists = journalFile.exists(); + ok(!exists, "Journal file doesn't exist"); + + journalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8)); + + let file = filesDir.clone(); + file.append("1"); + + exists = file.exists(); + ok(!exists, "File doesn't exist"); + + file.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8)); + + info("Storing blob"); + + let trans = db.transaction(objectStoreName, "readwrite"); + + request = trans.objectStore(objectStoreName).add(blob, 1); + request.onsuccess = continueToNextStepSync; + + yield undefined; + + trans.oncomplete = continueToNextStepSync; + + yield undefined; + + exists = journalFile.exists(); + ok(!exists, "Journal file doesn't exist"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_filehandle_append_read_data.js b/dom/indexedDB/test/unit/test_filehandle_append_read_data.js new file mode 100644 index 000000000..ed2f77ef6 --- /dev/null +++ b/dom/indexedDB/test/unit/test_filehandle_append_read_data.js @@ -0,0 +1,98 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "FileHandle doesn't work in workers yet"; + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis."; + for (let i = 0; i < 5; i++) { + testString += testString; + } + + var testBuffer = getRandomBuffer(100000); + + var testBlob = new Blob([testBuffer], {type: "binary/random"}); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.createMutableFile("test.txt"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let mutableFile = event.target.result; + mutableFile.onerror = errorHandler; + + let location = 0; + + let fileHandle = mutableFile.open("readwrite"); + is(fileHandle.location, location, "Correct location"); + + request = fileHandle.append(testString); + ok(fileHandle.location === null, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location = 0; + request = fileHandle.readAsText(testString.length); + location += testString.length + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let resultString = event.target.result; + ok(resultString == testString, "Correct string data"); + + request = fileHandle.append(testBuffer); + ok(fileHandle.location === null, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location = location; + request = fileHandle.readAsArrayBuffer(testBuffer.byteLength); + location += testBuffer.byteLength; + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let resultBuffer = event.target.result; + ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data"); + + request = fileHandle.append(testBlob); + ok(fileHandle.location === null, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + fileHandle.location = location; + request = fileHandle.readAsArrayBuffer(testBlob.size); + location += testBlob.size; + is(fileHandle.location, location, "Correct location"); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + resultBuffer = event.target.result; + ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data"); + + request = fileHandle.getMetadata({ size: true }); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + is(result.size, location, "Correct size"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_getAll.js b/dom/indexedDB/test/unit/test_getAll.js new file mode 100644 index 000000000..c543feb07 --- /dev/null +++ b/dom/indexedDB/test/unit/test_getAll.js @@ -0,0 +1,195 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const values = [ "a", "1", 1, "foo", 300, true, false, 4.5, null ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request.onsuccess = grabEventAndContinueHandler; + request = objectStore.mozGetAll(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 0, "No elements"); + + let addedCount = 0; + + for (let i in values) { + request = objectStore.add(values[i]); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedCount == values.length) { + executeSoon(function() { testGenerator.next(); }); + } + } + } + yield undefined; + yield undefined; + + request = db.transaction("foo").objectStore("foo").mozGetAll(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, values.length, "Same length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + request = db.transaction("foo").objectStore("foo").mozGetAll(null, 5); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 5, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + let keyRange = IDBKeyRange.bound(1, 9); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, values.length, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, values.length, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, null); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, values.length, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, undefined); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, values.length, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[i], "Same value"); + } + + keyRange = IDBKeyRange.bound(4, 7); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[parseInt(i) + 3], "Same value"); + } + + // Get should take a key range also but it doesn't return an array. + request = db.transaction("foo").objectStore("foo").get(keyRange); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, false, "Not an array object"); + is(event.target.result, values[3], "Correct value"); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[parseInt(i) + 3], "Same value"); + } + + keyRange = IDBKeyRange.bound(4, 7); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, 50); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[parseInt(i) + 3], "Same value"); + } + + keyRange = IDBKeyRange.bound(4, 7); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + keyRange = IDBKeyRange.bound(4, 7, true, true); + + request = db.transaction("foo").objectStore("foo").mozGetAll(keyRange); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], values[parseInt(i) + 4], "Same value"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_getUsage.js b/dom/indexedDB/test/unit/test_getUsage.js new file mode 100644 index 000000000..1834b1815 --- /dev/null +++ b/dom/indexedDB/test/unit/test_getUsage.js @@ -0,0 +1,128 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const origins = [ + { + origin: "http://example.com", + persisted: false, + usage: 49152 + }, + + { + origin: "http://localhost", + persisted: false, + usage: 147456 + }, + + { + origin: "http://www.mozilla.org", + persisted: false, + usage: 98304 + } + ]; + + const allOrigins = [ + { + origin: "chrome", + persisted: false, + usage: 147456 + }, + + { + origin: "http://example.com", + persisted: false, + usage: 49152 + }, + + { + origin: "http://localhost", + persisted: false, + usage: 147456 + }, + + { + origin: "http://www.mozilla.org", + persisted: false, + usage: 98304 + } + ]; + + function verifyResult(result, origins) { + ok(result instanceof Array, "Got an array object"); + ok(result.length == origins.length, "Correct number of elements"); + + info("Sorting elements"); + + result.sort(function(a, b) { + let originA = a.origin + let originB = b.origin + + if (originA < originB) { + return -1; + } + if (originA > originB) { + return 1; + } + return 0; + }); + + info("Verifying elements"); + + for (let i = 0; i < result.length; i++) { + let a = result[i]; + let b = origins[i]; + ok(a.origin == b.origin, "Origin equals"); + ok(a.persisted == b.persisted, "Persisted equals"); + ok(a.usage == b.usage, "Usage equals"); + } + } + + info("Clearing"); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + info("Getting usage"); + + getUsage(grabResultAndContinueHandler, /* getAll */ true); + let result = yield undefined; + + info("Verifying result"); + + verifyResult(result, []); + + info("Installing profile"); + + // The profile contains IndexedDB databases placed across the repositories. + // The file create_db.js in the package was run locally, specifically it was + // temporarily added to xpcshell.ini and then executed: + // mach xpcshell-test --interactive dom/indexedDB/test/unit/create_db.js + installPackagedProfile("getUsage_profile"); + + info("Getting usage"); + + getUsage(grabResultAndContinueHandler, /* getAll */ false); + result = yield undefined; + + info("Verifying result"); + + verifyResult(result, origins); + + info("Getting usage"); + + getUsage(grabResultAndContinueHandler, /* getAll */ true); + result = yield undefined; + + info("Verifying result"); + + verifyResult(result, allOrigins); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_globalObjects_ipc.js b/dom/indexedDB/test/unit/test_globalObjects_ipc.js new file mode 100644 index 000000000..641af0feb --- /dev/null +++ b/dom/indexedDB/test/unit/test_globalObjects_ipc.js @@ -0,0 +1,19 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + // Test for IDBKeyRange and indexedDB availability in ipcshell. + run_test_in_child("./GlobalObjectsChild.js", function() { + do_test_finished(); + continueToNextStep(); + }); + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_globalObjects_other.js b/dom/indexedDB/test/unit/test_globalObjects_other.js new file mode 100644 index 000000000..a1338459c --- /dev/null +++ b/dom/indexedDB/test/unit/test_globalObjects_other.js @@ -0,0 +1,60 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let ioService = + Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + + function getSpec(filename) { + let file = do_get_file(filename); + let uri = ioService.newFileURI(file); + return uri.spec; + } + + // Test for IDBKeyRange and indexedDB availability in JS modules. + Cu.import(getSpec("GlobalObjectsModule.jsm")); + let test = new GlobalObjectsModule(); + test.ok = ok; + test.finishTest = continueToNextStep; + test.runTest(); + yield undefined; + + // Test for IDBKeyRange and indexedDB availability in JS components. + do_load_manifest("GlobalObjectsComponent.manifest"); + test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"]. + createInstance(Ci.nsISupports).wrappedJSObject; + test.ok = ok; + test.finishTest = continueToNextStep; + test.runTest(); + yield undefined; + + // Test for IDBKeyRange and indexedDB availability in JS sandboxes. + let principal = Cc["@mozilla.org/systemprincipal;1"]. + createInstance(Ci.nsIPrincipal); + let sandbox = new Cu.Sandbox(principal, + { wantGlobalProperties: ["indexedDB"] }); + sandbox.__SCRIPT_URI_SPEC__ = getSpec("GlobalObjectsSandbox.js"); + Cu.evalInSandbox( + "Components.classes['@mozilla.org/moz/jssubscript-loader;1'] \ + .createInstance(Components.interfaces.mozIJSSubScriptLoader) \ + .loadSubScript(__SCRIPT_URI_SPEC__);", sandbox, "1.7"); + sandbox.ok = ok; + sandbox.finishTest = continueToNextStep; + Cu.evalInSandbox("runTest();", sandbox); + yield undefined; + + finishTest(); + yield undefined; +} + +this.runTest = function() { + do_get_profile(); + + do_test_pending(); + testGenerator.next(); +} diff --git a/dom/indexedDB/test/unit/test_globalObjects_xpc.js b/dom/indexedDB/test/unit/test_globalObjects_xpc.js new file mode 100644 index 000000000..57611d046 --- /dev/null +++ b/dom/indexedDB/test/unit/test_globalObjects_xpc.js @@ -0,0 +1,26 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = "Splendid Test"; + + // Test for IDBKeyRange and indexedDB availability in xpcshell. + let keyRange = IDBKeyRange.only(42); + ok(keyRange, "Got keyRange"); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + ok(db, "Got database"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_global_data.js b/dom/indexedDB/test/unit/test_global_data.js new file mode 100644 index 000000000..e7df96ec9 --- /dev/null +++ b/dom/indexedDB/test/unit/test_global_data.js @@ -0,0 +1,57 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStore = { name: "Objects", + options: { keyPath: "id", autoIncrement: true } }; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db1 = event.target.result; + + is(db1.objectStoreNames.length, 0, "No objectStores in db1"); + + db1.createObjectStore(objectStore.name, objectStore.options); + + continueToNextStep(); + yield undefined; + + request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let db2 = event.target.result; + + ok(db1 !== db2, "Databases are not the same object"); + + is(db1.objectStoreNames.length, 1, "1 objectStore in db1"); + is(db1.objectStoreNames.item(0), objectStore.name, "Correct name"); + + is(db2.objectStoreNames.length, 1, "1 objectStore in db2"); + is(db2.objectStoreNames.item(0), objectStore.name, "Correct name"); + + let objectStore1 = db1.transaction(objectStore.name) + .objectStore(objectStore.name); + is(objectStore1.name, objectStore.name, "Same name"); + is(objectStore1.keyPath, objectStore.options.keyPath, "Same keyPath"); + + let objectStore2 = db2.transaction(objectStore.name) + .objectStore(objectStore.name); + + ok(objectStore1 !== objectStore2, "Different objectStores"); + is(objectStore1.name, objectStore2.name, "Same name"); + is(objectStore1.keyPath, objectStore2.keyPath, "Same keyPath"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js b/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js new file mode 100644 index 000000000..1e793b391 --- /dev/null +++ b/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js @@ -0,0 +1,68 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/default/http+++www.mozilla.org + { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }, + + // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net + { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net", + dbName: "dbN", dbVersion: 1 }, + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let uri = ios.newURI(params.url, null, null); + let principal = + ssm.createCodebasePrincipal(uri, + {appId: params.appId || ssm.NO_APPID, + inIsolatedMozBrowser: params.inIsolatedMozBrowser}); + let request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbVersion); + return request; + } + + for (let i = 1; i <= 2; i++) { + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("idbSubdirUpgrade" + i + "_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_idle_maintenance.js b/dom/indexedDB/test/unit/test_idle_maintenance.js new file mode 100644 index 000000000..04e3b2d80 --- /dev/null +++ b/dom/indexedDB/test/unit/test_idle_maintenance.js @@ -0,0 +1,174 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let uri = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService). + newURI("https://www.example.com", null, null); + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + info("Setting permissions"); + + let permMgr = + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); + permMgr.add(uri, "indexedDB", Ci.nsIPermissionManager.ALLOW_ACTION); + + info("Setting idle preferences to prevent real 'idle-daily' notification"); + + let prefs = + Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + prefs.setIntPref("idle.lastDailyNotification", (Date.now() / 1000) - 10); + + info("Activating real idle service"); + + do_get_idle(); + + info("Creating databases"); + + let quotaManagerService = Cc["@mozilla.org/dom/quota-manager-service;1"]. + getService(Ci.nsIQuotaManagerService); + + // Keep at least one database open. + let req = indexedDB.open("foo-a", 1); + req.onerror = errorHandler; + req.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let dbA = event.target.result; + + // Keep at least one factory operation alive by deleting a database that is + // stil open. + req = indexedDB.open("foo-b", 1); + req.onerror = errorHandler; + req.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let dbB = event.target.result; + + indexedDB.deleteDatabase("foo-b"); + + // Create a database which we will later try to open while maintenance is + // performed. + req = indexedDB.open("foo-c", 1); + req.onerror = errorHandler; + req.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let dbC = event.target.result; + dbC.close(); + + let dbCount = 0; + + for (let persistence of ["persistent", "temporary", "default"]) { + for (let i = 1; i <= 5; i++) { + let dbName = "foo-" + i; + let dbPersistence = persistence; + let req = indexedDB.openForPrincipal(principal, + dbName, + { version: 1, + storage: dbPersistence }); + req.onerror = event => { + if (dbPersistence != "persistent") { + errorHandler(event); + return; + } + + // Explicit persistence is currently blocked on mobile. + info("Failed to create persistent database '" + dbPersistence + "/" + + dbName + "', hopefully this is on mobile!"); + + event.preventDefault(); + + if (!(--dbCount)) { + continueToNextStep(); + } + }; + req.onupgradeneeded = event => { + let db = event.target.result; + let objectStore = db.createObjectStore("foo"); + + // Add lots of data... + for (let j = 0; j < 100; j++) { + objectStore.add("abcdefghijklmnopqrstuvwxyz0123456789", j); + } + + // And then clear it so that maintenance has some space to reclaim. + objectStore.clear(); + }; + req.onsuccess = event => { + let db = event.target.result; + ok(db, "Created database '" + dbPersistence + "/" + dbName + "'"); + + db.close(); + + if (!(--dbCount)) { + continueToNextStep(); + } + }; + dbCount++; + } + } + yield undefined; + + info("Getting usage before maintenance"); + + let usageBeforeMaintenance; + + quotaManagerService.getUsageForPrincipal(principal, (request) => { + let usage = request.result.usage; + ok(usage > 0, "Usage is non-zero"); + usageBeforeMaintenance = usage; + continueToNextStep(); + }); + yield undefined; + + info("Sending fake 'idle-daily' notification to QuotaManager"); + + let observer = quotaManagerService.QueryInterface(Ci.nsIObserver); + observer.observe(null, "idle-daily", ""); + + info("Opening database while maintenance is performed"); + + req = indexedDB.open("foo-c", 1); + req.onerror = errorHandler; + req.onsuccess = grabEventAndContinueHandler; + yield undefined; + + info("Waiting for maintenance to start"); + + // This time is totally arbitrary. Most likely directory scanning will have + // completed, QuotaManager locks will be acquired, and maintenance tasks will + // be scheduled before this time has elapsed, so we will be testing the + // maintenance code. However, if something is slow then this will test + // shutting down in the middle of maintenance. + setTimeout(continueToNextStep, 10000); + yield undefined; + + info("Getting usage after maintenance"); + + let usageAfterMaintenance; + + quotaManagerService.getUsageForPrincipal(principal, (request) => { + let usage = request.result.usage; + ok(usage > 0, "Usage is non-zero"); + usageAfterMaintenance = usage; + continueToNextStep(); + }); + yield undefined; + + info("Usage before: " + usageBeforeMaintenance + ". " + + "Usage after: " + usageAfterMaintenance); + + ok(usageAfterMaintenance <= usageBeforeMaintenance, + "Maintenance decreased file sizes or left them the same"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_index_empty_keyPath.js b/dom/indexedDB/test/unit/test_index_empty_keyPath.js new file mode 100644 index 000000000..9fbcfc9c9 --- /dev/null +++ b/dom/indexedDB/test/unit/test_index_empty_keyPath.js @@ -0,0 +1,83 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreData = [ + { key: "1", value: "foo" }, + { key: "2", value: "bar" }, + { key: "3", value: "baz" } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; // upgradeneeded + + let db = event.target.result; + + let objectStore = db.createObjectStore("data", { keyPath: null }); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; // testGenerator.send + + // Now create the index. + objectStore.createIndex("set", "", { unique: true }); + yield undefined; // success + + let trans = db.transaction("data", "readwrite"); + objectStore = trans.objectStore("data"); + index = objectStore.index("set"); + + request = index.get("bar"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.target.result, "bar", "Got correct result"); + + request = objectStore.add("foopy", 4); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + yield undefined; + + request = index.get("foopy"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.target.result, "foopy", "Got correct result"); + + request = objectStore.add("foopy", 5); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + + trans.oncomplete = grabEventAndContinueHandler; + + yield undefined; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_index_getAll.js b/dom/indexedDB/test/unit/test_index_getAll.js new file mode 100644 index 000000000..b4af8fd04 --- /dev/null +++ b/dom/indexedDB/test/unit/test_index_getAll.js @@ -0,0 +1,191 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true } }, + { name: "height", keyPath: "height", options: { unique: false } }, + { name: "weight", keyPath: "weight", options: { unique: false } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + yield undefined; + ok(true, "1"); + + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + + ok(true, "2"); + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + request = objectStore.index("height").mozGetAllKeys(65); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, null); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, undefined); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "4"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, objectStoreDataHeightSort.length, + "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[i].key, "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(null, 4); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "5"); + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[i].key, "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "6"); + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 1, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_index_getAllObjects.js b/dom/indexedDB/test/unit/test_index_getAllObjects.js new file mode 100644 index 000000000..1aad5dc08 --- /dev/null +++ b/dom/indexedDB/test/unit/test_index_getAllObjects.js @@ -0,0 +1,233 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true } }, + { name: "height", keyPath: "height", options: { unique: false } }, + { name: "weight", keyPath: "weight", options: { unique: false } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, {}); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + request = objectStore.index("height").mozGetAll(65); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, null); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, undefined); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, objectStoreDataHeightSort.length, + "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[i].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(null, 4); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[i].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 1, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_index_object_cursors.js b/dom/indexedDB/test/unit/test_index_object_cursors.js new file mode 100644 index 000000000..f3daa433b --- /dev/null +++ b/dom/indexedDB/test/unit/test_index_object_cursors.js @@ -0,0 +1,147 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const objectStoreData = [ + { name: "", options: { keyPath: "id", autoIncrement: true } }, + { name: null, options: { keyPath: "ss" } }, + { name: undefined, options: { } }, + { name: "4", options: { autoIncrement: true } }, + ]; + + const indexData = [ + { name: "", keyPath: "name", options: { unique: true } }, + { name: null, keyPath: "height", options: { } } + ]; + + const data = [ + { ss: "237-23-7732", name: "Ann", height: 60 }, + { ss: "237-23-7733", name: "Bob", height: 65 } + ]; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + event.target.onsuccess = continueToNextStep; + + for (let objectStoreIndex in objectStoreData) { + const objectStoreInfo = objectStoreData[objectStoreIndex]; + let objectStore = db.createObjectStore(objectStoreInfo.name, + objectStoreInfo.options); + for (let indexIndex in indexData) { + const indexInfo = indexData[indexIndex]; + let index = objectStore.createIndex(indexInfo.name, + indexInfo.keyPath, + indexInfo.options); + } + } + yield undefined; + + ok(true, "Initial setup"); + + for (let objectStoreIndex in objectStoreData) { + const info = objectStoreData[objectStoreIndex]; + + for (let indexIndex in indexData) { + const objectStoreName = objectStoreData[objectStoreIndex].name; + const indexName = indexData[indexIndex].name; + + let objectStore = + db.transaction(objectStoreName, "readwrite") + .objectStore(objectStoreName); + ok(true, "Got objectStore " + objectStoreName); + + for (let dataIndex in data) { + const obj = data[dataIndex]; + let key; + if (!info.options.keyPath && !info.options.autoIncrement) { + key = obj.ss; + } + objectStore.add(obj, key); + } + + let index = objectStore.index(indexName); + ok(true, "Got index " + indexName); + + let keyIndex = 0; + + index.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (!cursor) { + continueToNextStep(); + return; + } + + is(cursor.key, data[keyIndex][indexData[indexIndex].keyPath], + "Good key"); + is(cursor.value.ss, data[keyIndex].ss, "Correct ss"); + is(cursor.value.name, data[keyIndex].name, "Correct name"); + is(cursor.value.height, data[keyIndex].height, "Correct height"); + + if (!keyIndex) { + let obj = cursor.value; + obj.updated = true; + + cursor.update(obj).onsuccess = function(event) { + ok(true, "Object updated"); + cursor.continue(); + keyIndex++ + } + return; + } + + cursor.delete().onsuccess = function(event) { + ok(true, "Object deleted"); + cursor.continue(); + keyIndex++ + } + }; + yield undefined; + + is(keyIndex, 2, "Saw all the items"); + + keyIndex = 0; + + db.transaction(objectStoreName).objectStore(objectStoreName) + .openCursor() + .onsuccess = function(event) { + let cursor = event.target.result; + if (!cursor) { + continueToNextStep(); + return; + } + + is(cursor.value.ss, data[keyIndex].ss, "Correct ss"); + is(cursor.value.name, data[keyIndex].name, "Correct name"); + is(cursor.value.height, data[keyIndex].height, "Correct height"); + is(cursor.value.updated, true, "Correct updated flag"); + + cursor.continue(); + keyIndex++; + }; + yield undefined; + + is(keyIndex, 1, "Saw all the items"); + + db.transaction(objectStoreName, "readwrite") + .objectStore(objectStoreName).clear() + .onsuccess = continueToNextStep; + yield undefined; + + objectStore = index = null; // Bug 943409 workaround. + } + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_index_update_delete.js b/dom/indexedDB/test/unit/test_index_update_delete.js new file mode 100644 index 000000000..860087aad --- /dev/null +++ b/dom/indexedDB/test/unit/test_index_update_delete.js @@ -0,0 +1,171 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let name = this.window ? window.location.pathname : "Splendid Test"; + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + for (let autoIncrement of [false, true]) { + let objectStore = + db.createObjectStore(autoIncrement, { keyPath: "id", + autoIncrement: autoIncrement }); + + for (let i = 0; i < 10; i++) { + objectStore.add({ id: i, index: i }); + } + + for (let unique of [false, true]) { + objectStore.createIndex(unique, "index", { unique: unique }); + } + + for (let i = 10; i < 20; i++) { + objectStore.add({ id: i, index: i }); + } + } + + event = yield undefined; + is(event.type, "success", "expect a success event"); + + for (let autoIncrement of [false, true]) { + let objectStore = db.transaction(autoIncrement) + .objectStore(autoIncrement); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.target.result, 20, "Correct number of entries in objectStore"); + + let objectStoreCount = event.target.result; + let indexCount = event.target.result; + + for (let unique of [false, true]) { + let index = db.transaction(autoIncrement, "readwrite") + .objectStore(autoIncrement) + .index(unique); + + index.count().onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.target.result, indexCount, + "Correct number of entries in index"); + + let modifiedEntry = unique ? 5 : 10; + let keyRange = IDBKeyRange.only(modifiedEntry); + + let sawEntry = false; + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + sawEntry = true; + is(cursor.key, modifiedEntry, "Correct key"); + + cursor.value.index = unique ? 30 : 35; + cursor.update(cursor.value).onsuccess = function(event) { + cursor.continue(); + } + } + else { + continueToNextStep(); + } + } + yield undefined; + + is(sawEntry, true, "Saw entry for key value " + modifiedEntry); + + // Recount index. Shouldn't change. + index = db.transaction(autoIncrement, "readwrite") + .objectStore(autoIncrement) + .index(unique); + + index.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, indexCount, + "Correct number of entries in index"); + + modifiedEntry = unique ? 30 : 35; + keyRange = IDBKeyRange.only(modifiedEntry); + + sawEntry = false; + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + sawEntry = true; + is(cursor.key, modifiedEntry, "Correct key"); + + delete cursor.value.index; + cursor.update(cursor.value).onsuccess = function(event) { + indexCount--; + cursor.continue(); + } + } + else { + continueToNextStep(); + } + } + yield undefined; + + is(sawEntry, true, "Saw entry for key value " + modifiedEntry); + + // Recount objectStore. Should be unchanged. + objectStore = db.transaction(autoIncrement, "readwrite") + .objectStore(autoIncrement); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreCount, + "Correct number of entries in objectStore"); + + // Recount index. Should be one item less. + index = objectStore.index(unique); + + index.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, indexCount, + "Correct number of entries in index"); + + modifiedEntry = objectStoreCount - 1; + + objectStore.delete(modifiedEntry).onsuccess = + grabEventAndContinueHandler; + event = yield undefined; + + objectStoreCount--; + indexCount--; + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, objectStoreCount, + "Correct number of entries in objectStore"); + + index.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, indexCount, + "Correct number of entries in index"); + + index = event = null; // Bug 943409 workaround. + } + objectStore = event = null; // Bug 943409 workaround. + } + + finishTest(); + event = db = request = null; // Bug 943409 workaround. + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_indexes.js b/dom/indexedDB/test/unit/test_indexes.js new file mode 100644 index 000000000..aaf536feb --- /dev/null +++ b/dom/indexedDB/test/unit/test_indexes.js @@ -0,0 +1,1261 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true } }, + { name: "height", keyPath: "height", options: { } }, + { name: "weight", keyPath: "weight", options: { unique: false } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, { keyPath: null }); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + // Check global properties to make sure they are correct. + is(objectStore.indexNames.length, indexData.length, "Good index count"); + for (let i in indexData) { + let found = false; + for (let j = 0; j < objectStore.indexNames.length; j++) { + if (objectStore.indexNames.item(j) == indexData[i].name) { + found = true; + break; + } + } + is(found, true, "objectStore has our index"); + let index = objectStore.index(indexData[i].name); + is(index.name, indexData[i].name, "Correct name"); + is(index.objectStore.name, objectStore.name, "Correct store name"); + is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); + is(index.unique, indexData[i].options.unique ? true : false, + "Correct unique value"); + } + + request = objectStore.index("name").getKey("Bob"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, "237-23-7732", "Correct key returned!"); + + request = objectStore.index("name").get("Bob"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, "Bob", "Correct name returned!"); + is(event.target.result.height, 60, "Correct height returned!"); + is(event.target.result.weight, 120, "Correct weight returned!"); + + ok(true, "Test group 1"); + + let keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + ok(!("value" in cursor), "No value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + ok(!("value" in cursor), "No value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 2"); + + keyIndex = 0; + + request = objectStore.index("weight").openKeyCursor(null, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight, + "Correct key"); + is(cursor.primaryKey, objectStoreDataWeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight, + "Correct key"); + is(cursor.primaryKey, objectStoreDataWeightSort[keyIndex].key, + "Correct value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length - 1, "Saw all the expected keys"); + + // Check that the name index enforces its unique constraint. + objectStore = db.transaction(objectStoreName, "readwrite") + .objectStore(objectStoreName); + request = objectStore.add({ name: "Bob", height: 62, weight: 170 }, + "237-23-7738"); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + ok(true, "Test group 3"); + + keyIndex = objectStoreDataNameSort.length - 1; + + request = objectStore.index("name").openKeyCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 4"); + + keyIndex = 1; + let keyRange = IDBKeyRange.bound("Bob", "Ron"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 5"); + + keyIndex = 2; + keyRange = IDBKeyRange.bound("Bob", "Ron", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 6"); + + keyIndex = 1; + keyRange = IDBKeyRange.bound("Bob", "Ron", false, true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 7"); + + keyIndex = 2; + keyRange = IDBKeyRange.bound("Bob", "Ron", true, true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 8"); + + keyIndex = 1; + keyRange = IDBKeyRange.lowerBound("Bob"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 9"); + + keyIndex = 2; + keyRange = IDBKeyRange.lowerBound("Bob", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 10"); + + keyIndex = 0; + keyRange = IDBKeyRange.upperBound("Joe"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 3, "Saw all the expected keys"); + + ok(true, "Test group 11"); + + keyIndex = 0; + keyRange = IDBKeyRange.upperBound("Joe", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 2, "Saw all the expected keys"); + + ok(true, "Test group 12"); + + keyIndex = 3; + keyRange = IDBKeyRange.only("Pat"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 13"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 14"); + + keyIndex = objectStoreDataNameSort.length - 1; + + request = objectStore.index("name").openCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 15"); + + keyIndex = 1; + keyRange = IDBKeyRange.bound("Bob", "Ron"); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 16"); + + keyIndex = 2; + keyRange = IDBKeyRange.bound("Bob", "Ron", true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 17"); + + keyIndex = 1; + keyRange = IDBKeyRange.bound("Bob", "Ron", false, true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 18"); + + keyIndex = 2; + keyRange = IDBKeyRange.bound("Bob", "Ron", true, true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 19"); + + keyIndex = 4; + keyRange = IDBKeyRange.bound("Bob", "Ron"); + + request = objectStore.index("name").openCursor(keyRange, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 0, "Saw all the expected keys"); + + ok(true, "Test group 20"); + + // Test "nextunique" + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openKeyCursor(keyRange, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 21"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openKeyCursor(keyRange, + "nextunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 21.5"); + + keyIndex = 5; + + request = objectStore.index("height").openKeyCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 22"); + + keyIndex = 5; + + request = objectStore.index("height").openKeyCursor(null, + "prevunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + if (keyIndex == 5) { + keyIndex--; + } + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 23"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openCursor(keyRange, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 24"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openCursor(keyRange, + "nextunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 24.5"); + + keyIndex = 5; + + request = objectStore.index("height").openCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 25"); + + keyIndex = 5; + + request = objectStore.index("height").openCursor(null, + "prevunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + if (keyIndex == 5) { + keyIndex--; + } + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 26"); + + keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + let nextKey = !keyIndex ? "Pat" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + if (!keyIndex) { + keyIndex = 3; + } + else { + keyIndex++; + } + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 27"); + + keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + let nextKey = !keyIndex ? "Flo" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + keyIndex += keyIndex ? 1 : 2; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 28"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + let nextKey = !keyIndex ? "Pat" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + if (!keyIndex) { + keyIndex = 3; + } + else { + keyIndex++; + } + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 29"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + let nextKey = !keyIndex ? "Flo" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex += keyIndex ? 1 : 2; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_indexes_bad_values.js b/dom/indexedDB/test/unit/test_indexes_bad_values.js new file mode 100644 index 000000000..37817c286 --- /dev/null +++ b/dom/indexedDB/test/unit/test_indexes_bad_values.js @@ -0,0 +1,130 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "Pat", height: 65 } }, + { key: "237-23-7738", value: { name: "Mel", height: 66, weight: {} } } + ]; + + const badObjectStoreData = [ + { key: "237-23-7739", value: { name: "Rob", height: 65 } }, + { key: "237-23-7740", value: { name: "Jen", height: 66, weight: {} } } + ]; + + const indexData = [ + { name: "weight", keyPath: "weight", options: { unique: false } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, { } ); + + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + + addedData = 0; + for (let i in badObjectStoreData) { + request = objectStore.add(badObjectStoreData[i].value, + badObjectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == badObjectStoreData.length) { + executeSoon(function() { testGenerator.next() }); + } + } + } + yield undefined; + yield undefined; + + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + let keyIndex = 0; + + request = objectStore.index("weight").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight, + "Correct key"); + is(cursor.primaryKey, objectStoreDataWeightSort[keyIndex].key, + "Correct value"); + keyIndex++; + + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataWeightSort.length, "Saw all weights"); + + keyIndex = 0; + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + keyIndex++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length + badObjectStoreData.length, + "Saw all people"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_indexes_funny_things.js b/dom/indexedDB/test/unit/test_indexes_funny_things.js new file mode 100644 index 000000000..af0384c77 --- /dev/null +++ b/dom/indexedDB/test/unit/test_indexes_funny_things.js @@ -0,0 +1,168 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + // Blob constructor is not implemented outside of windows yet (Bug 827723). + if (!this.window) { + finishTest(); + yield undefined; + } + + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreName = "Things"; + + const blob1 = new Blob(["foo", "bar"], { type: "text/plain" }); + const blob2 = new Blob(["foobazybar"], { type: "text/plain" }); + const blob3 = new Blob(["2"], { type: "bogus/" }); + const str = "The Book of Mozilla"; + str.type = blob1; + const arr = [1, 2, 3, 4, 5]; + + const objectStoreData = [ + { key: "1", value: blob1}, + { key: "2", value: blob2}, + { key: "3", value: blob3}, + { key: "4", value: str}, + { key: "5", value: arr}, + ]; + + const indexData = [ + { name: "type", keyPath: "type", options: { } }, + { name: "length", keyPath: "length", options: { unique: true } } + ]; + + const objectStoreDataTypeSort = [ + { key: "3", value: blob3}, + { key: "1", value: blob1}, + { key: "2", value: blob2}, + ]; + + const objectStoreDataLengthSort = [ + { key: "5", value: arr}, + { key: "4", value: str}, + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, { keyPath: null }); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + // Check global properties to make sure they are correct. + is(objectStore.indexNames.length, indexData.length, "Good index count"); + for (let i in indexData) { + let found = false; + for (let j = 0; j < objectStore.indexNames.length; j++) { + if (objectStore.indexNames.item(j) == indexData[i].name) { + found = true; + break; + } + } + is(found, true, "objectStore has our index"); + let index = objectStore.index(indexData[i].name); + is(index.name, indexData[i].name, "Correct name"); + is(index.objectStore.name, objectStore.name, "Correct store name"); + is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); + is(index.unique, indexData[i].options.unique ? true : false, + "Correct unique value"); + } + + ok(true, "Test group 1"); + + let keyIndex = 0; + + request = objectStore.index("type").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataTypeSort[keyIndex].value.type, + "Correct key"); + is(cursor.primaryKey, objectStoreDataTypeSort[keyIndex].key, + "Correct primary key"); + ok(!("value" in cursor), "No value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataTypeSort[keyIndex].value.type, + "Correct key"); + is(cursor.primaryKey, objectStoreDataTypeSort[keyIndex].key, + "Correct value"); + ok(!("value" in cursor), "No value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataTypeSort.length, "Saw all the expected keys"); + + ok(true, "Test group 2"); + + keyIndex = 0; + + request = objectStore.index("length").openKeyCursor(null, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataLengthSort[keyIndex].value.length, + "Correct key"); + is(cursor.primaryKey, objectStoreDataLengthSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataLengthSort[keyIndex].value.length, + "Correct key"); + is(cursor.primaryKey, objectStoreDataLengthSort[keyIndex].key, + "Correct value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataLengthSort.length, "Saw all the expected keys"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_invalid_cursor.js b/dom/indexedDB/test/unit/test_invalid_cursor.js new file mode 100644 index 000000000..11fba3654 --- /dev/null +++ b/dom/indexedDB/test/unit/test_invalid_cursor.js @@ -0,0 +1,64 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "Need to implement a gc() function for worker tests"; + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName = ("window" in this) ? window.location.pathname : "test"; + const dbVersion = 1; + const objectStoreName = "foo"; + const data = 0; + + let req = indexedDB.open(dbName, dbVersion); + req.onerror = errorHandler; + req.onupgradeneeded = grabEventAndContinueHandler; + req.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + let db = event.target.result; + + let objectStore = + db.createObjectStore(objectStoreName, { autoIncrement: true }); + objectStore.add(data); + + event = yield undefined; + + is(event.type, "success", "Got success event for open"); + + objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); + + objectStore.openCursor().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got success event for openCursor"); + + let cursor = event.target.result; + is(cursor.value, data, "Got correct cursor value"); + + objectStore.get(cursor.key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data, "Got correct get value"); + + info("Collecting garbage"); + + gc(); + + info("Done collecting garbage"); + + cursor.continue(); + event = yield undefined; + + is(event.target.result, null, "No more entries"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_invalid_version.js b/dom/indexedDB/test/unit/test_invalid_version.js new file mode 100644 index 000000000..7566fad8a --- /dev/null +++ b/dom/indexedDB/test/unit/test_invalid_version.js @@ -0,0 +1,50 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + try { + indexedDB.open(name, 0); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + try { + indexedDB.open(name, -1); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + try { + indexedDB.open(name, { version: 0 }); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + try { + indexedDB.open(name, { version: -1 }); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_invalidate.js b/dom/indexedDB/test/unit/test_invalidate.js new file mode 100644 index 000000000..fa96ae9ed --- /dev/null +++ b/dom/indexedDB/test/unit/test_invalidate.js @@ -0,0 +1,82 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + + let dbCount = 0; + + // Test invalidating during a versionchange transaction. + info("Opening database " + ++dbCount); + + let request = indexedDB.open(databaseName, dbCount); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database " + dbCount); + + request.onupgradeneeded = unexpectedSuccessHandler; + + let objStore = + request.result.createObjectStore("foo", { autoIncrement: true }); + objStore.createIndex("fooIndex", "fooIndex", { unique: true }); + objStore.put({ foo: 1 }); + objStore.get(1); + objStore.count(); + objStore.openCursor(); + objStore.delete(1); + + info("Invalidating database " + dbCount); + + clearAllDatabases(continueToNextStepSync); + + objStore = request.result.createObjectStore("bar"); + objStore.createIndex("barIndex", "barIndex", { multiEntry: true }); + objStore.put({ bar: 1, barIndex: [ 0, 1 ] }, 10); + objStore.get(10); + objStore.count(); + objStore.openCursor(); + objStore.delete(10); + + yield undefined; + + executeSoon(continueToNextStepSync); + yield undefined; + + // Test invalidating after the complete event of a versionchange transaction. + info("Opening database " + ++dbCount); + + request = indexedDB.open(databaseName, dbCount); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database " + dbCount); + + request.onupgradeneeded = unexpectedSuccessHandler; + + request.transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", + "Got complete event for versionchange transaction on database " + dbCount); + + info("Invalidating database " + dbCount); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + executeSoon(continueToNextStepSync); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_key_requirements.js b/dom/indexedDB/test/unit/test_key_requirements.js new file mode 100644 index 000000000..90f3ce864 --- /dev/null +++ b/dom/indexedDB/test/unit/test_key_requirements.js @@ -0,0 +1,285 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.addEventListener("error", function(event) { + event.preventDefault(); + }, false); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request = objectStore.add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let key1 = event.target.result; + + request = objectStore.put({}, key1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key1, "put gave the same key back"); + + let key2 = 10; + + request = objectStore.put({}, key2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key2, "put gave the same key back"); + + key2 = 100; + + request = objectStore.add({}, key2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key2, "put gave the same key back"); + + try { + objectStore.put({}); + ok(true, "put with no key should not throw with autoIncrement!"); + } + catch (e) { + ok(false, "put with no key threw with autoIncrement"); + } + + try { + objectStore.put({}); + ok(true, "put with no key should not throw with autoIncrement!"); + } + catch (e) { + ok(false, "put with no key threw with autoIncrement"); + } + + try { + objectStore.delete(); + ok(false, "remove with no key should throw!"); + } + catch (e) { + ok(true, "remove with no key threw"); + } + + objectStore = db.createObjectStore("bar"); + + try { + objectStore.add({}); + ok(false, "add with no key should throw!"); + } + catch (e) { + ok(true, "add with no key threw"); + } + + try { + objectStore.put({}); + ok(false, "put with no key should throw!"); + } + catch (e) { + ok(true, "put with no key threw"); + } + + try { + objectStore.put({}); + ok(false, "put with no key should throw!"); + } + catch (e) { + ok(true, "put with no key threw"); + } + + try { + objectStore.delete(); + ok(false, "remove with no key should throw!"); + } + catch (e) { + ok(true, "remove with no key threw"); + } + + objectStore = db.createObjectStore("baz", { keyPath: "id" }); + + try { + objectStore.add({}); + ok(false, "add with no key should throw!"); + } + catch (e) { + ok(true, "add with no key threw"); + } + + try { + objectStore.add({id:5}, 5); + ok(false, "add with inline key and passed key should throw!"); + } + catch (e) { + ok(true, "add with inline key and passed key threw"); + } + + try { + objectStore.put({}); + ok(false, "put with no key should throw!"); + } + catch (e) { + ok(true, "put with no key threw"); + } + + try { + objectStore.put({}); + ok(false, "put with no key should throw!"); + } + catch (e) { + ok(true, "put with no key threw"); + } + + try { + objectStore.delete(); + ok(false, "remove with no key should throw!"); + } + catch (e) { + ok(true, "remove with no key threw"); + } + + key1 = 10; + + request = objectStore.add({id:key1}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key1, "add gave back the same key"); + + request = objectStore.put({id:10}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key1, "put gave back the same key"); + + request = objectStore.put({id:10}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key1, "put gave back the same key"); + + request = objectStore.add({id:10}); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + try { + objectStore.add({}, null); + ok(false, "add with null key should throw!"); + } + catch (e) { + ok(true, "add with null key threw"); + } + + try { + objectStore.put({}, null); + ok(false, "put with null key should throw!"); + } + catch (e) { + ok(true, "put with null key threw"); + } + + try { + objectStore.put({}, null); + ok(false, "put with null key should throw!"); + } + catch (e) { + ok(true, "put with null key threw"); + } + + try { + objectStore.delete({}, null); + ok(false, "remove with null key should throw!"); + } + catch (e) { + ok(true, "remove with null key threw"); + } + + objectStore = db.createObjectStore("bazing", { keyPath: "id", + autoIncrement: true }); + + request = objectStore.add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + key1 = event.target.result; + + request = objectStore.put({id:key1}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key1, "put gave the same key back"); + + key2 = 10; + + request = objectStore.put({id:key2}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, key2, "put gave the same key back"); + + try { + objectStore.put({}); + ok(true, "put with no key should not throw with autoIncrement!"); + } + catch (e) { + ok(false, "put with no key threw with autoIncrement"); + } + + try { + objectStore.put({}); + ok(true, "put with no key should not throw with autoIncrement!"); + } + catch (e) { + ok(false, "put with no key threw with autoIncrement"); + } + + try { + objectStore.delete(); + ok(false, "remove with no key should throw!"); + } + catch (e) { + ok(true, "remove with no key threw"); + } + + try { + objectStore.add({id:5}, 5); + ok(false, "add with inline key and passed key should throw!"); + } + catch (e) { + ok(true, "add with inline key and passed key threw"); + } + + request = objectStore.delete(key2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_keys.js b/dom/indexedDB/test/unit/test_keys.js new file mode 100644 index 000000000..00072748e --- /dev/null +++ b/dom/indexedDB/test/unit/test_keys.js @@ -0,0 +1,269 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbname = this.window ? window.location.pathname : "Splendid Test"; + const RW = "readwrite" + let c1 = 1; + let c2 = 1; + + let openRequest = indexedDB.open(dbname, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + let trans = event.target.transaction; + + // Create test stores + let store = db.createObjectStore("store"); + + // Test simple inserts + var keys = [ + -1/0, + -1.7e308, + -10000, + -2, + -1.5, + -1, + -1.00001e-200, + -1e-200, + 0, + 1e-200, + 1.00001e-200, + 1, + 2, + 10000, + 1.7e308, + 1/0, + new Date("1750-01-02"), + new Date("1800-12-31T12:34:56.001"), + new Date(-1000), + new Date(-10), + new Date(-1), + new Date(0), + new Date(1), + new Date(2), + new Date(1000), + new Date("1971-01-01"), + new Date("1971-01-01T01:01:01Z"), + new Date("1971-01-01T01:01:01.001Z"), + new Date("1971-01-01T01:01:01.01Z"), + new Date("1971-01-01T01:01:01.1Z"), + new Date("1980-02-02"), + new Date("3333-03-19T03:33:33.333"), + "", + "\x00", + "\x00\x00", + "\x00\x01", + "\x01", + "\x02", + "\x03", + "\x04", + "\x07", + "\x08", + "\x0F", + "\x10", + "\x1F", + "\x20", + "01234", + "\x3F", + "\x40", + "A", + "A\x00", + "A1", + "ZZZZ", + "a", + "a\x00", + "aa", + "azz", + "}", + "\x7E", + "\x7F", + "\x80", + "\xFF", + "\u0100", + "\u01FF", + "\u0200", + "\u03FF", + "\u0400", + "\u07FF", + "\u0800", + "\u0FFF", + "\u1000", + "\u1FFF", + "\u2000", + "\u3FFF", + "\u4000", + "\u7FFF", + "\u8000", + "\uD800", + "\uD800a", + "\uD800\uDC01", + "\uDBFF", + "\uDC00", + "\uDFFF\uD800", + "\uFFFE", + "\uFFFF", + "\uFFFF\x00", + "\uFFFFZZZ", + [], + [-1/0], + [-1], + [0], + [1], + [1, "a"], + [1, []], + [1, [""]], + [2, 3], + [2, 3.0000000000001], + [12, [[]]], + [12, [[[]]]], + [12, [[[""]]]], + [12, [[["foo"]]]], + [12, [[[[[3]]]]]], + [12, [[[[[[3]]]]]]], + [new Date(-1)], + [new Date(1)], + [""], + ["", [[]]], + ["", [[[]]]], + ["abc"], + ["abc", "def"], + ["abc\x00"], + ["abc\x00", "\x00\x01"], + ["abc\x00", "\x00def"], + ["abc\x00\x00def"], + ["x", [[]]], + ["x", [[[]]]], + [[]], + [[],"foo"], + [[],[]], + [[[]]], + [[[]], []], + [[[]], [[]]], + [[[]], [[1]]], + [[[]], [[[]]]], + [[[1]]], + [[[[]], []]], + ]; + + for (var i = 0; i < keys.length; ++i) { + let keyI = keys[i]; + is(indexedDB.cmp(keyI, keyI), 0, i + " compared to self"); + + function doCompare(keyI) { + for (var j = i-1; j >= i-10 && j >= 0; --j) { + is(indexedDB.cmp(keyI, keys[j]), 1, i + " compared to " + j); + is(indexedDB.cmp(keys[j], keyI), -1, j + " compared to " + i); + } + } + + doCompare(keyI); + store.add(i, keyI).onsuccess = function(e) { + is(indexedDB.cmp(e.target.result, keyI), 0, + "Returned key should cmp as equal"); + ok(compareKeys(e.target.result, keyI), + "Returned key should actually be equal"); + }; + + // Test that -0 compares the same as 0 + if (keyI === 0) { + doCompare(-0); + let req = store.add(i, -0); + req.addEventListener("error", new ExpectError("ConstraintError", true)); + req.onsuccess = unexpectedSuccessHandler; + yield undefined; + } + else if (Array.isArray(keyI) && keyI.length === 1 && keyI[0] === 0) { + doCompare([-0]); + let req = store.add(i, [-0]); + req.addEventListener("error", new ExpectError("ConstraintError", true)); + req.onsuccess = unexpectedSuccessHandler; + yield undefined; + } + } + + store.openCursor().onsuccess = grabEventAndContinueHandler; + for (i = 0; i < keys.length; ++i) { + event = yield undefined; + let cursor = event.target.result; + is(indexedDB.cmp(cursor.key, keys[i]), 0, + "Read back key should cmp as equal"); + ok(compareKeys(cursor.key, keys[i]), + "Read back key should actually be equal"); + is(cursor.value, i, "Stored with right value"); + + cursor.continue(); + } + event = yield undefined; + is(event.target.result, null, "no more results expected"); + + var nan = 0/0; + var invalidKeys = [ + nan, + undefined, + null, + /x/, + {}, + new Date(NaN), + new Date("foopy"), + [nan], + [undefined], + [null], + [/x/], + [{}], + [new Date(NaN)], + [1, nan], + [1, undefined], + [1, null], + [1, /x/], + [1, {}], + [1, [nan]], + [1, [undefined]], + [1, [null]], + [1, [/x/]], + [1, [{}]], + ]; + + for (i = 0; i < invalidKeys.length; ++i) { + try { + indexedDB.cmp(invalidKeys[i], 1); + ok(false, "didn't throw"); + } + catch(ex) { + ok(ex instanceof DOMException, "Threw DOMException"); + is(ex.name, "DataError", "Threw right DOMException"); + is(ex.code, 0, "Threw with right code"); + } + try { + indexedDB.cmp(1, invalidKeys[i]); + ok(false, "didn't throw2"); + } + catch(ex) { + ok(ex instanceof DOMException, "Threw DOMException2"); + is(ex.name, "DataError", "Threw right DOMException2"); + is(ex.code, 0, "Threw with right code2"); + } + try { + store.put(1, invalidKeys[i]); + ok(false, "didn't throw3"); + } + catch(ex) { + ok(ex instanceof DOMException, "Threw DOMException3"); + is(ex.name, "DataError", "Threw right DOMException3"); + is(ex.code, 0, "Threw with right code3"); + } + } + + openRequest.onsuccess = grabEventAndContinueHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_locale_aware_index_getAll.js b/dom/indexedDB/test/unit/test_locale_aware_index_getAll.js new file mode 100644 index 000000000..c59bc127c --- /dev/null +++ b/dom/indexedDB/test/unit/test_locale_aware_index_getAll.js @@ -0,0 +1,191 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true, locale: true } }, + { name: "height", keyPath: "height", options: { unique: false, locale: true } }, + { name: "weight", keyPath: "weight", options: { unique: false, locale: true } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + yield undefined; + ok(true, "1"); + + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + + ok(true, "2"); + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + request = objectStore.index("height").mozGetAllKeys(65); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, null); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, undefined); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "3"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + ok(true, "4"); + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, objectStoreDataHeightSort.length, + "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[i].key, "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(null, 4); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "5"); + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[i].key, "Correct key"); + } + + request = objectStore.index("height").mozGetAllKeys(65, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(true, "6"); + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 1, "Correct length"); + + for (let i in event.target.result) { + is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key, + "Correct key"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js b/dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js new file mode 100644 index 000000000..1ec82f1c2 --- /dev/null +++ b/dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js @@ -0,0 +1,233 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true, locale: true } }, + { name: "height", keyPath: "height", options: { unique: false, locale: true } }, + { name: "weight", keyPath: "weight", options: { unique: false, locale: true } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, {}); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + request = objectStore.index("height").mozGetAll(65); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, 0); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, null); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, undefined); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 2, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, objectStoreDataHeightSort.length, + "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[i].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(null, 4); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 4, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[i].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + request = objectStore.index("height").mozGetAll(65, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array object"); + is(event.target.result.length, 1, "Correct length"); + + for (let i in event.target.result) { + let result = event.target.result[i]; + let testObj = objectStoreDataHeightSort[parseInt(i) + 3].value; + + is(result.name, testObj.name, "Correct name"); + is(result.height, testObj.height, "Correct height"); + + if (testObj.hasOwnProperty("weight")) { + is(result.weight, testObj.weight, "Correct weight"); + } + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_locale_aware_indexes.js b/dom/indexedDB/test/unit/test_locale_aware_indexes.js new file mode 100644 index 000000000..b79ca58c7 --- /dev/null +++ b/dom/indexedDB/test/unit/test_locale_aware_indexes.js @@ -0,0 +1,1268 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreName = "People"; + + const objectStoreData = [ + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } } + ]; + + const indexData = [ + { name: "name", keyPath: "name", options: { unique: true, locale: "es-ES" } }, + { name: "height", keyPath: "height", options: { locale: "auto" } }, + { name: "weight", keyPath: "weight", options: { unique: false, locale: "es-ES" } } + ]; + + const objectStoreDataNameSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } } + ]; + + const objectStoreDataWeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + const objectStoreDataHeightSort = [ + { key: "237-23-7733", value: { name: "ana", height: 52, weight: 110 } }, + { key: "237-23-7735", value: { name: "\u00F3scar", height: 58, weight: 130 } }, + { key: "237-23-7732", value: { name: "\u00E1na", height: 60, weight: 120 } }, + { key: "237-23-7736", value: { name: "bob", height: 65, weight: 150 } }, + { key: "237-23-7737", value: { name: "\u00E9ason", height: 65 } }, + { key: "237-23-7734", value: { name: "fabio", height: 73, weight: 180 } } + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, { keyPath: null }); + + // First, add all our data to the object store. + let addedData = 0; + for (let i in objectStoreData) { + request = objectStore.add(objectStoreData[i].value, + objectStoreData[i].key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedData == objectStoreData.length) { + testGenerator.send(event); + } + } + } + event = yield undefined; + // Now create the indexes. + for (let i in indexData) { + objectStore.createIndex(indexData[i].name, indexData[i].keyPath, + indexData[i].options); + } + is(objectStore.indexNames.length, indexData.length, "Good index count"); + yield undefined; + objectStore = db.transaction(objectStoreName) + .objectStore(objectStoreName); + + // Check global properties to make sure they are correct. + is(objectStore.indexNames.length, indexData.length, "Good index count"); + for (let i in indexData) { + let found = false; + for (let j = 0; j < objectStore.indexNames.length; j++) { + if (objectStore.indexNames.item(j) == indexData[i].name) { + found = true; + break; + } + } + is(found, true, "objectStore has our index"); + let index = objectStore.index(indexData[i].name); + is(index.name, indexData[i].name, "Correct name"); + is(index.objectStore.name, objectStore.name, "Correct store name"); + is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); + is(index.unique, indexData[i].options.unique ? true : false, + "Correct unique value"); + if (indexData[i].options.locale == "auto") { + is(index.isAutoLocale, true, "Correct isAutoLocale value"); + is(index.locale, "en_US", "Correct locale value"); + } else { + is(index.isAutoLocale, false, "Correct isAutoLocale value"); + is(index.locale, indexData[i].options.locale, "Correct locale value"); + } + } + + request = objectStore.index("name").getKey("\u00E1na"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, "237-23-7732", "Correct key returned!"); + + request = objectStore.index("name").get("\u00E1na"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, "\u00E1na", "Correct name returned!"); + is(event.target.result.height, 60, "Correct height returned!"); + is(event.target.result.weight, 120, "Correct weight returned!"); + + ok(true, "Test group 1"); + + let keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + ok(!("value" in cursor), "No value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + ok(!("value" in cursor), "No value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 2"); + + keyIndex = 0; + + request = objectStore.index("weight").openKeyCursor(null, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight, + "Correct key"); + is(cursor.primaryKey, objectStoreDataWeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight, + "Correct key"); + is(cursor.primaryKey, objectStoreDataWeightSort[keyIndex].key, + "Correct value"); + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length - 1, "Saw all the expected keys"); + + // Check that the name index enforces its unique constraint. + objectStore = db.transaction(objectStoreName, "readwrite") + .objectStore(objectStoreName); + request = objectStore.add({ name: "\u00E1na", height: 62, weight: 170 }, + "237-23-7738"); + request.addEventListener("error", new ExpectError("ConstraintError", true)); + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + ok(true, "Test group 3"); + + keyIndex = objectStoreDataNameSort.length - 1; + + request = objectStore.index("name").openKeyCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 4"); + + keyIndex = 1; + let keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 5"); + + keyIndex = 2; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 6"); + + keyIndex = 1; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", false, true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 7"); + + keyIndex = 2; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", true, true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 8"); + + keyIndex = 1; + keyRange = IDBKeyRange.lowerBound("\u00E1na"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 9"); + + keyIndex = 2; + keyRange = IDBKeyRange.lowerBound("\u00E1na", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 10"); + + keyIndex = 0; + keyRange = IDBKeyRange.upperBound("bob"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 3, "Saw all the expected keys"); + + ok(true, "Test group 11"); + + keyIndex = 0; + keyRange = IDBKeyRange.upperBound("bob", true); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 2, "Saw all the expected keys"); + + ok(true, "Test group 12"); + + keyIndex = 3; + keyRange = IDBKeyRange.only("\u00E9ason"); + + request = objectStore.index("name").openKeyCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 13"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 14"); + + keyIndex = objectStoreDataNameSort.length - 1; + + request = objectStore.index("name").openCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 15"); + + keyIndex = 1; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio"); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 16"); + + keyIndex = 2; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 17"); + + keyIndex = 1; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", false, true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 18"); + + keyIndex = 2; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio", true, true); + + request = objectStore.index("name").openCursor(keyRange); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 19"); + + keyIndex = 4; + keyRange = IDBLocaleAwareKeyRange.bound("\u00E1na", "fabio"); + + request = objectStore.index("name").openCursor(keyRange, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 0, "Saw all the expected keys"); + + ok(true, "Test group 20"); + + // Test "nextunique" + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openKeyCursor(keyRange, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 21"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openKeyCursor(keyRange, + "nextunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 21.5"); + + keyIndex = 5; + + request = objectStore.index("height").openKeyCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 22"); + + keyIndex = 5; + + request = objectStore.index("height").openKeyCursor(null, + "prevunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct value"); + + cursor.continue(); + if (keyIndex == 5) { + keyIndex--; + } + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 23"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openCursor(keyRange, "next"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 5, "Saw all the expected keys"); + + ok(true, "Test group 24"); + + keyIndex = 3; + keyRange = IDBKeyRange.only(65); + + request = objectStore.index("height").openCursor(keyRange, + "nextunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex++; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, 4, "Saw all the expected keys"); + + ok(true, "Test group 24.5"); + + keyIndex = 5; + + request = objectStore.index("height").openCursor(null, "prev"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 25"); + + keyIndex = 5; + + request = objectStore.index("height").openCursor(null, + "prevunique"); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height, + "Correct key"); + is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataHeightSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataHeightSort[keyIndex].value.weight, + "Correct weight"); + } + + cursor.continue(); + if (keyIndex == 5) { + keyIndex--; + } + keyIndex--; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, -1, "Saw all the expected keys"); + + ok(true, "Test group 26"); + + keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + let nextKey = !keyIndex ? "\u00E9ason" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + if (!keyIndex) { + keyIndex = 3; + } + else { + keyIndex++; + } + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 27"); + + keyIndex = 0; + + request = objectStore.index("name").openKeyCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + let nextKey = !keyIndex ? "bar" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct value"); + + keyIndex += keyIndex ? 1 : 2; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreData.length, "Saw all the expected keys"); + + ok(true, "Test group 28"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + let nextKey = !keyIndex ? "\u00E9ason" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + if (!keyIndex) { + keyIndex = 3; + } + else { + keyIndex++; + } + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + ok(true, "Test group 29"); + + keyIndex = 0; + + request = objectStore.index("name").openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + let nextKey = !keyIndex ? "bar" : undefined; + + cursor.continue(nextKey); + + is(cursor.key, objectStoreDataNameSort[keyIndex].value.name, + "Correct key"); + is(cursor.primaryKey, objectStoreDataNameSort[keyIndex].key, + "Correct primary key"); + is(cursor.value.name, objectStoreDataNameSort[keyIndex].value.name, + "Correct name"); + is(cursor.value.height, + objectStoreDataNameSort[keyIndex].value.height, + "Correct height"); + if ("weight" in cursor.value) { + is(cursor.value.weight, + objectStoreDataNameSort[keyIndex].value.weight, + "Correct weight"); + } + + keyIndex += keyIndex ? 1 : 2; + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_lowDiskSpace.js b/dom/indexedDB/test/unit/test_lowDiskSpace.js new file mode 100644 index 000000000..eaea5797d --- /dev/null +++ b/dom/indexedDB/test/unit/test_lowDiskSpace.js @@ -0,0 +1,754 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +var disableWorkerTest = "This test uses SpecialPowers"; + +var self = this; + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName = self.window ? window.location.pathname : "test_lowDiskSpace"; + const dbVersion = 1; + + const objectStoreName = "foo"; + const objectStoreOptions = { keyPath: "foo" }; + + const indexName = "bar"; + const indexOptions = { unique: true }; + + const dbData = [ + { foo: 0, bar: 0 }, + { foo: 1, bar: 10 }, + { foo: 2, bar: 20 }, + { foo: 3, bar: 30 }, + { foo: 4, bar: 40 }, + { foo: 5, bar: 50 }, + { foo: 6, bar: 60 }, + { foo: 7, bar: 70 }, + { foo: 8, bar: 80 }, + { foo: 9, bar: 90 } + ]; + + let lowDiskMode = false; + function setLowDiskMode(val) { + let data = val ? "full" : "free"; + + if (val == lowDiskMode) { + info("Low disk mode is: " + data); + } + else { + info("Changing low disk mode to: " + data); + SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", + data); + lowDiskMode = val; + } + } + + { // Make sure opening works from the beginning. + info("Test 1"); + + setLowDiskMode(false); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Opened database without setting low disk mode"); + + let db = event.target.result; + db.close(); + } + + { // Make sure delete works in low disk mode. + info("Test 2"); + + setLowDiskMode(true); + + let request = indexedDB.deleteDatabase(dbName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Deleted database after setting low disk mode"); + } + + { // Make sure creating a db in low disk mode fails. + info("Test 3"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = expectedErrorHandler("QuotaExceededError"); + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "error", "Didn't create new database in low disk mode"); + } + + { // Make sure opening an already-existing db in low disk mode succeeds. + info("Test 4"); + + setLowDiskMode(false); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Created database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + + setLowDiskMode(true); + + request = indexedDB.open(dbName); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Opened existing database in low disk mode"); + + db = event.target.result; + db.close(); + } + + { // Make sure upgrading an already-existing db in low disk mode succeeds. + info("Test 5"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion + 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Created database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + } + + { // Make sure creating objectStores in low disk mode fails. + info("Test 6"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion + 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + let txn = event.target.transaction; + txn.onerror = expectedErrorHandler("AbortError"); + txn.onabort = grabEventAndContinueHandler; + + let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); + + request.onupgradeneeded = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "abort", "Got correct event type"); + is(event.target.error.name, "QuotaExceededError", "Got correct error type"); + + request.onerror = expectedErrorHandler("AbortError"); + event = yield undefined; + } + + { // Make sure creating indexes in low disk mode fails. + info("Test 7"); + + setLowDiskMode(false); + + let request = indexedDB.open(dbName, dbVersion + 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Upgraded database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + + setLowDiskMode(true); + + request = indexedDB.open(dbName, dbVersion + 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + db = event.target.result; + db.onerror = errorHandler; + let txn = event.target.transaction; + txn.onerror = expectedErrorHandler("AbortError"); + txn.onabort = grabEventAndContinueHandler; + + objectStore = event.target.transaction.objectStore(objectStoreName); + let index = objectStore.createIndex(indexName, indexName, indexOptions); + + request.onupgradeneeded = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "abort", "Got correct event type"); + is(event.target.error.name, "QuotaExceededError", "Got correct error type"); + + request.onerror = expectedErrorHandler("AbortError"); + event = yield undefined; + } + + { // Make sure deleting indexes in low disk mode succeeds. + info("Test 8"); + + setLowDiskMode(false); + + let request = indexedDB.open(dbName, dbVersion + 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = event.target.transaction.objectStore(objectStoreName); + let index = objectStore.createIndex(indexName, indexName, indexOptions); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Upgraded database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + + setLowDiskMode(true); + + request = indexedDB.open(dbName, dbVersion + 4); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + db = event.target.result; + db.onerror = errorHandler; + + objectStore = event.target.transaction.objectStore(objectStoreName); + objectStore.deleteIndex(indexName); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Upgraded database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + } + + { // Make sure deleting objectStores in low disk mode succeeds. + info("Test 9"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion + 5); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + db.deleteObjectStore(objectStoreName); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Upgraded database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + + // Reset everything. + indexedDB.deleteDatabase(dbName); + } + + + { // Add data that the rest of the tests will use. + info("Adding test data"); + + setLowDiskMode(false); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); + let index = objectStore.createIndex(indexName, indexName, indexOptions); + + for (let data of dbData) { + objectStore.add(data); + } + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Upgraded database"); + ok(event.target.result === db, "Got the same database"); + + db.close(); + } + + { // Make sure read operations in readonly transactions succeed in low disk + // mode. + info("Test 10"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let transaction = db.transaction(objectStoreName); + let objectStore = transaction.objectStore(objectStoreName); + let index = objectStore.index(indexName); + + let data = dbData[0]; + + let requestCounter = new RequestCounter(); + + objectStore.get(data.foo).onsuccess = requestCounter.handler(); + objectStore.mozGetAll().onsuccess = requestCounter.handler(); + objectStore.count().onsuccess = requestCounter.handler(); + index.get(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAll().onsuccess = requestCounter.handler(); + index.getKey(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAllKeys().onsuccess = requestCounter.handler(); + index.count().onsuccess = requestCounter.handler(); + + let objectStoreDataCount = 0; + + request = objectStore.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + objectStoreDataCount++; + objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(objectStoreDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexDataCount++; + indexDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexKeyDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexKeyDataCount++; + indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexKeyDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + // Wait for all requests. + yield undefined; + + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Transaction succeeded"); + + db.close(); + } + + { // Make sure read operations in readwrite transactions succeed in low disk + // mode. + info("Test 11"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let transaction = db.transaction(objectStoreName, "readwrite"); + let objectStore = transaction.objectStore(objectStoreName); + let index = objectStore.index(indexName); + + let data = dbData[0]; + + let requestCounter = new RequestCounter(); + + objectStore.get(data.foo).onsuccess = requestCounter.handler(); + objectStore.mozGetAll().onsuccess = requestCounter.handler(); + objectStore.count().onsuccess = requestCounter.handler(); + index.get(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAll().onsuccess = requestCounter.handler(); + index.getKey(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAllKeys().onsuccess = requestCounter.handler(); + index.count().onsuccess = requestCounter.handler(); + + let objectStoreDataCount = 0; + + request = objectStore.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + objectStoreDataCount++; + objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(objectStoreDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexDataCount++; + indexDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexKeyDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexKeyDataCount++; + indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexKeyDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + // Wait for all requests. + yield undefined; + + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Transaction succeeded"); + + db.close(); + } + + { // Make sure write operations in readwrite transactions fail in low disk + // mode. + info("Test 12"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let transaction = db.transaction(objectStoreName, "readwrite"); + let objectStore = transaction.objectStore(objectStoreName); + let index = objectStore.index(indexName); + + let data = dbData[0]; + let newData = { foo: 999, bar: 999 }; + + let requestCounter = new RequestCounter(); + + objectStore.add(newData).onerror = requestCounter.errorHandler(); + objectStore.put(newData).onerror = requestCounter.errorHandler(); + + objectStore.get(data.foo).onsuccess = requestCounter.handler(); + objectStore.mozGetAll().onsuccess = requestCounter.handler(); + objectStore.count().onsuccess = requestCounter.handler(); + index.get(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAll().onsuccess = requestCounter.handler(); + index.getKey(data.bar).onsuccess = requestCounter.handler(); + index.mozGetAllKeys().onsuccess = requestCounter.handler(); + index.count().onsuccess = requestCounter.handler(); + + let objectStoreDataCount = 0; + + request = objectStore.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + objectStoreDataCount++; + cursor.update(cursor.value).onerror = requestCounter.errorHandler(); + objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(objectStoreDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexDataCount++; + cursor.update(cursor.value).onerror = requestCounter.errorHandler(); + indexDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + let indexKeyDataCount = 0; + + request = index.openCursor(); + request.onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + indexKeyDataCount++; + cursor.update(cursor.value).onerror = requestCounter.errorHandler(); + indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); + } + else { + is(indexKeyDataCount, dbData.length, "Saw all data"); + requestCounter.decr(); + } + }; + requestCounter.incr(); + + // Wait for all requests. + yield undefined; + + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Transaction succeeded"); + + db.close(); + } + + { // Make sure deleting operations in readwrite transactions succeed in low + // disk mode. + info("Test 13"); + + setLowDiskMode(true); + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let transaction = db.transaction(objectStoreName, "readwrite"); + let objectStore = transaction.objectStore(objectStoreName); + let index = objectStore.index(indexName); + + let dataIndex = 0; + let data = dbData[dataIndex++]; + + let requestCounter = new RequestCounter(); + + objectStore.delete(data.foo).onsuccess = requestCounter.handler(); + + objectStore.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + cursor.delete().onsuccess = requestCounter.handler(); + } + requestCounter.decr(); + }; + requestCounter.incr(); + + index.openCursor(null, "prev").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + cursor.delete().onsuccess = requestCounter.handler(); + } + requestCounter.decr(); + }; + requestCounter.incr(); + + yield undefined; + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, dbData.length - 3, "Actually deleted something"); + + objectStore.clear(); + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, "Actually cleared"); + + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Transaction succeeded"); + + db.close(); + } + + finishTest(); + yield undefined; +} + +function RequestCounter(expectedType) { + this._counter = 0; +} +RequestCounter.prototype = { + incr: function() { + this._counter++; + }, + + decr: function() { + if (!--this._counter) { + continueToNextStepSync(); + } + }, + + handler: function(type, preventDefault) { + this.incr(); + return function(event) { + is(event.type, type || "success", "Correct type"); + this.decr(); + }.bind(this); + }, + + errorHandler: function(eventType, errorName) { + this.incr(); + return function(event) { + is(event.type, eventType || "error", "Correct type"); + is(event.target.error.name, errorName || "QuotaExceededError", + "Correct error name"); + event.preventDefault(); + event.stopPropagation(); + this.decr(); + }.bind(this); + } +}; diff --git a/dom/indexedDB/test/unit/test_maximal_serialized_object_size.js b/dom/indexedDB/test/unit/test_maximal_serialized_object_size.js new file mode 100644 index 000000000..12a933ddc --- /dev/null +++ b/dom/indexedDB/test/unit/test_maximal_serialized_object_size.js @@ -0,0 +1,95 @@ +/** + * 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 name = this.window ? + window.location.pathname : "test_maximal_serialized_object_size.js"; + const megaBytes = 1024 * 1024; + const kMessageOverhead = 1; // in MB + const kMaxIpcMessageSize = 20; // in MB + const kMaxIdbMessageSize = kMaxIpcMessageSize - kMessageOverhead; + + let chunks = new Array(kMaxIdbMessageSize); + for (let i = 0; i < kMaxIdbMessageSize; i++) { + chunks[i] = new ArrayBuffer(1 * megaBytes); + } + + if (this.window) { + SpecialPowers.pushPrefEnv( + { "set": [["dom.indexedDB.maxSerializedMsgSize", + kMaxIpcMessageSize * megaBytes ]] + }, + continueToNextStep + ); + yield undefined; + } else { + setMaxSerializedMsgSize(kMaxIpcMessageSize * megaBytes); + } + + let openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore("test store", { keyPath: "id" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore.name, "Correct object store name"); + + function testTooLargeError(aOperation, aObject) { + try { + objectStore[aOperation](aObject).onerror = errorHandler; + ok(false, "UnknownError is expected to be thrown!"); + } catch (e) { + ok(e instanceof DOMException, "got a DOM exception"); + is(e.name, "UnknownError", "correct error"); + ok(!!e.message, "Error message: " + e.message); + ok(e.message.startsWith("The serialized value is too large"), + "Correct error message prefix."); + } + } + + info("Verify IDBObjectStore.add() - object is too large"); + testTooLargeError("add", { id: 1, data: chunks }); + + info("Verify IDBObjectStore.add() - object size is closed to the maximal size."); + chunks.length = chunks.length - 1; + let request = objectStore.add({ id: 1, data: chunks }); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + yield undefined; + + info("Verify IDBObjectStore.add() - object key is too large"); + chunks.length = 10; + testTooLargeError("add", { id: chunks }); + + objectStore.createIndex("index name", "index"); + ok(objectStore.index("index name"), "Index created."); + + info("Verify IDBObjectStore.add() - index key is too large"); + testTooLargeError("add", { id: 2, index: chunks }); + + info("Verify IDBObjectStore.add() - object key and index key are too large"); + let indexChunks = chunks.splice(0, 5); + testTooLargeError("add", { id: chunks, index: indexChunks }); + + openRequest.onsuccess = continueToNextStep; + yield undefined; + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_metadata2Restore.js b/dom/indexedDB/test/unit/test_metadata2Restore.js new file mode 100644 index 000000000..fe29de67a --- /dev/null +++ b/dom/indexedDB/test/unit/test_metadata2Restore.js @@ -0,0 +1,268 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/permanent/chrome + // The .metadata-v2 file was intentionally removed for this origin directory + // to test restoring. + { dbName: "dbA", + dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/temporary/http+++localhost + // The .metadata-v2 file was intentionally removed for this origin directory + // to test restoring. + { url: "http://localhost", dbName: "dbB", + dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/default/http+++localhost+81^userContextId=1 + // The .metadata-v2 file was intentionally removed for this origin directory + // to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:81", dbName: "dbC", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+82^userContextId=1 + // The .metadata-v2 file was intentionally truncated for this origin directory + // to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:82", dbName: "dbD", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+83^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // 4 bytes of the 64 bit timestamp + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:83", dbName: "dbE", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+84^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:84", dbName: "dbF", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+85^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp and + // the 8 bit persisted flag + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:85", dbName: "dbG", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+86^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag and + // 2 bytes of the 32 bit reserved data 1 + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:86", dbName: "dbH", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+87^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag and + // the 32 bit reserved data 1 + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:87", dbName: "dbI", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+88^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1 and + // 2 bytes of the 32 bit reserved data 2 + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:88", dbName: "dbJ", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+89^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1 and + // the 32 bit reserved data 2 + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:89", dbName: "dbK", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+90^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2 and + // 2 bytes of the 32 bit suffix length + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:90", dbName: "dbL", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+91^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length and + // first 5 chars of the suffix + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:91", dbName: "dbM", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+92^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length and + // the suffix + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:92", dbName: "dbN", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+93^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix and + // 2 bytes of the 32 bit group length + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:93", dbName: "dbO", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+94^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix, + // the 32 bit group length and + // first 5 chars of the group + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:94", dbName: "dbP", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+95^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix, + // the 32 bit group length and + // the group + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:95", dbName: "dbQ", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+96^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix, + // the 32 bit group length, + // the group and + // 2 bytes of the 32 bit origin length + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:96", dbName: "dbR", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+97^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix, + // the 32 bit group length, + // the group, + // the 32 bit origin length and + // first 12 char of the origin + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:97", dbName: "dbS", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+98^userContextId=1 + // The .metadata-v2 file was intentionally modified to contain only + // the 64 bit timestamp, + // the 8 bit persisted flag, + // the 32 bit reserved data 1, + // the 32 bit reserved data 2, + // the 32 bit suffix length, + // the suffix, + // the 32 bit group length, + // the group, + // the 32 bit origin length and + // the origin + // for this origin directory to test restoring. + { attrs: { userContextId: 1 }, url: "http://localhost:98", dbName: "dbT", + dbOptions: { version: 1, storage: "default" } } + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let request; + if ("url" in params) { + let uri = ios.newURI(params.url, null, null); + let principal = ssm.createCodebasePrincipal(uri, params.attrs || {}); + request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbOptions); + } else { + request = indexedDB.open(params.dbName, params.dbOptions); + } + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("metadata2Restore_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_metadataRestore.js b/dom/indexedDB/test/unit/test_metadataRestore.js new file mode 100644 index 000000000..e1a84a150 --- /dev/null +++ b/dom/indexedDB/test/unit/test_metadataRestore.js @@ -0,0 +1,109 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/permanent/chrome + { dbName: "dbA", + dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/temporary/http+++localhost + { url: "http://localhost", dbName: "dbB", + dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/default/http+++localhost+81 + { url: "http://localhost:81", dbName: "dbC", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+82 + { url: "http://localhost:82", dbName: "dbD", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+83 + { url: "http://localhost:83", dbName: "dbE", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+84 + { url: "http://localhost:84", dbName: "dbF", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+85 + { url: "http://localhost:85", dbName: "dbG", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+86 + { url: "http://localhost:86", dbName: "dbH", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+87 + { url: "http://localhost:87", dbName: "dbI", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+88 + { url: "http://localhost:88", dbName: "dbJ", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+89 + { url: "http://localhost:89", dbName: "dbK", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+90 + { url: "http://localhost:90", dbName: "dbL", + dbOptions: { version: 1, storage: "default" } } + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let request; + if ("url" in params) { + let uri = ios.newURI(params.url, null, null); + let principal = ssm.createCodebasePrincipal(uri, {}); + request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbOptions); + } else { + request = indexedDB.open(params.dbName, params.dbOptions); + } + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("metadataRestore_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_multientry.js b/dom/indexedDB/test/unit/test_multientry.js new file mode 100644 index 000000000..c1479498e --- /dev/null +++ b/dom/indexedDB/test/unit/test_multientry.js @@ -0,0 +1,218 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + // Test object stores + + let name = this.window ? window.location.pathname : "Splendid Test"; + let openRequest = indexedDB.open(name, 1); + openRequest.onerror = errorHandler; + openRequest.onupgradeneeded = grabEventAndContinueHandler; + openRequest.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + db.onerror = errorHandler; + let tests = + [{ add: { x: 1, id: 1 }, + indexes:[{ v: 1, k: 1 }] }, + { add: { x: [2, 3], id: 2 }, + indexes:[{ v: 1, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }] }, + { put: { x: [2, 4], id: 1 }, + indexes:[{ v: 2, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }, + { v: 4, k: 1 }] }, + { add: { x: [5, 6, 5, -2, 3], id: 3 }, + indexes:[{ v:-2, k: 3 }, + { v: 2, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }, + { v: 3, k: 3 }, + { v: 4, k: 1 }, + { v: 5, k: 3 }, + { v: 6, k: 3 }] }, + { delete: IDBKeyRange.bound(1, 3), + indexes:[] }, + { put: { x: ["food", {}, false, undefined, /x/, [73, false]], id: 2 }, + indexes:[{ v: "food", k: 2 }] }, + { add: { x: [{}, /x/, -12, "food", null, [false], undefined], id: 3 }, + indexes:[{ v: -12, k: 3 }, + { v: "food", k: 2 }, + { v: "food", k: 3 }] }, + { put: { x: [], id: 2 }, + indexes:[{ v: -12, k: 3 }, + { v: "food", k: 3 }] }, + { put: { x: { y: 3 }, id: 3 }, + indexes:[] }, + { add: { x: false, id: 7 }, + indexes:[] }, + { delete: IDBKeyRange.lowerBound(0), + indexes:[] }, + ]; + + let store = db.createObjectStore("mystore", { keyPath: "id" }); + let index = store.createIndex("myindex", "x", { multiEntry: true }); + is(index.multiEntry, true, "index created with multiEntry"); + + let i; + for (i = 0; i < tests.length; ++i) { + let test = tests[i]; + let testName = " for " + JSON.stringify(test); + let req; + if (test.add) { + req = store.add(test.add); + } + else if (test.put) { + req = store.put(test.put); + } + else if (test.delete) { + req = store.delete(test.delete); + } + else { + ok(false, "borked test"); + } + req.onsuccess = grabEventAndContinueHandler; + let e = yield undefined; + + req = index.openKeyCursor(); + req.onsuccess = grabEventAndContinueHandler; + for (let j = 0; j < test.indexes.length; ++j) { + e = yield undefined; + is(req.result.key, test.indexes[j].v, "found expected index key at index " + j + testName); + is(req.result.primaryKey, test.indexes[j].k, "found expected index primary key at index " + j + testName); + req.result.continue(); + } + e = yield undefined; + ok(req.result == null, "exhausted indexes"); + + let tempIndex = store.createIndex("temp index", "x", { multiEntry: true }); + req = tempIndex.openKeyCursor(); + req.onsuccess = grabEventAndContinueHandler; + for (let j = 0; j < test.indexes.length; ++j) { + e = yield undefined; + is(req.result.key, test.indexes[j].v, "found expected temp index key at index " + j + testName); + is(req.result.primaryKey, test.indexes[j].k, "found expected temp index primary key at index " + j + testName); + req.result.continue(); + } + e = yield undefined; + ok(req.result == null, "exhausted temp index"); + store.deleteIndex("temp index"); + } + + // Unique indexes + tests = + [{ add: { x: 1, id: 1 }, + indexes:[{ v: 1, k: 1 }] }, + { add: { x: [2, 3], id: 2 }, + indexes:[{ v: 1, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }] }, + { put: { x: [2, 4], id: 3 }, + fail: true }, + { put: { x: [1, 4], id: 1 }, + indexes:[{ v: 1, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }, + { v: 4, k: 1 }] }, + { add: { x: [5, 0, 5, 5, 5], id: 3 }, + indexes:[{ v: 0, k: 3 }, + { v: 1, k: 1 }, + { v: 2, k: 2 }, + { v: 3, k: 2 }, + { v: 4, k: 1 }, + { v: 5, k: 3 }] }, + { delete: IDBKeyRange.bound(1, 2), + indexes:[{ v: 0, k: 3 }, + { v: 5, k: 3 }] }, + { add: { x: [0, 6], id: 8 }, + fail: true }, + { add: { x: 5, id: 8 }, + fail: true }, + { put: { x: 0, id: 8 }, + fail: true }, + ]; + + store.deleteIndex("myindex"); + index = store.createIndex("myindex", "x", { multiEntry: true, unique: true }); + is(index.multiEntry, true, "index created with multiEntry"); + + let indexes; + for (i = 0; i < tests.length; ++i) { + let test = tests[i]; + let testName = " for " + JSON.stringify(test); + let req; + if (test.add) { + req = store.add(test.add); + } + else if (test.put) { + req = store.put(test.put); + } + else if (test.delete) { + req = store.delete(test.delete); + } + else { + ok(false, "borked test"); + } + + if (!test.fail) { + req.onsuccess = grabEventAndContinueHandler; + let e = yield undefined; + indexes = test.indexes; + } + else { + req.onsuccess = unexpectedSuccessHandler; + req.onerror = grabEventAndContinueHandler; + ok(true, "waiting for error"); + let e = yield undefined; + ok(true, "got error: " + e.type); + e.preventDefault(); + e.stopPropagation(); + } + + let e; + req = index.openKeyCursor(); + req.onsuccess = grabEventAndContinueHandler; + for (let j = 0; j < indexes.length; ++j) { + e = yield undefined; + is(req.result.key, indexes[j].v, "found expected index key at index " + j + testName); + is(req.result.primaryKey, indexes[j].k, "found expected index primary key at index " + j + testName); + req.result.continue(); + } + e = yield undefined; + ok(req.result == null, "exhausted indexes"); + + let tempIndex = store.createIndex("temp index", "x", { multiEntry: true, unique: true }); + req = tempIndex.openKeyCursor(); + req.onsuccess = grabEventAndContinueHandler; + for (let j = 0; j < indexes.length; ++j) { + e = yield undefined; + is(req.result.key, indexes[j].v, "found expected temp index key at index " + j + testName); + is(req.result.primaryKey, indexes[j].k, "found expected temp index primary key at index " + j + testName); + req.result.continue(); + } + e = yield undefined; + ok(req.result == null, "exhausted temp index"); + store.deleteIndex("temp index"); + } + + + openRequest.onsuccess = grabEventAndContinueHandler; + yield undefined; + + let trans = db.transaction(["mystore"], "readwrite"); + store = trans.objectStore("mystore"); + index = store.index("myindex"); + is(index.multiEntry, true, "index still is multiEntry"); + trans.oncomplete = grabEventAndContinueHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_mutableFileUpgrade.js b/dom/indexedDB/test/unit/test_mutableFileUpgrade.js new file mode 100644 index 000000000..7862c7a90 --- /dev/null +++ b/dom/indexedDB/test/unit/test_mutableFileUpgrade.js @@ -0,0 +1,122 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbNames = [ + "No files", + "Blobs and mutable files" + ] + const version = 1; + const objectStoreName = "test"; + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("mutableFileUpgrade_profile"); + + let request = indexedDB.open(dbNames[0], version); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(1); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, "text", "Correct result"); + + request = indexedDB.open(dbNames[1], version); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Correct event type"); + + db = event.target.result; + db.onerror = errorHandler; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(1); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, "text", "Correct result"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(2); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyBlob(event.target.result, getBlob("blob0")); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(3); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let result = event.target.result; + + verifyBlob(result[0], getBlob("blob1")); + yield undefined; + + verifyBlob(result[1], getBlob("blob2")); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(4); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + verifyMutableFile(event.target.result, getFile("mutablefile0", "", "")); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(5); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + result = event.target.result; + + verifyMutableFile(result[0], getFile("mutablefile1", "", "")); + yield undefined; + + verifyMutableFile(result[1], getFile("mutablefile2", "", "")); + yield undefined; + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName) + .get(6); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + result = event.target.result; + + verifyBlob(result[0], getBlob("blob3")); + yield undefined; + + verifyMutableFile(result[1], getFile("mutablefile3", "", "")); + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_names_sorted.js b/dom/indexedDB/test/unit/test_names_sorted.js new file mode 100644 index 000000000..eac03a84a --- /dev/null +++ b/dom/indexedDB/test/unit/test_names_sorted.js @@ -0,0 +1,114 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreInfo = [ + { name: "foo", options: { keyPath: "id" }, location: 1 }, + { name: "bar", options: { keyPath: "id" }, location: 0 }, + ]; + const indexInfo = [ + { name: "foo", keyPath: "value", location: 1 }, + { name: "bar", keyPath: "value", location: 0 }, + ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + let db = event.target.result; + + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + let objectStore = info.hasOwnProperty("options") ? + db.createObjectStore(info.name, info.options) : + db.createObjectStore(info.name); + + // Test index creation, and that it ends up in indexNames. + let objectStoreName = info.name; + for (let j = 0; j < indexInfo.length; j++) { + let info = indexInfo[j]; + let count = objectStore.indexNames.length; + let index = info.hasOwnProperty("options") ? + objectStore.createIndex(info.name, info.keyPath, + info.options) : + objectStore.createIndex(info.name, info.keyPath); + } + } + + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + + event = yield undefined; + + let objectStoreNames = [] + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + objectStoreNames.push(info.name); + + is(db.objectStoreNames[info.location], info.name, + "Got objectStore name in the right location"); + + let trans = db.transaction(info.name); + let objectStore = trans.objectStore(info.name); + for (let j = 0; j < indexInfo.length; j++) { + let info = indexInfo[j]; + is(objectStore.indexNames[info.location], info.name, + "Got index name in the right location"); + } + } + + let trans = db.transaction(objectStoreNames); + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + + is(trans.objectStoreNames[info.location], info.name, + "Got objectStore name in the right location"); + } + + db.close(); + + request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + + objectStoreNames = [] + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + objectStoreNames.push(info.name); + + is(db.objectStoreNames[info.location], info.name, + "Got objectStore name in the right location"); + + let trans = db.transaction(info.name); + let objectStore = trans.objectStore(info.name); + for (let j = 0; j < indexInfo.length; j++) { + let info = indexInfo[j]; + is(objectStore.indexNames[info.location], info.name, + "Got index name in the right location"); + } + } + + trans = db.transaction(objectStoreNames); + for (let i = 0; i < objectStoreInfo.length; i++) { + let info = objectStoreInfo[i]; + + is(trans.objectStoreNames[info.location], info.name, + "Got objectStore name in the right location"); + } + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_objectCursors.js b/dom/indexedDB/test/unit/test_objectCursors.js new file mode 100644 index 000000000..6c84a7394 --- /dev/null +++ b/dom/indexedDB/test/unit/test_objectCursors.js @@ -0,0 +1,85 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStores = [ + { name: "a", autoIncrement: false }, + { name: "b", autoIncrement: true } + ]; + + const indexes = [ + { name: "a", options: { } }, + { name: "b", options: { unique: true } } + ]; + + var j = 0; + for (let i in objectStores) { + let request = indexedDB.open(name, ++j); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onversionchange = function(event) { + event.target.close(); + }; + + let objectStore = + db.createObjectStore(objectStores[i].name, + { keyPath: "id", + autoIncrement: objectStores[i].autoIncrement }); + + for (let j in indexes) { + objectStore.createIndex(indexes[j].name, "name", indexes[j].options); + } + + let data = { name: "Ben" }; + if (!objectStores[i].autoIncrement) { + data.id = 1; + } + + request = objectStore.add(data); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result == 1 || event.target.result == 2, "Good id"); + } + + executeSoon(function() { testGenerator.next(); }); + yield undefined; + + let request = indexedDB.open(name, j); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + for (let i in objectStores) { + for (let j in indexes) { + let objectStore = db.transaction(objectStores[i].name) + .objectStore(objectStores[i].name); + let index = objectStore.index(indexes[j].name); + + request = index.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function (event) { + is(event.target.result.value.name, "Ben", "Good object"); + executeSoon(function() { testGenerator.next(); }); + } + yield undefined; + } + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_objectStore_getAllKeys.js b/dom/indexedDB/test/unit/test_objectStore_getAllKeys.js new file mode 100644 index 000000000..dfc1870c7 --- /dev/null +++ b/dom/indexedDB/test/unit/test_objectStore_getAllKeys.js @@ -0,0 +1,123 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() { + const dbName = this.window ? + window.location.pathname : + "test_objectStore_getAllKeys"; + const dbVersion = 1; + const objectStoreName = "foo"; + const keyCount = 200; + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + info("Creating database"); + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + for (let i = 0; i < keyCount; i++) { + objectStore.add(true, i); + } + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + db = event.target.result; + objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); + + info("Getting all keys"); + objectStore.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, keyCount, "Got correct array length"); + + let match = true; + for (let i = 0; i < keyCount; i++) { + if (event.target.result[i] != i) { + match = false; + break; + } + } + ok(match, "Got correct keys"); + + info("Getting all keys with key range"); + let keyRange = IDBKeyRange.bound(10, 20, false, true); + objectStore.getAllKeys(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, 10, "Got correct array length"); + + match = true; + for (let i = 10; i < 20; i++) { + if (event.target.result[i - 10] != i) { + match = false; + break; + } + } + ok(match, "Got correct keys"); + + info("Getting all keys with unmatched key range"); + keyRange = IDBKeyRange.bound(10000, 200000); + objectStore.getAllKeys(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, 0, "Got correct array length"); + + info("Getting all keys with limit"); + objectStore.getAllKeys(null, 5).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, 5, "Got correct array length"); + + match = true; + for (let i = 0; i < 5; i++) { + if (event.target.result[i] != i) { + match = false; + break; + } + } + ok(match, "Got correct keys"); + + info("Getting all keys with key range and limit"); + keyRange = IDBKeyRange.bound(10, 20, false, true); + objectStore.getAllKeys(keyRange, 5).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, 5, "Got correct array length"); + + match = true; + for (let i = 10; i < 15; i++) { + if (event.target.result[i - 10] != i) { + match = false; + break; + } + } + ok(match, "Got correct keys"); + + info("Getting all keys with unmatched key range and limit"); + keyRange = IDBKeyRange.bound(10000, 200000); + objectStore.getAllKeys(keyRange, 5).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(Array.isArray(event.target.result), "Got an array result"); + is(event.target.result.length, 0, "Got correct array length"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_objectStore_inline_autoincrement_key_added_on_put.js b/dom/indexedDB/test/unit/test_objectStore_inline_autoincrement_key_added_on_put.js new file mode 100644 index 000000000..176b3962d --- /dev/null +++ b/dom/indexedDB/test/unit/test_objectStore_inline_autoincrement_key_added_on_put.js @@ -0,0 +1,55 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + var request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + var event = yield undefined; + + var db = event.target.result; + + var test = { + name: "inline key; key generator", + autoIncrement: true, + storedObject: {name: "Lincoln"}, + keyName: "id", + }; + + let objectStore = db.createObjectStore(test.name, + { keyPath: test.keyName, + autoIncrement: test.autoIncrement }); + + request = objectStore.add(test.storedObject); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let id = event.target.result; + request = objectStore.get(id); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + // Sanity check! + is(event.target.result.name, test.storedObject.name, + "The correct object was stored."); + + // Ensure that the id was also stored on the object. + is(event.target.result.id, id, "The object had the id stored on it."); + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_objectStore_openKeyCursor.js b/dom/indexedDB/test/unit/test_objectStore_openKeyCursor.js new file mode 100644 index 000000000..3e88cedb5 --- /dev/null +++ b/dom/indexedDB/test/unit/test_objectStore_openKeyCursor.js @@ -0,0 +1,400 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() { + const dbName = this.window ? + window.location.pathname : + "test_objectStore_openKeyCursor"; + const dbVersion = 1; + const objectStoreName = "foo"; + const keyCount = 100; + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + info("Creating database"); + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + for (let i = 0; i < keyCount; i++) { + objectStore.add(true, i); + } + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + db = event.target.result; + objectStore = db.transaction(objectStoreName, "readwrite") + .objectStore(objectStoreName); + + info("Getting all keys"); + objectStore.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + const allKeys = event.target.result; + + ok(Array.isArray(allKeys), "Got an array result"); + is(allKeys.length, keyCount, "Got correct array length"); + + info("Opening normal key cursor"); + + let seenKeys = []; + objectStore.openKeyCursor().onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "next", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, allKeys.length, "Saw the right number of keys"); + + let match = true; + for (let i = 0; i < seenKeys.length; i++) { + if (seenKeys[i] !== allKeys[i]) { + match = false; + break; + } + } + ok(match, "All keys matched"); + + info("Opening key cursor with keyRange"); + + let keyRange = IDBKeyRange.bound(10, 20, false, true); + + seenKeys = []; + objectStore.openKeyCursor(keyRange).onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "next", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, 10, "Saw the right number of keys"); + + match = true; + for (let i = 0; i < seenKeys.length; i++) { + if (seenKeys[i] !== allKeys[i + 10]) { + match = false; + break; + } + } + ok(match, "All keys matched"); + + info("Opening key cursor with unmatched keyRange"); + + keyRange = IDBKeyRange.bound(10000, 200000); + + seenKeys = []; + objectStore.openKeyCursor(keyRange).onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + ok(false, "Shouldn't have any keys here"); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, 0, "Saw the right number of keys"); + + info("Opening reverse key cursor"); + + seenKeys = []; + objectStore.openKeyCursor(null, "prev").onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "prev", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, allKeys.length, "Saw the right number of keys"); + + seenKeys.reverse(); + + match = true; + for (let i = 0; i < seenKeys.length; i++) { + if (seenKeys[i] !== allKeys[i]) { + match = false; + break; + } + } + ok(match, "All keys matched"); + + info("Opening reverse key cursor with key range"); + + keyRange = IDBKeyRange.bound(10, 20, false, true); + + seenKeys = []; + objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "prev", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, 10, "Saw the right number of keys"); + + seenKeys.reverse(); + + match = true; + for (let i = 0; i < 10; i++) { + if (seenKeys[i] !== allKeys[i + 10]) { + match = false; + break; + } + } + ok(match, "All keys matched"); + + info("Opening reverse key cursor with unmatched key range"); + + keyRange = IDBKeyRange.bound(10000, 200000); + + seenKeys = []; + objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + ok(false, "Shouldn't have any keys here"); + cursor.continue(); + }; + yield undefined; + + is(seenKeys.length, 0, "Saw the right number of keys"); + + info("Opening key cursor with advance"); + + seenKeys = []; + objectStore.openKeyCursor().onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "next", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + if (seenKeys.length == 1) { + cursor.advance(10); + } else { + cursor.continue(); + } + }; + yield undefined; + + is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys"); + + match = true; + for (let i = 0, j = 0; i < seenKeys.length; i++) { + if (seenKeys[i] !== allKeys[i + j]) { + match = false; + break; + } + if (i == 0) { + j = 9; + } + } + ok(match, "All keys matched"); + + info("Opening key cursor with continue-to-key"); + + seenKeys = []; + objectStore.openKeyCursor().onsuccess = event => { + let cursor = event.target.result; + if (!cursor) { + continueToNextStepSync(); + return; + } + + is(cursor.source, objectStore, "Correct source"); + is(cursor.direction, "next", "Correct direction"); + + let exception = null; + try { + cursor.update(10); + } catch(e) { + exception = e; + } + ok(!!exception, "update() throws for key cursor"); + + exception = null; + try { + cursor.delete(); + } catch(e) { + exception = e; + } + ok(!!exception, "delete() throws for key cursor"); + + is(cursor.key, cursor.primaryKey, "key and primaryKey match"); + ok(!("value" in cursor), "No 'value' property on key cursor"); + + seenKeys.push(cursor.key); + + if (seenKeys.length == 1) { + cursor.continue(10); + } else { + cursor.continue(); + } + }; + yield undefined; + + is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys"); + + match = true; + for (let i = 0, j = 0; i < seenKeys.length; i++) { + if (seenKeys[i] !== allKeys[i + j]) { + match = false; + break; + } + if (i == 0) { + j = 9; + } + } + ok(match, "All keys matched"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_objectStore_remove_values.js b/dom/indexedDB/test/unit/test_objectStore_remove_values.js new file mode 100644 index 000000000..ee0628e9d --- /dev/null +++ b/dom/indexedDB/test/unit/test_objectStore_remove_values.js @@ -0,0 +1,92 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + var data = [ + { name: "inline key; key generator", + autoIncrement: true, + storedObject: {name: "Lincoln"}, + keyName: "id", + keyValue: undefined, + }, + { name: "inline key; no key generator", + autoIncrement: false, + storedObject: {id: 1, name: "Lincoln"}, + keyName: "id", + keyValue: undefined, + }, + { name: "out of line key; key generator", + autoIncrement: true, + storedObject: {name: "Lincoln"}, + keyName: undefined, + keyValue: undefined, + }, + { name: "out of line key; no key generator", + autoIncrement: false, + storedObject: {name: "Lincoln"}, + keyName: null, + keyValue: 1, + } + ]; + + for (let i = 0; i < data.length; i++) { + let test = data[i]; + + let request = indexedDB.open(name, i+1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onversionchange = function(event) { + event.target.close(); + }; + + let objectStore = db.createObjectStore(test.name, + { keyPath: test.keyName, + autoIncrement: test.autoIncrement }); + + request = objectStore.add(test.storedObject, test.keyValue); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let id = event.target.result; + request = objectStore.get(id); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + // Sanity check! + is(test.storedObject.name, event.target.result.name, + "The correct object was stored."); + + request = objectStore.delete(id); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + // Make sure it was removed. + request = objectStore.get(id); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result === undefined, "Object was deleted"); + + // Wait for success + yield undefined; + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_object_identity.js b/dom/indexedDB/test/unit/test_object_identity.js new file mode 100644 index 000000000..fa7b91339 --- /dev/null +++ b/dom/indexedDB/test/unit/test_object_identity.js @@ -0,0 +1,48 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + let transaction = event.target.transaction; + + let objectStore1 = db.createObjectStore("foo"); + let objectStore2 = transaction.objectStore("foo"); + ok(objectStore1 === objectStore2, "Got same objectStores"); + + let index1 = objectStore1.createIndex("bar", "key"); + let index2 = objectStore2.index("bar"); + ok(index1 === index2, "Got same indexes"); + + request.onsuccess = continueToNextStep; + yield undefined; + + transaction = db.transaction(db.objectStoreNames); + + let objectStore3 = transaction.objectStore("foo"); + let objectStore4 = transaction.objectStore("foo"); + ok(objectStore3 === objectStore4, "Got same objectStores"); + + ok(objectStore3 !== objectStore1, "Different objectStores"); + ok(objectStore4 !== objectStore2, "Different objectStores"); + + let index3 = objectStore3.index("bar"); + let index4 = objectStore4.index("bar"); + ok(index3 === index4, "Got same indexes"); + + ok(index3 !== index1, "Different indexes"); + ok(index4 !== index2, "Different indexes"); + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_odd_result_order.js b/dom/indexedDB/test/unit/test_odd_result_order.js new file mode 100644 index 000000000..fb3d992c9 --- /dev/null +++ b/dom/indexedDB/test/unit/test_odd_result_order.js @@ -0,0 +1,76 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const data = { key: 5, index: 10 }; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + ok(db instanceof IDBDatabase, "Got a real database"); + + db.onerror = errorHandler; + + let objectStore = db.createObjectStore("foo", { keyPath: "key", + autoIncrement: true }); + let index = objectStore.createIndex("foo", "index"); + + event.target.onsuccess = continueToNextStep; + yield undefined; + + objectStore = db.transaction("foo", "readwrite") + .objectStore("foo"); + request = objectStore.add(data); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let key; + executeSoon(function() { + key = request.result; + continueToNextStep(); + }); + yield undefined; + + is(key, data.key, "Got the right key"); + + objectStore = db.transaction("foo").objectStore("foo"); + objectStore.get(data.key).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let obj; + executeSoon(function() { + obj = event.target.result; + continueToNextStep(); + }); + yield undefined; + + is(obj.key, data.key, "Got the right key"); + is(obj.index, data.index, "Got the right property value"); + + objectStore = db.transaction("foo", "readwrite") + .objectStore("foo"); + request = objectStore.delete(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + key = undefined; + executeSoon(function() { + key = request.result; + continueToNextStep(); + }, 0); + yield undefined; + + ok(key === undefined, "Got the right value"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_oldDirectories.js b/dom/indexedDB/test/unit/test_oldDirectories.js new file mode 100644 index 000000000..47a24671e --- /dev/null +++ b/dom/indexedDB/test/unit/test_oldDirectories.js @@ -0,0 +1,72 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + // This lives in storage/default/http+++www.mozilla.org + const url = "http://www.mozilla.org"; + const dbName = "dbC"; + const dbVersion = 1; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase() { + let uri = ios.newURI(url, null, null); + let principal = ssm.createCodebasePrincipal(uri, {}); + let request = indexedDB.openForPrincipal(principal, dbName, dbVersion); + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("oldDirectories_profile"); + + let request = openDatabase(); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + request = openDatabase(); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Correct event type"); + + let directoryService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + + let profileDir = directoryService.get("ProfD", Ci.nsIFile); + + let dir = profileDir.clone(); + dir.append("indexedDB"); + + let exists = dir.exists(); + ok(!exists, "indexedDB doesn't exist"); + + dir = profileDir.clone(); + dir.append("storage"); + dir.append("persistent"); + + exists = dir.exists(); + ok(!exists, "storage/persistent doesn't exist"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_open_empty_db.js b/dom/indexedDB/test/unit/test_open_empty_db.js new file mode 100644 index 000000000..add956669 --- /dev/null +++ b/dom/indexedDB/test/unit/test_open_empty_db.js @@ -0,0 +1,46 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const names = [ + //"", + null, + undefined, + this.window ? window.location.pathname : "Splendid Test" + ]; + + const version = 1; + + for (let name of names) { + let request = indexedDB.open(name, version); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + if (name === null) { + name = "null"; + } + else if (name === undefined) { + name = "undefined"; + } + + let db = event.target.result; + is(db.name, name, "Bad name"); + is(db.version, version, "Bad version"); + is(db.objectStoreNames.length, 0, "Bad objectStores list"); + + is(db.name, request.result.name, "Bad name"); + is(db.version, request.result.version, "Bad version"); + is(db.objectStoreNames.length, request.result.objectStoreNames.length, + "Bad objectStores list"); + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_open_for_principal.js b/dom/indexedDB/test/unit/test_open_for_principal.js new file mode 100644 index 000000000..171f71b39 --- /dev/null +++ b/dom/indexedDB/test/unit/test_open_for_principal.js @@ -0,0 +1,90 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + const objectStoreName = "Foo"; + + const data = { key: 1, value: "bar" }; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, null, "Got no data"); + + request = objectStore.add(data.value, data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.key, "Got correct key"); + + let uri = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService) + .newURI("http://appdata.example.com", null, null); + let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Components.interfaces.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + request = indexedDB.openForPrincipal(principal, name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + db = event.target.result; + db.onerror = errorHandler; + + objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + objectStore = db.transaction([objectStoreName]) + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, null, "Got no data"); + + db.close(); + + request = indexedDB.deleteForPrincipal(principal, name); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_open_objectStore.js b/dom/indexedDB/test/unit/test_open_objectStore.js new file mode 100644 index 000000000..207107941 --- /dev/null +++ b/dom/indexedDB/test/unit/test_open_objectStore.js @@ -0,0 +1,39 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "Objects"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + is(db.objectStoreNames.length, 0, "Bad objectStores list"); + + let objectStore = db.createObjectStore(objectStoreName, + { keyPath: "foo" }); + + is(db.objectStoreNames.length, 1, "Bad objectStores list"); + is(db.objectStoreNames.item(0), objectStoreName, "Bad name"); + + yield undefined; + + objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); + + is(objectStore.name, objectStoreName, "Bad name"); + is(objectStore.keyPath, "foo", "Bad keyPath"); + if(objectStore.indexNames.length, 0, "Bad indexNames"); + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_optionalArguments.js b/dom/indexedDB/test/unit/test_optionalArguments.js new file mode 100644 index 000000000..ad170dc8d --- /dev/null +++ b/dom/indexedDB/test/unit/test_optionalArguments.js @@ -0,0 +1,1711 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const osName = "people"; + const indexName = "weight"; + + const data = [ + { ssn: "237-23-7732", name: "Bob", height: 60, weight: 120 }, + { ssn: "237-23-7733", name: "Ann", height: 52, weight: 110 }, + { ssn: "237-23-7734", name: "Ron", height: 73, weight: 180 }, + { ssn: "237-23-7735", name: "Sue", height: 58, weight: 130 }, + { ssn: "237-23-7736", name: "Joe", height: 65, weight: 150 }, + { ssn: "237-23-7737", name: "Pat", height: 65 }, + { ssn: "237-23-7738", name: "Mel", height: 66, weight: {} }, + { ssn: "237-23-7739", name: "Tom", height: 62, weight: 130 } + ]; + + const weightSort = [1, 0, 3, 7, 4, 2]; + + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(osName, { keyPath: "ssn" }); + objectStore.createIndex(indexName, "weight", { unique: false }); + + for (let i of data) { + objectStore.add(i); + } + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + try { + IDBKeyRange.bound(1, -1); + ok(false, "Bound keyRange with backwards args should throw!"); + } + catch (e) { + is(e.name, "DataError", "Threw correct exception"); + is(e.code, 0, "Threw with correct code"); + } + + try { + IDBKeyRange.bound(1, 1); + ok(true, "Bound keyRange with same arg should be ok"); + } + catch (e) { + ok(false, "Bound keyRange with same arg should have been ok"); + } + + try { + IDBKeyRange.bound(1, 1, true); + ok(false, "Bound keyRange with same arg and open should throw!"); + } + catch (e) { + is(e.name, "DataError", "Threw correct exception"); + is(e.code, 0, "Threw with correct code"); + } + + try { + IDBKeyRange.bound(1, 1, true, true); + ok(false, "Bound keyRange with same arg and open should throw!"); + } + catch (e) { + is(e.name, "DataError", "Threw correct exception"); + is(e.code, 0, "Threw with correct code"); + } + + objectStore = db.transaction(osName).objectStore(osName); + + try { + objectStore.get(); + ok(false, "Get with unspecified arg should have thrown"); + } + catch(e) { + ok(true, "Get with unspecified arg should have thrown"); + } + + try { + objectStore.get(undefined); + ok(false, "Get with undefined should have thrown"); + } + catch(e) { + ok(true, "Get with undefined arg should have thrown"); + } + + try { + objectStore.get(null); + ok(false, "Get with null should have thrown"); + } + catch(e) { + is(e instanceof DOMException, true, + "Got right kind of exception"); + is(e.name, "DataError", "Correct error."); + is(e.code, 0, "Correct code."); + } + + objectStore.get(data[2].ssn).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[2].name, "Correct data"); + + let keyRange = IDBKeyRange.only(data[2].ssn); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[2].name, "Correct data"); + + keyRange = IDBKeyRange.lowerBound(data[2].ssn); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[2].name, "Correct data"); + + keyRange = IDBKeyRange.lowerBound(data[2].ssn, true); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[3].name, "Correct data"); + + keyRange = IDBKeyRange.upperBound(data[2].ssn); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[0].name, "Correct data"); + + keyRange = IDBKeyRange.bound(data[2].ssn, data[4].ssn); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[2].name, "Correct data"); + + keyRange = IDBKeyRange.bound(data[2].ssn, data[4].ssn, true); + + objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.name, data[3].name, "Correct data"); + + objectStore = db.transaction(osName, "readwrite") + .objectStore(osName); + + try { + objectStore.delete(); + ok(false, "Delete with unspecified arg should have thrown"); + } + catch(e) { + ok(true, "Delete with unspecified arg should have thrown"); + } + + try { + objectStore.delete(undefined); + ok(false, "Delete with undefined should have thrown"); + } + catch(e) { + ok(true, "Delete with undefined arg should have thrown"); + } + + try { + objectStore.delete(null); + ok(false, "Delete with null should have thrown"); + } + catch(e) { + is(e instanceof DOMException, true, + "Got right kind of exception"); + is(e.name, "DataError", "Correct error."); + is(e.code, 0, "Correct code."); + } + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.length, "Correct count"); + + objectStore.delete(data[2].ssn).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result === undefined, "Correct result"); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.length - 1, "Correct count"); + + keyRange = IDBKeyRange.bound(data[3].ssn, data[5].ssn); + + objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result === undefined, "Correct result"); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.length - 4, "Correct count"); + + keyRange = IDBKeyRange.lowerBound(10); + + objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + ok(event.target.result === undefined, "Correct result"); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, 0, "Correct count"); + + event.target.transaction.oncomplete = grabEventAndContinueHandler; + + for (let i of data) { + objectStore.add(i); + } + + yield undefined; + + objectStore = db.transaction(osName).objectStore(osName); + + objectStore.count().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.length, "Correct count"); + + let count = 0; + + objectStore.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, data.length, "Correct count for no arg to openCursor"); + + count = 0; + + objectStore.openCursor(null).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, data.length, "Correct count for null arg to openCursor"); + + count = 0; + + objectStore.openCursor(undefined).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, data.length, "Correct count for undefined arg to openCursor"); + + count = 0; + + objectStore.openCursor(data[2].ssn).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for single key arg to openCursor"); + + count = 0; + + objectStore.openCursor("foo").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent single key arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.only(data[2].ssn); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for only keyRange arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[2].ssn); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, data.length - 2, + "Correct count for lowerBound arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[2].ssn, true); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, data.length - 3, + "Correct count for lowerBound arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound("foo"); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent lowerBound arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 2, "Correct count for bound arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn, true); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for bound arg to openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn, true, true); + + objectStore.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, "Correct count for bound arg to openCursor"); + + let index = objectStore.index(indexName); + + count = 0; + + index.openKeyCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for unspecified arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(null).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for null arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(undefined).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for undefined arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(data[0].weight).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for single key arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor("foo").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent key arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.only("foo"); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.only(data[0].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, + "Correct count for only keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for lowerBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for lowerBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound("foo"); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for lowerBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight, + true); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound("foo"); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(0); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for upperBound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight, + true); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight, + true, true); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 2, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 1, + data[weightSort[weightSort.length - 1]].weight + 1); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 2, + data[weightSort[0]].weight - 1); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[1]].weight, + data[weightSort[2]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 3, + "Correct count for bound keyRange arg to index.openKeyCursor"); + + count = 0; + + index.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for unspecified arg to index.openCursor"); + + count = 0; + + index.openCursor(null).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for null arg to index.openCursor"); + + count = 0; + + index.openCursor(undefined).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for undefined arg to index.openCursor"); + + count = 0; + + index.openCursor(data[0].weight).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for single key arg to index.openCursor"); + + count = 0; + + index.openCursor("foo").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent key arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.only("foo"); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.only(data[0].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, + "Correct count for only keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for lowerBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for lowerBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.lowerBound("foo"); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for lowerBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight, + true); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound("foo"); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.upperBound(0); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for upperBound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for bound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight, + true); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 1, + "Correct count for bound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[weightSort.length - 1]].weight, + true, true); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length - 2, + "Correct count for bound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 1, + data[weightSort[weightSort.length - 1]].weight + 1); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for bound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 2, + data[weightSort[0]].weight - 1); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for bound keyRange arg to index.openCursor"); + + count = 0; + keyRange = IDBKeyRange.bound(data[weightSort[1]].weight, + data[weightSort[2]].weight); + + index.openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 3, + "Correct count for bound keyRange arg to index.openCursor"); + + try { + index.get(); + ok(false, "Get with unspecified arg should have thrown"); + } + catch(e) { + ok(true, "Get with unspecified arg should have thrown"); + } + + try { + index.get(undefined); + ok(false, "Get with undefined should have thrown"); + } + catch(e) { + ok(true, "Get with undefined arg should have thrown"); + } + + try { + index.get(null); + ok(false, "Get with null should have thrown"); + } + catch(e) { + is(e instanceof DOMException, true, + "Got right kind of exception"); + is(e.name, "DataError", "Correct error."); + is(e.code, 0, "Correct code."); + } + + index.get(data[0].weight).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[0].weight, "Got correct result"); + + keyRange = IDBKeyRange.only(data[0].weight); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[0].weight, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[0]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight - 1); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[0]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight + 1); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[1]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[1]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[0]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight, true); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[1]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight, true, true); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got correct result"); + + keyRange = IDBKeyRange.upperBound(data[weightSort[5]].weight); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.weight, data[weightSort[0]].weight, + "Got correct result"); + + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true); + + index.get(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got correct result"); + + try { + index.getKey(); + ok(false, "Get with unspecified arg should have thrown"); + } + catch(e) { + ok(true, "Get with unspecified arg should have thrown"); + } + + try { + index.getKey(undefined); + ok(false, "Get with undefined should have thrown"); + } + catch(e) { + ok(true, "Get with undefined arg should have thrown"); + } + + try { + index.getKey(null); + ok(false, "Get with null should have thrown"); + } + catch(e) { + is(e instanceof DOMException, true, + "Got right kind of exception"); + is(e.name, "DataError", "Correct error."); + is(e.code, 0, "Correct code."); + } + + index.getKey(data[0].weight).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[0].ssn, "Got correct result"); + + keyRange = IDBKeyRange.only(data[0].weight); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[0].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight - 1); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight + 1); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[1]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[1]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight, true); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[1]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.bound(data[weightSort[0]].weight, + data[weightSort[1]].weight, true, true); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got correct result"); + + keyRange = IDBKeyRange.upperBound(data[weightSort[5]].weight); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true); + + index.getKey(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got correct result"); + + count = 0; + + index.openKeyCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for no arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(null).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for null arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(undefined).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, weightSort.length, + "Correct count for undefined arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor(data[weightSort[0]].weight).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, "Correct count for single key arg to index.openKeyCursor"); + + count = 0; + + index.openKeyCursor("foo").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 0, + "Correct count for non-existent single key arg to index.openKeyCursor"); + + count = 0; + keyRange = IDBKeyRange.only(data[weightSort[0]].weight); + + index.openKeyCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + count++; + cursor.continue(); + } + else { + testGenerator.next(); + } + } + yield undefined; + + is(count, 1, + "Correct count for only keyRange arg to index.openKeyCursor"); + + objectStore.mozGetAll(data[1].ssn).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, 1, "Got correct length"); + is(event.target.result[0].ssn, data[1].ssn, "Got correct result"); + + objectStore.mozGetAll(null).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, data.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[i].ssn, "Got correct value"); + } + + objectStore.mozGetAll(undefined).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, data.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[i].ssn, "Got correct value"); + } + + objectStore.mozGetAll().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, data.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[i].ssn, "Got correct value"); + } + + keyRange = IDBKeyRange.lowerBound(0); + + objectStore.mozGetAll(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, data.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[i].ssn, "Got correct value"); + } + + index.mozGetAll().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAll(undefined).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAll(null).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAll(data[weightSort[0]].weight).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, 1, "Got correct length"); + is(event.target.result[0].ssn, data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(0); + + index.mozGetAll(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i].ssn, data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i], data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAllKeys(undefined).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i], data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAllKeys(null).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i], data[weightSort[i]].ssn, + "Got correct value"); + } + + index.mozGetAllKeys(data[weightSort[0]].weight).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, 1, "Got correct length"); + is(event.target.result[0], data[weightSort[0]].ssn, "Got correct result"); + + keyRange = IDBKeyRange.lowerBound(0); + + index.mozGetAllKeys(keyRange).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result instanceof Array, true, "Got an array"); + is(event.target.result.length, weightSort.length, "Got correct length"); + for (let i in event.target.result) { + is(event.target.result[i], data[weightSort[i]].ssn, + "Got correct value"); + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_overlapping_transactions.js b/dom/indexedDB/test/unit/test_overlapping_transactions.js new file mode 100644 index 000000000..2606d7aea --- /dev/null +++ b/dom/indexedDB/test/unit/test_overlapping_transactions.js @@ -0,0 +1,92 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStores = [ "foo", "bar" ]; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + event.target.onsuccess = grabEventAndContinueHandler; + for (let i in objectStores) { + db.createObjectStore(objectStores[i], { autoIncrement: true }); + } + event = yield undefined; + + is(db.objectStoreNames.length, objectStores.length, + "Correct objectStoreNames list"); + + for (let i = 0; i < 50; i++) { + let stepNumber = 0; + + request = db.transaction(["foo"], "readwrite") + .objectStore("foo") + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(stepNumber, 1, "This callback came first"); + stepNumber++; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + } + + request = db.transaction(["foo"], "readwrite") + .objectStore("foo") + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(stepNumber, 2, "This callback came second"); + stepNumber++; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + } + + request = db.transaction(["foo", "bar"], "readwrite") + .objectStore("bar") + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(stepNumber, 3, "This callback came third"); + stepNumber++; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + } + + request = db.transaction(["foo", "bar"], "readwrite") + .objectStore("bar") + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(stepNumber, 4, "This callback came fourth"); + stepNumber++; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + } + + request = db.transaction(["bar"], "readwrite") + .objectStore("bar") + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(stepNumber, 5, "This callback came fifth"); + stepNumber++; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + } + + stepNumber++; + yield undefined; yield undefined; yield undefined; yield undefined; yield undefined; + + is(stepNumber, 6, "All callbacks received"); + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_persistenceType.js b/dom/indexedDB/test/unit/test_persistenceType.js new file mode 100644 index 000000000..0f5dd72a2 --- /dev/null +++ b/dom/indexedDB/test/unit/test_persistenceType.js @@ -0,0 +1,86 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = "Splendid Test"; + const version = 1; + + const objectStoreName = "Foo"; + const data = { key: 1, value: "bar" }; + + try { + indexedDB.open(name, { version: version, storage: "unknown" }); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof TypeError, "Got TypeError."); + is(e.name, "TypeError", "Good error name."); + } + + let request = indexedDB.open(name, { version: version, + storage: "persistent" }); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + is(db.name, name, "Correct name"); + is(db.version, version, "Correct version"); + is(db.storage, "persistent", "Correct persistence type"); + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Got no data"); + + request = objectStore.add(data.value, data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.key, "Got correct key"); + + request = indexedDB.open(name, { version: version, + storage: "temporary" }); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + is(db.name, name, "Correct name"); + is(db.version, version, "Correct version"); + is(db.storage, "persistent", "Correct persistence type"); + + objectStore = db.transaction([objectStoreName]) + .objectStore(objectStoreName); + + request = objectStore.get(data.key); + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, data.value, "Got correct data"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_put_get_values.js b/dom/indexedDB/test/unit/test_put_get_values.js new file mode 100644 index 000000000..b7345795c --- /dev/null +++ b/dom/indexedDB/test/unit/test_put_get_values.js @@ -0,0 +1,55 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "Objects"; + + let testString = { key: 0, value: "testString" }; + let testInt = { key: 1, value: 1002 }; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, + { autoIncrement: 0 }); + + request = objectStore.add(testString.value, testString.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testString.key, "Got the right key"); + request = objectStore.get(testString.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testString.value, "Got the right value"); + }; + }; + + request = objectStore.add(testInt.value, testInt.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testInt.key, "Got the right key"); + request = objectStore.get(testInt.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testInt.value, "Got the right value"); + }; + } + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_put_get_values_autoIncrement.js b/dom/indexedDB/test/unit/test_put_get_values_autoIncrement.js new file mode 100644 index 000000000..3291148d3 --- /dev/null +++ b/dom/indexedDB/test/unit/test_put_get_values_autoIncrement.js @@ -0,0 +1,54 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "Objects"; + + let testString = { value: "testString" }; + let testInt = { value: 1002 }; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore(objectStoreName, + { autoIncrement: 1 }); + + request = objectStore.put(testString.value); + request.onerror = errorHandler; + request.onsuccess = function(event) { + testString.key = event.target.result; + request = objectStore.get(testString.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testString.value, "Got the right value"); + }; + }; + + request = objectStore.put(testInt.value); + request.onerror = errorHandler; + request.onsuccess = function(event) { + testInt.key = event.target.result; + request = objectStore.get(testInt.key); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, testInt.value, "Got the right value"); + }; + } + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js b/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js new file mode 100644 index 000000000..1f04bb6d0 --- /dev/null +++ b/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js @@ -0,0 +1,141 @@ +/** + * 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"; + + const android = mozinfo.os == "android"; + + // We want 512 KB database on Android and 4 MB database on other platforms. + const groupLimitKB = android ? 512 : 4096; + + // The group limit is calculated as 20% of the global temporary storage limit. + const tempStorageLimitKB = groupLimitKB * 5; + + // We want 64 KB chunks on Android and 512 KB chunks on other platforms. + const dataSizeKB = android ? 64 : 512; + const dataSize = dataSizeKB * 1024; + + const maxIter = 5; + + 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, { autoIncrement: true }); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + ok(true, "Filling database"); + + let obj = { + name: "foo" + } + + if (!blobs) { + obj.data = getRandomView(dataSize); + } + + let iter = 1; + 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); + request.onerror = function(event) + { + event.stopPropagation(); + } + + trans.oncomplete = function(event) { + if (iter == 1) { + 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"); + 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 { + 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; + } + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_readonly_transactions.js b/dom/indexedDB/test/unit/test_readonly_transactions.js new file mode 100644 index 000000000..91f0f4c9a --- /dev/null +++ b/dom/indexedDB/test/unit/test_readonly_transactions.js @@ -0,0 +1,174 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const osName = "foo"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + db.createObjectStore(osName, { autoIncrement: "true" }); + + yield undefined; + + let key1, key2; + + request = db.transaction([osName], "readwrite") + .objectStore(osName) + .add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + key1 = event.target.result; + testGenerator.next(); + } + yield undefined; + + request = db.transaction(osName, "readwrite").objectStore(osName).add({}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + key2 = event.target.result; + testGenerator.next(); + } + yield undefined; + + request = db.transaction([osName], "readwrite") + .objectStore(osName) + .put({}, key1); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + request = db.transaction(osName, "readwrite") + .objectStore(osName) + .put({}, key2); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + request = db.transaction([osName], "readwrite") + .objectStore(osName) + .put({}, key1); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + request = db.transaction(osName, "readwrite") + .objectStore(osName) + .put({}, key1); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + request = db.transaction([osName], "readwrite") + .objectStore(osName) + .delete(key1); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + request = db.transaction(osName, "readwrite") + .objectStore(osName) + .delete(key2); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readwrite", "Correct mode"); + testGenerator.next(); + } + yield undefined; + + try { + request = db.transaction([osName]).objectStore(osName).add({}); + ok(false, "Adding to a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Adding to a readonly transaction failed"); + } + + try { + request = db.transaction(osName).objectStore(osName).add({}); + ok(false, "Adding to a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Adding to a readonly transaction failed"); + } + + try { + request = db.transaction([osName]).objectStore(osName).put({}); + ok(false, "Adding or modifying a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Adding or modifying a readonly transaction failed"); + } + + try { + request = db.transaction(osName).objectStore(osName).put({}); + ok(false, "Adding or modifying a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Adding or modifying a readonly transaction failed"); + } + + try { + request = db.transaction([osName]).objectStore(osName).put({}, key1); + ok(false, "Modifying a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Modifying a readonly transaction failed"); + } + + try { + request = db.transaction(osName).objectStore(osName).put({}, key1); + ok(false, "Modifying a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Modifying a readonly transaction failed"); + } + + try { + request = db.transaction([osName]).objectStore(osName).delete(key1); + ok(false, "Removing from a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Removing from a readonly transaction failed"); + } + + try { + request = db.transaction(osName).objectStore(osName).delete(key2); + ok(false, "Removing from a readonly transaction should fail!"); + } + catch (e) { + ok(true, "Removing from a readonly transaction failed"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_readwriteflush_disabled.js b/dom/indexedDB/test/unit/test_readwriteflush_disabled.js new file mode 100644 index 000000000..484b1aa42 --- /dev/null +++ b/dom/indexedDB/test/unit/test_readwriteflush_disabled.js @@ -0,0 +1,72 @@ +/** + * 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 name = + this.window ? window.location.pathname : "test_readwriteflush_disabled.js"; + + info("Resetting experimental pref"); + + if (this.window) { + SpecialPowers.pushPrefEnv( + { + "set": [ + ["dom.indexedDB.experimental", false] + ] + }, + continueToNextStep + ); + yield undefined; + } else { + resetExperimental(); + } + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(name); + + yield undefined; + + // success + let db = request.result; + + info("Attempting to create a 'readwriteflush' transaction"); + + let exception; + + try { + let transaction = db.transaction(name, "readwriteflush"); + } catch (e) { + exception = e; + } + + ok(exception, "'readwriteflush' transaction threw"); + ok(exception instanceof Error, "exception is an Error object"); + is(exception.message, + "Argument 2 of IDBDatabase.transaction 'readwriteflush' is not a valid " + + "value for enumeration IDBTransactionMode.", + "exception has the correct message"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_remove_index.js b/dom/indexedDB/test/unit/test_remove_index.js new file mode 100644 index 000000000..d9916b975 --- /dev/null +++ b/dom/indexedDB/test/unit/test_remove_index.js @@ -0,0 +1,58 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const indexName = "My Test Index"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore("test store", { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore.name, "Correct name"); + + is(objectStore.indexNames.length, 0, "Correct indexNames list"); + + let index = objectStore.createIndex(indexName, "foo"); + + is(objectStore.indexNames.length, 1, "Correct indexNames list"); + is(objectStore.indexNames.item(0), indexName, "Correct name"); + is(objectStore.index(indexName), index, "Correct instance"); + + objectStore.deleteIndex(indexName); + + is(objectStore.indexNames.length, 0, "Correct indexNames list"); + try { + objectStore.index(indexName); + ok(false, "should have thrown"); + } + catch(ex) { + ok(ex instanceof DOMException, "Got a DOMException"); + is(ex.name, "NotFoundError", "expect a NotFoundError"); + is(ex.code, DOMException.NOT_FOUND_ERR, "expect a NOT_FOUND_ERR"); + } + + let index2 = objectStore.createIndex(indexName, "foo"); + isnot(index, index2, "New instance should be created"); + + is(objectStore.indexNames.length, 1, "Correct recreacted indexNames list"); + is(objectStore.indexNames.item(0), indexName, "Correct recreacted name"); + is(objectStore.index(indexName), index2, "Correct instance"); + + event.target.transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_remove_objectStore.js b/dom/indexedDB/test/unit/test_remove_objectStore.js new file mode 100644 index 000000000..324fa0d2f --- /dev/null +++ b/dom/indexedDB/test/unit/test_remove_objectStore.js @@ -0,0 +1,129 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const objectStoreName = "Objects"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + + let db = event.target.result; + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore(objectStoreName, + { keyPath: "foo" }); + + let addedCount = 0; + + for (let i = 0; i < 100; i++) { + request = objectStore.add({foo: i}); + request.onerror = errorHandler; + request.onsuccess = function(event) { + if (++addedCount == 100) { + executeSoon(function() { testGenerator.next(); }); + } + } + } + yield undefined; + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStoreName, "Correct name"); + + event.target.transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + // Wait for success. + event = yield undefined; + + db.close(); + + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + + db = event.target.result; + let trans = event.target.transaction; + + let oldObjectStore = trans.objectStore(objectStoreName); + isnot(oldObjectStore, null, "Correct object store prior to deleting"); + db.deleteObjectStore(objectStoreName); + is(db.objectStoreNames.length, 0, "Correct objectStores list"); + try { + trans.objectStore(objectStoreName); + ok(false, "should have thrown"); + } + catch(ex) { + ok(ex instanceof DOMException, "Got a DOMException"); + is(ex.name, "NotFoundError", "expect a NotFoundError"); + is(ex.code, DOMException.NOT_FOUND_ERR, "expect a NOT_FOUND_ERR"); + } + + objectStore = db.createObjectStore(objectStoreName, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStoreName, "Correct name"); + is(trans.objectStore(objectStoreName), objectStore, "Correct new objectStore"); + isnot(oldObjectStore, objectStore, "Old objectStore is not new objectStore"); + + request = objectStore.openCursor(); + request.onerror = errorHandler; + request.onsuccess = function(event) { + is(event.target.result, null, "ObjectStore shouldn't have any items"); + testGenerator.send(event); + } + event = yield undefined; + + db.deleteObjectStore(objectStore.name); + is(db.objectStoreNames.length, 0, "Correct objectStores list"); + + continueToNextStep(); + yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + // Wait for success. + event = yield undefined; + + db.close(); + + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + trans = event.target.transaction; + + objectStore = db.createObjectStore(objectStoreName, { keyPath: "foo" }); + + request = objectStore.add({foo:"bar"}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + db.deleteObjectStore(objectStoreName); + + event = yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_rename_index.js b/dom/indexedDB/test/unit/test_rename_index.js new file mode 100644 index 000000000..e7ab8c038 --- /dev/null +++ b/dom/indexedDB/test/unit/test_rename_index.js @@ -0,0 +1,193 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName = "test store"; + const indexName_ToBeDeleted = "test index to be deleted"; + const indexName_v0 = "test index v0"; + const indexName_v1 = "test index v1"; + const indexName_v2 = "test index v2"; + const indexName_v3 = indexName_ToBeDeleted; + const indexName_v4 = "test index v4"; + + info("Rename in v1."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore(storeName, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore.name, "Correct object store name"); + + // create index to be deleted later in v3. + objectStore.createIndex(indexName_ToBeDeleted, "foo"); + ok(objectStore.index(indexName_ToBeDeleted), "Index created."); + + // create target index to be renamed. + let index = objectStore.createIndex(indexName_v0, "bar"); + ok(objectStore.index(indexName_v0), "Index created."); + is(index.name, indexName_v0, "Correct index name"); + index.name = indexName_v1; + is(index.name, indexName_v1, "Renamed index successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v1 and run renaming in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName); + + // indexName_v0 created in v1 shall not be available. + try { + index = objectStore.index(indexName_v0); + ok(false, "NotFoundError shall be thrown."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "NotFoundError", "correct error"); + } + + // rename to "v2". + index = objectStore.index(indexName_v1); + is(index.name, indexName_v1, "Correct index name") + index.name = indexName_v2; + is(index.name, indexName_v2, "Renamed index successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + txn = db.transaction(storeName); + + objectStore = txn.objectStore(storeName); + index = objectStore.index(indexName_v2); + is(index.name, indexName_v2, "Correct index name"); + + db.close(); + + info("Rename in v3."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName); + ok(objectStore.index(indexName_ToBeDeleted), "index is valid."); + objectStore.deleteIndex(indexName_ToBeDeleted); + try { + objectStore.index(indexName_ToBeDeleted); + ok(false, "NotFoundError shall be thrown if the index name is deleted."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "NotFoundError", "correct error"); + } + + info("Rename with the name of the deleted index."); + index = objectStore.index(indexName_v2); + index.name = indexName_v3; + is(index.name, indexName_v3, "Renamed index successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v3."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + txn = db.transaction(storeName); + + objectStore = txn.objectStore(storeName); + index = objectStore.index(indexName_v3); + is(index.name, indexName_v3, "Correct index name"); + + db.close(); + + info("Abort the version change transaction while renaming index."); + request = indexedDB.open(name, 4); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName); + index = objectStore.index(indexName_v3); + index.name = indexName_v4; + is(index.name, indexName_v4, "Renamed successfully"); + let putRequest = objectStore.put({ foo: "fooValue", bar: "barValue" }); + putRequest.onsuccess = continueToNextStepSync; + yield undefined; + + // Aborting the transaction. + request.onerror = expectedErrorHandler("AbortError"); + txn.abort(); + yield undefined; + + // Verify if the name of the index handle is reverted. + is(index.name, indexName_v3, "The name is reverted after aborted."); + + info("Verify if the objectstore name is unchanged."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + txn = db.transaction(storeName); + + objectStore = txn.objectStore(storeName); + index = objectStore.index(indexName_v3); + is(index.name, indexName_v3, "Correct index name"); + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_rename_index_errors.js b/dom/indexedDB/test/unit/test_rename_index_errors.js new file mode 100644 index 000000000..cac972a1f --- /dev/null +++ b/dom/indexedDB/test/unit/test_rename_index_errors.js @@ -0,0 +1,129 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName = "test store"; + const indexName1 = "test index 1"; + const indexName2 = "test index 2"; + + info("Setup test indexes."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore = db.createObjectStore(storeName, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore.name, "Correct name"); + + let index1 = objectStore.createIndex(indexName1, "bar"); + is(objectStore.index(indexName1).name, index1.name, "Correct index name"); + is(index1.name, indexName1, "Correct index name"); + let index2 = objectStore.createIndex(indexName2, "baz"); + is(objectStore.index(indexName2).name, index2.name, "Correct index name"); + is(index2.name, indexName2, "Correct index name"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify IDB Errors in version 2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName); + index1 = objectStore.index(indexName1); + index2 = objectStore.index(indexName2); + is(index1.name, indexName1, "Correct index name"); + is(index2.name, indexName2, "Correct index name"); + + // Rename with the name already adopted by the other index. + try { + index1.name = indexName2; + ok(false, "ConstraintError shall be thrown if the index name already exists."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "ConstraintError", "correct error"); + } + + // Rename with identical name. + try { + index1.name = indexName1; + ok(true, "It shall be fine to set the same name."); + } catch (e) { + ok(false, "Got a database exception: " + e.name); + } + + objectStore.deleteIndex(indexName2); + + // Rename after deleted. + try { + index2.name = indexName2; + ok(false, "InvalidStateError shall be thrown if deleted."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "InvalidStateError", "correct error"); + } + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + // Rename when the transaction is inactive. + try { + index1.name = indexName1; + ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "correct error"); + } + + info("Rename when the transaction is not an upgrade one."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + txn = db.transaction(storeName); + objectStore = txn.objectStore(storeName); + index1 = objectStore.index(indexName1); + + try { + index1.name = indexName1; + ok(false, "InvalidStateError shall be thrown if it's not an upgrade transaction."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "InvalidStateError", "correct error"); + } + + txn.oncomplete = continueToNextStepSync; + yield undefined; + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_rename_objectStore.js b/dom/indexedDB/test/unit/test_rename_objectStore.js new file mode 100644 index 000000000..4c7796889 --- /dev/null +++ b/dom/indexedDB/test/unit/test_rename_objectStore.js @@ -0,0 +1,171 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName_ToBeDeleted = "test store to be deleted"; + const storeName_v0 = "test store v0"; + const storeName_v1 = "test store v1"; + const storeName_v2 = "test store v2"; + const storeName_v3 = storeName_ToBeDeleted; + const storeName_v4 = "test store v4"; + + info("Rename in v1."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + // create objectstore to be deleted later in v3. + db.createObjectStore(storeName_ToBeDeleted, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name"); + + // create target objectstore to be renamed. + let objectStore = db.createObjectStore(storeName_v0, { keyPath: "bar" }); + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(objectStore.name), "Correct name"); + + objectStore.name = storeName_v1; + is(objectStore.name, storeName_v1, "Renamed successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v1 and run renaming in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v1), "Correct name"); + ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name"); + + objectStore = txn.objectStore(storeName_v1); + objectStore.name = storeName_v2; + is(objectStore.name, storeName_v2, "Renamed successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v2), "Correct name"); + ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name"); + + db.close(); + + info("Rename in v3."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v2), "Correct name"); + ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name"); + db.deleteObjectStore(storeName_ToBeDeleted); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v2) && + !db.objectStoreNames.contains(storeName_ToBeDeleted), "Deleted correctly"); + + objectStore = txn.objectStore(storeName_v2); + objectStore.name = storeName_v3; + is(objectStore.name, storeName_v3, "Renamed successfully"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify renaming done in v3."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v3), "Correct name"); + + db.close(); + + info("Abort the version change transaction while renaming objectstore."); + request = indexedDB.open(name, 4); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + objectStore = txn.objectStore(storeName_v3); + objectStore.name = storeName_v4; + is(objectStore.name, storeName_v4, "Renamed successfully"); + let putRequest = objectStore.put({ bar: "barValue" }); + putRequest.onsuccess = continueToNextStepSync; + yield undefined; + + // Aborting the transaction. + request.onerror = expectedErrorHandler("AbortError"); + txn.abort(); + yield undefined; + + // Verify if the name of the objectStore handle is reverted. + is(objectStore.name, storeName_v3, "The name is reverted after aborted."); + + info("Verify if the objectstore name is unchanged."); + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + ok(db.objectStoreNames.contains(storeName_v3), "Correct name"); + + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_rename_objectStore_errors.js b/dom/indexedDB/test/unit/test_rename_objectStore_errors.js new file mode 100644 index 000000000..0e4b796a6 --- /dev/null +++ b/dom/indexedDB/test/unit/test_rename_objectStore_errors.js @@ -0,0 +1,127 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + const storeName1 = "test store 1"; + const storeName2 = "test store 2"; + + info("Setup test object stores."); + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + let txn = event.target.transaction; + + is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); + + let objectStore1 = db.createObjectStore(storeName1, { keyPath: "foo" }); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(0), objectStore1.name, "Correct name"); + is(objectStore1.name, storeName1, "Correct name"); + + let objectStore2 = db.createObjectStore(storeName2, { keyPath: "bar" }); + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + is(db.objectStoreNames.item(1), objectStore2.name, "Correct name"); + is(objectStore2.name, storeName2, "Correct name"); + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Verify IDB Errors in version 2."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = continueToNextStep; + event = yield undefined; + + db = event.target.result; + txn = event.target.transaction; + + is(db.objectStoreNames.length, 2, "Correct objectStoreNames list"); + + objectStore1 = txn.objectStore(storeName1); + objectStore2 = txn.objectStore(storeName2); + is(objectStore1.name, storeName1, "Correct name"); + is(objectStore2.name, storeName2, "Correct name"); + + // Rename with the name already adopted by the other object store. + try { + objectStore1.name = storeName2; + ok(false, "ConstraintError shall be thrown if the store name already exists."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "ConstraintError", "correct error"); + } + + // Rename with the identical name. + try { + objectStore1.name = storeName1; + ok(true, "It shall be fine to set the same name."); + } catch (e) { + ok(false, "Got a database exception: " + e.name); + } + + db.deleteObjectStore(storeName2); + + // Rename after deleted. + try { + objectStore2.name = storeName2; + ok(false, "InvalidStateError shall be thrown if deleted."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "InvalidStateError", "correct error"); + } + + txn.oncomplete = continueToNextStepSync; + yield undefined; + request.onsuccess = continueToNextStep; + yield undefined; + db.close(); + + info("Rename when the transaction is inactive."); + try { + objectStore1.name = storeName1; + ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TransactionInactiveError", "correct error"); + } + + info("Rename when the transaction is not an upgrade one."); + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db = event.target.result; + txn = db.transaction(storeName1); + objectStore1 = txn.objectStore(storeName1); + + try { + objectStore1.name = storeName1; + ok(false, "InvalidStateError shall be thrown if it's not an upgrade transaction."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "InvalidStateError", "correct error"); + } + + txn.oncomplete = continueToNextStepSync; + yield undefined; + db.close(); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_request_readyState.js b/dom/indexedDB/test/unit/test_request_readyState.js new file mode 100644 index 000000000..c19d974a3 --- /dev/null +++ b/dom/indexedDB/test/unit/test_request_readyState.js @@ -0,0 +1,51 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + is(request.readyState, "pending", "Correct readyState"); + + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(request.readyState, "done", "Correct readyState"); + + let db = event.target.result; + + let objectStore = db.createObjectStore("foo"); + let key = 10; + + request = objectStore.add({}, key); + is(request.readyState, "pending", "Correct readyState"); + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(request.readyState, "done", "Correct readyState"); + is(event.target.result, key, "Correct key"); + + request = objectStore.get(key); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + is(request.readyState, "pending", "Correct readyState"); + event = yield undefined; + + ok(event.target.result, "Got something"); + is(request.readyState, "done", "Correct readyState"); + + // Wait for success + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_sandbox.js b/dom/indexedDB/test/unit/test_sandbox.js new file mode 100644 index 000000000..bcd4cad51 --- /dev/null +++ b/dom/indexedDB/test/unit/test_sandbox.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function exerciseInterface() { + function DB(name, store) { + this.name = name; + this.store = store; + this._db = this._create(); + } + + DB.prototype = { + _create: function() { + var op = indexedDB.open(this.name); + op.onupgradeneeded = e => { + var db = e.target.result; + db.createObjectStore(this.store); + }; + return new Promise(resolve => { + op.onsuccess = e => resolve(e.target.result); + }); + }, + + _result: function(tx, op) { + return new Promise((resolve, reject) => { + op.onsuccess = e => resolve(e.target.result); + op.onerror = () => reject(op.error); + tx.onabort = () => reject(tx.error); + }); + }, + + get: function(k) { + return this._db.then(db => { + var tx = db.transaction(this.store, 'readonly'); + var store = tx.objectStore(this.store); + return this._result(tx, store.get(k)); + }); + }, + + add: function(k, v) { + return this._db.then(db => { + var tx = db.transaction(this.store, 'readwrite'); + var store = tx.objectStore(this.store); + return this._result(tx, store.add(v, k)); + }); + } + }; + + var db = new DB('data', 'base'); + return db.add('x', [ 10, {} ]) + .then(_ => db.get('x')) + .then(x => { + equal(x.length, 2); + equal(x[0], 10); + equal(typeof x[1], 'object'); + equal(Object.keys(x[1]).length, 0); + }); +} + +function run_test() { + do_get_profile(); + + let Cu = Components.utils; + let sb = new Cu.Sandbox('https://www.example.com', + { wantGlobalProperties: ['indexedDB'] }); + + sb.equal = equal; + var innerPromise = new Promise((resolve, reject) => { + sb.test_done = resolve; + sb.test_error = reject; + }); + Cu.evalInSandbox('(' + exerciseInterface.toSource() + ')()' + + '.then(test_done, test_error);', sb); + + Cu.importGlobalProperties(['indexedDB']); + do_test_pending(); + Promise.all([innerPromise, exerciseInterface()]) + .then(do_test_finished); +} diff --git a/dom/indexedDB/test/unit/test_schema18upgrade.js b/dom/indexedDB/test/unit/test_schema18upgrade.js new file mode 100644 index 000000000..f2069018f --- /dev/null +++ b/dom/indexedDB/test/unit/test_schema18upgrade.js @@ -0,0 +1,336 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const testName = "schema18upgrade"; + const testKeys = [ + -1/0, + -1.7e308, + -10000, + -2, + -1.5, + -1, + -1.00001e-200, + -1e-200, + 0, + 1e-200, + 1.00001e-200, + 1, + 2, + 10000, + 1.7e308, + 1/0, + new Date("1750-01-02"), + new Date("1800-12-31T12:34:56.001Z"), + new Date(-1000), + new Date(-10), + new Date(-1), + new Date(0), + new Date(1), + new Date(2), + new Date(1000), + new Date("1971-01-01"), + new Date("1971-01-01T01:01:01Z"), + new Date("1971-01-01T01:01:01.001Z"), + new Date("1971-01-01T01:01:01.01Z"), + new Date("1971-01-01T01:01:01.1Z"), + new Date("1980-02-02"), + new Date("3333-03-19T03:33:33.333Z"), + "", + "\x00", + "\x00\x00", + "\x00\x01", + "\x01", + "\x02", + "\x03", + "\x04", + "\x07", + "\x08", + "\x0F", + "\x10", + "\x1F", + "\x20", + "01234", + "\x3F", + "\x40", + "A", + "A\x00", + "A1", + "ZZZZ", + "a", + "a\x00", + "aa", + "azz", + "}", + "\x7E", + "\x7F", + "\x80", + "\xFF", + "\u0100", + "\u01FF", + "\u0200", + "\u03FF", + "\u0400", + "\u07FF", + "\u0800", + "\u0FFF", + "\u1000", + "\u1FFF", + "\u2000", + "\u3FFF", + "\u4000", + "\u7FFF", + "\u8000", + "\uD800", + "\uD800a", + "\uD800\uDC01", + "\uDBFF", + "\uDC00", + "\uDFFF\uD800", + "\uFFFE", + "\uFFFF", + "\uFFFF\x00", + "\uFFFFZZZ", + [], + [-1/0], + [-1], + [0], + [1], + [1, "a"], + [1, []], + [1, [""]], + [2, 3], + [2, 3.0000000000001], + [12, [[]]], + [12, [[[]]]], + [12, [[[""]]]], + [12, [[["foo"]]]], + [12, [[[[[3]]]]]], + [12, [[[[[[3]]]]]]], + [12, [[[[[[3],[[[[[4.2]]]]]]]]]]], + [new Date(-1)], + [new Date(1)], + [""], + ["", [[]]], + ["", [[[]]]], + ["abc"], + ["abc", "def"], + ["abc\x00"], + ["abc\x00", "\x00\x01"], + ["abc\x00", "\x00def"], + ["abc\x00\x00def"], + ["x", [[]]], + ["x", [[[]]]], + [[]], + [[],"foo"], + [[],[]], + [[[]]], + [[[]], []], + [[[]], [[]]], + [[[]], [[1]]], + [[[]], [[[]]]], + [[[1]]], + [[[[]], []]], + ]; + const testString = + "abcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()-_+=,<.>/?\\|"; + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + info("Installing profile"); + + installPackagedProfile(testName + "_profile"); + + info("Opening database with no version"); + + let request = indexedDB.open(testName); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + is(db.version, 1, "Correct db version"); + + let transaction = db.transaction(testName); + transaction.oncomplete = grabEventAndContinueHandler; + + let objectStore = transaction.objectStore(testName); + let index = objectStore.index("uniqueIndex"); + + info("Starting 'uniqueIndex' cursor"); + + let keyIndex = 0; + index.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + info("Comparing " + JSON.stringify(cursor.primaryKey) + " to " + + JSON.stringify(testKeys[cursor.key]) + + " [" + cursor.key + "]"); + is(indexedDB.cmp(cursor.primaryKey, testKeys[cursor.key]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.primaryKey, testKeys[cursor.key]), true, + "Keys compare equally via 'compareKeys'"); + + let indexProperty = cursor.value.index; + is(Array.isArray(indexProperty), true, "index property is Array"); + is(indexProperty[0], cursor.key, "index property first item correct"); + is(indexProperty[1], cursor.key + 1, "index property second item correct"); + + is(cursor.key, keyIndex, "Cursor key property is correct"); + + is(cursor.value.testString, testString, "Test string compared equally"); + + keyIndex++; + cursor.continue(); + } + }; + yield undefined; + + is(keyIndex, testKeys.length, "Saw all keys"); + + transaction = db.transaction(testName, "readwrite"); + transaction.oncomplete = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(testName); + index = objectStore.index("index"); + + info("Getting all 'index' keys"); + + index.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, testKeys.length * 2, "Got all keys"); + + info("Starting objectStore cursor"); + + objectStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + let value = cursor.value; + is(value.testString, testString, "Test string compared equally"); + + delete value.index; + cursor.update(value); + + cursor.continue(); + } else { + continueToNextStepSync(); + } + }; + yield undefined; + + info("Getting all 'index' keys"); + + index.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, 0, "Removed all keys"); + yield undefined; + + db.close(); + + info("Opening database with new version"); + + request = indexedDB.open(testName, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Deleting indexes"); + + objectStore = event.target.transaction.objectStore(testName); + objectStore.deleteIndex("index"); + objectStore.deleteIndex("uniqueIndex"); + + event = yield undefined; + + db = event.target.result; + + transaction = db.transaction(testName, "readwrite"); + transaction.oncomplete = grabEventAndContinueHandler; + + info("Starting objectStore cursor"); + + objectStore = transaction.objectStore(testName); + objectStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + let value = cursor.value; + is(value.testString, testString, "Test string compared equally"); + + value.index = value.keyPath; + cursor.update(value); + + cursor.continue(); + } + }; + event = yield undefined; + + db.close(); + + info("Opening database with new version"); + + request = indexedDB.open(testName, 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Creating indexes"); + + objectStore = event.target.transaction.objectStore(testName); + objectStore.createIndex("index", "index"); + + event = yield undefined; + + db = event.target.result; + + transaction = db.transaction(testName); + transaction.oncomplete = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(testName); + index = objectStore.index("index"); + + info("Starting 'index' cursor"); + + keyIndex = 0; + index.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + is(indexedDB.cmp(cursor.primaryKey, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.primaryKey, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + is(indexedDB.cmp(cursor.key, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.key, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + + let indexProperty = cursor.value.index; + is(indexedDB.cmp(indexProperty, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(indexProperty, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + + is(cursor.value.testString, testString, "Test string compared equally"); + + keyIndex++; + cursor.continue(); + } + }; + yield undefined; + + is(keyIndex, testKeys.length, "Added all keys again"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_schema21upgrade.js b/dom/indexedDB/test/unit/test_schema21upgrade.js new file mode 100644 index 000000000..2cd6e45b2 --- /dev/null +++ b/dom/indexedDB/test/unit/test_schema21upgrade.js @@ -0,0 +1,336 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const testName = "schema21upgrade"; + const testKeys = [ + -1/0, + -1.7e308, + -10000, + -2, + -1.5, + -1, + -1.00001e-200, + -1e-200, + 0, + 1e-200, + 1.00001e-200, + 1, + 2, + 10000, + 1.7e308, + 1/0, + new Date("1750-01-02"), + new Date("1800-12-31T12:34:56.001Z"), + new Date(-1000), + new Date(-10), + new Date(-1), + new Date(0), + new Date(1), + new Date(2), + new Date(1000), + new Date("1971-01-01"), + new Date("1971-01-01T01:01:01Z"), + new Date("1971-01-01T01:01:01.001Z"), + new Date("1971-01-01T01:01:01.01Z"), + new Date("1971-01-01T01:01:01.1Z"), + new Date("1980-02-02"), + new Date("3333-03-19T03:33:33.333Z"), + "", + "\x00", + "\x00\x00", + "\x00\x01", + "\x01", + "\x02", + "\x03", + "\x04", + "\x07", + "\x08", + "\x0F", + "\x10", + "\x1F", + "\x20", + "01234", + "\x3F", + "\x40", + "A", + "A\x00", + "A1", + "ZZZZ", + "a", + "a\x00", + "aa", + "azz", + "}", + "\x7E", + "\x7F", + "\x80", + "\xFF", + "\u0100", + "\u01FF", + "\u0200", + "\u03FF", + "\u0400", + "\u07FF", + "\u0800", + "\u0FFF", + "\u1000", + "\u1FFF", + "\u2000", + "\u3FFF", + "\u4000", + "\u7FFF", + "\u8000", + "\uD800", + "\uD800a", + "\uD800\uDC01", + "\uDBFF", + "\uDC00", + "\uDFFF\uD800", + "\uFFFE", + "\uFFFF", + "\uFFFF\x00", + "\uFFFFZZZ", + [], + [-1/0], + [-1], + [0], + [1], + [1, "a"], + [1, []], + [1, [""]], + [2, 3], + [2, 3.0000000000001], + [12, [[]]], + [12, [[[]]]], + [12, [[[""]]]], + [12, [[["foo"]]]], + [12, [[[[[3]]]]]], + [12, [[[[[[3]]]]]]], + [12, [[[[[[3],[[[[[4.2]]]]]]]]]]], + [new Date(-1)], + [new Date(1)], + [""], + ["", [[]]], + ["", [[[]]]], + ["abc"], + ["abc", "def"], + ["abc\x00"], + ["abc\x00", "\x00\x01"], + ["abc\x00", "\x00def"], + ["abc\x00\x00def"], + ["x", [[]]], + ["x", [[[]]]], + [[]], + [[],"foo"], + [[],[]], + [[[]]], + [[[]], []], + [[[]], [[]]], + [[[]], [[1]]], + [[[]], [[[]]]], + [[[1]]], + [[[[]], []]], + ]; + const testString = + "abcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()-_+=,<.>/?\\|"; + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + info("Installing profile"); + + installPackagedProfile(testName + "_profile"); + + info("Opening database with no version"); + + let request = indexedDB.open(testName); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + is(db.version, 1, "Correct db version"); + + let transaction = db.transaction(testName); + transaction.oncomplete = grabEventAndContinueHandler; + + let objectStore = transaction.objectStore(testName); + let index = objectStore.index("uniqueIndex"); + + info("Starting 'uniqueIndex' cursor"); + + let keyIndex = 0; + index.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + info("Comparing " + JSON.stringify(cursor.primaryKey) + " to " + + JSON.stringify(testKeys[cursor.key]) + + " [" + cursor.key + "]"); + is(indexedDB.cmp(cursor.primaryKey, testKeys[cursor.key]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.primaryKey, testKeys[cursor.key]), true, + "Keys compare equally via 'compareKeys'"); + + let indexProperty = cursor.value.index; + is(Array.isArray(indexProperty), true, "index property is Array"); + is(indexProperty[0], cursor.key, "index property first item correct"); + is(indexProperty[1], cursor.key + 1, "index property second item correct"); + + is(cursor.key, keyIndex, "Cursor key property is correct"); + + is(cursor.value.testString, testString, "Test string compared equally"); + + keyIndex++; + cursor.continue(); + } + }; + yield undefined; + + is(keyIndex, testKeys.length, "Saw all keys"); + + transaction = db.transaction(testName, "readwrite"); + transaction.oncomplete = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(testName); + index = objectStore.index("index"); + + info("Getting all 'index' keys"); + + index.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, testKeys.length * 2, "Got all keys"); + + info("Starting objectStore cursor"); + + objectStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + let value = cursor.value; + is(value.testString, testString, "Test string compared equally"); + + delete value.index; + cursor.update(value); + + cursor.continue(); + } else { + continueToNextStepSync(); + } + }; + yield undefined; + + info("Getting all 'index' keys"); + + index.getAllKeys().onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result.length, 0, "Removed all keys"); + yield undefined; + + db.close(); + + info("Opening database with new version"); + + request = indexedDB.open(testName, 2); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Deleting indexes"); + + objectStore = event.target.transaction.objectStore(testName); + objectStore.deleteIndex("index"); + objectStore.deleteIndex("uniqueIndex"); + + event = yield undefined; + + db = event.target.result; + + transaction = db.transaction(testName, "readwrite"); + transaction.oncomplete = grabEventAndContinueHandler; + + info("Starting objectStore cursor"); + + objectStore = transaction.objectStore(testName); + objectStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + let value = cursor.value; + is(value.testString, testString, "Test string compared equally"); + + value.index = value.keyPath; + cursor.update(value); + + cursor.continue(); + } + }; + event = yield undefined; + + db.close(); + + info("Opening database with new version"); + + request = indexedDB.open(testName, 3); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + info("Creating indexes"); + + objectStore = event.target.transaction.objectStore(testName); + objectStore.createIndex("index", "index"); + + event = yield undefined; + + db = event.target.result; + + transaction = db.transaction(testName); + transaction.oncomplete = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(testName); + index = objectStore.index("index"); + + info("Starting 'index' cursor"); + + keyIndex = 0; + index.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + is(indexedDB.cmp(cursor.primaryKey, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.primaryKey, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + is(indexedDB.cmp(cursor.key, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(cursor.key, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + + let indexProperty = cursor.value.index; + is(indexedDB.cmp(indexProperty, testKeys[keyIndex]), 0, + "Keys compare equally via 'indexedDB.cmp'"); + is(compareKeys(indexProperty, testKeys[keyIndex]), true, + "Keys compare equally via 'compareKeys'"); + + is(cursor.value.testString, testString, "Test string compared equally"); + + keyIndex++; + cursor.continue(); + } + }; + yield undefined; + + is(keyIndex, testKeys.length, "Added all keys again"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_schema23upgrade.js b/dom/indexedDB/test/unit/test_schema23upgrade.js new file mode 100644 index 000000000..47c9c2986 --- /dev/null +++ b/dom/indexedDB/test/unit/test_schema23upgrade.js @@ -0,0 +1,66 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/default/http+++www.mozilla.org + { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }, + + // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net + { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net", + dbName: "dbN", dbVersion: 1 }, + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let uri = ios.newURI(params.url, null, null); + let principal = + ssm.createCodebasePrincipal(uri, + {appId: params.appId || ssm.NO_APPID, + inIsolatedMozBrowser: params.inIsolatedMozBrowser}); + let request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbVersion); + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("schema23upgrade_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_setVersion.js b/dom/indexedDB/test/unit/test_setVersion.js new file mode 100644 index 000000000..cb2400b5d --- /dev/null +++ b/dom/indexedDB/test/unit/test_setVersion.js @@ -0,0 +1,51 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.close(); + + // Check default state. + is(db.version, 1, "Correct default version for a new database."); + + const versions = [ + 7, + 42, + ]; + + for (let i = 0; i < versions.length; i++) { + let version = versions[i]; + + let request = indexedDB.open(name, version); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + is(db.version, version, "Database version number updated correctly"); + is(event.target.transaction.mode, "versionchange", "Correct mode"); + + // Wait for success + yield undefined; + + db.close(); + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_setVersion_abort.js b/dom/indexedDB/test/unit/test_setVersion_abort.js new file mode 100644 index 000000000..262c10346 --- /dev/null +++ b/dom/indexedDB/test/unit/test_setVersion_abort.js @@ -0,0 +1,97 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + let objectStore = db.createObjectStore("foo"); + let index = objectStore.createIndex("bar", "baz"); + + is(db.version, 1, "Correct version"); + is(db.objectStoreNames.length, 1, "Correct objectStoreNames length"); + is(objectStore.indexNames.length, 1, "Correct indexNames length"); + + let transaction = event.target.transaction; + is(transaction.mode, "versionchange", "Correct transaction mode"); + transaction.oncomplete = unexpectedSuccessHandler; + transaction.onabort = grabEventAndContinueHandler; + transaction.abort(); + + is(db.version, 0, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + is(objectStore.indexNames.length, 0, "Correct indexNames length"); + + // Test that the db is actually closed. + try { + db.transaction(""); + ok(false, "Expect an exception"); + } catch (e) { + ok(true, "Expect an exception"); + is(e.name, "InvalidStateError", "Expect an InvalidStateError"); + } + + event = yield undefined; + is(event.type, "abort", "Got transaction abort event"); + is(event.target, transaction, "Right target"); + + is(db.version, 0, "Correct version"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length"); + is(objectStore.indexNames.length, 0, "Correct indexNames length"); + + request.onerror = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + + event = yield undefined; + + is(event.type, "error", "Got request error event"); + is(event.target, request, "Right target"); + is(event.target.transaction, null, "No transaction"); + + event.preventDefault(); + + request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + let db2 = event.target.result; + + isnot(db, db2, "Should give a different db instance"); + is(db2.version, 1, "Correct version"); + is(db2.objectStoreNames.length, 0, "Correct objectStoreNames length"); + + let objectStore2 = db2.createObjectStore("foo"); + let index2 = objectStore2.createIndex("bar", "baz"); + + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + event = yield undefined; + + is(event.target.result, db2, "Correct target"); + is(event.type, "success", "Got success event"); + is(db2.version, 1, "Correct version"); + is(db2.objectStoreNames.length, 1, "Correct objectStoreNames length"); + is(objectStore2.indexNames.length, 1, "Correct indexNames length"); + is(db.version, 0, "Correct version still"); + is(db.objectStoreNames.length, 0, "Correct objectStoreNames length still"); + is(objectStore.indexNames.length, 0, "Correct indexNames length still"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_setVersion_events.js b/dom/indexedDB/test/unit/test_setVersion_events.js new file mode 100644 index 000000000..1bd757dc8 --- /dev/null +++ b/dom/indexedDB/test/unit/test_setVersion_events.js @@ -0,0 +1,165 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + // Open a datbase for the first time. + let request = indexedDB.open(name, 1); + + // Sanity checks + ok(request instanceof IDBRequest, "Request should be an IDBRequest"); + ok(request instanceof IDBOpenDBRequest, "Request should be an IDBOpenDBRequest"); + ok(request instanceof EventTarget, "Request should be an EventTarget"); + is(request.source, null, "Request should have no source"); + try { + request.result; + ok(false, "Getter should have thrown!"); + } catch (e if e.result == 0x8053000b /* NS_ERROR_DOM_INVALID_STATE_ERR */) { + ok(true, "Getter threw the right exception"); + } + + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let versionChangeEventCount = 0; + let db1, db2, db3; + + db1 = event.target.result; + db1.addEventListener("versionchange", function(event) { + ok(true, "Got version change event"); + ok(event instanceof IDBVersionChangeEvent, "Event is of the right type"); + is("source" in event.target, false, "Correct source"); + is(event.target, db1, "Correct target"); + is(event.target.version, 1, "Correct db version"); + is(event.oldVersion, 1, "Correct event oldVersion"); + is(event.newVersion, 2, "Correct event newVersion"); + is(versionChangeEventCount++, 0, "Correct count"); + db1.close(); + }, false); + + // Open the database again and trigger an upgrade that should succeed + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onsuccess = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onblocked = errorHandler; + + event = yield undefined; + + // Test the upgradeneeded event. + ok(event instanceof IDBVersionChangeEvent, "Event is of the right type"); + ok(event.target.result instanceof IDBDatabase, "Good result"); + db2 = event.target.result; + is(event.target.transaction.mode, "versionchange", + "Correct mode"); + is(db2.version, 2, "Correct db version"); + is(event.oldVersion, 1, "Correct event oldVersion"); + is(event.newVersion, 2, "Correct event newVersion"); + + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + db2.addEventListener("versionchange", function(event) { + ok(true, "Got version change event"); + ok(event instanceof IDBVersionChangeEvent, "Event is of the right type"); + is("source" in event.target, false, "Correct source"); + is(event.target, db2, "Correct target"); + is(event.target.version, 2, "Correct db version"); + is(event.oldVersion, 2, "Correct event oldVersion"); + is(event.newVersion, 3, "Correct event newVersion"); + is(versionChangeEventCount++, 1, "Correct count"); + }, false); + + // Test opening the existing version again + request = indexedDB.open(name, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + request.onblocked = errorHandler; + + event = yield undefined; + + db3 = event.target.result; + + // Test an upgrade that should fail + request = indexedDB.open(name, 3); + request.onerror = errorHandler; + request.onsuccess = errorHandler; + request.onupgradeneeded = errorHandler; + request.onblocked = grabEventAndContinueHandler; + + event = yield undefined; + ok(true, "Got version change blocked event"); + ok(event instanceof IDBVersionChangeEvent, "Event is of the right type"); + is(event.target.source, null, "Correct source"); + is(event.target.transaction, null, "Correct transaction"); + is(event.target, request, "Correct target"); + is(db3.version, 2, "Correct db version"); + is(event.oldVersion, 2, "Correct event oldVersion"); + is(event.newVersion, 3, "Correct event newVersion"); + versionChangeEventCount++; + db2.close(); + db3.close(); + + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + event = yield undefined; + + db3 = event.target.result; + db3.close(); + + // Test another upgrade that should succeed. + request = indexedDB.open(name, 4); + request.onerror = errorHandler; + request.onsuccess = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onblocked = errorHandler; + + event = yield undefined; + + ok(event instanceof IDBVersionChangeEvent, "Event is of the right type"); + ok(event.target.result instanceof IDBDatabase, "Good result"); + is(event.target.transaction.mode, "versionchange", + "Correct mode"); + is(event.oldVersion, 3, "Correct event oldVersion"); + is(event.newVersion, 4, "Correct event newVersion"); + + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + ok(event.target.result instanceof IDBDatabase, "Expect a database here"); + is(event.target.result.version, 4, "Right version"); + is(db3.version, 3, "After closing the version should not change!"); + is(db2.version, 2, "After closing the version should not change!"); + is(db1.version, 1, "After closing the version should not change!"); + + is(versionChangeEventCount, 3, "Saw all expected events"); + + event = new IDBVersionChangeEvent("versionchange"); + ok(event, "Should be able to create an event with just passing in the type"); + event = new IDBVersionChangeEvent("versionchange", {oldVersion: 1}); + ok(event, "Should be able to create an event with just the old version"); + is(event.oldVersion, 1, "Correct old version"); + is(event.newVersion, null, "Correct new version"); + event = new IDBVersionChangeEvent("versionchange", {newVersion: 1}); + ok(event, "Should be able to create an event with just the new version"); + is(event.oldVersion, 0, "Correct old version"); + is(event.newVersion, 1, "Correct new version"); + event = new IDBVersionChangeEvent("versionchange", {oldVersion: 1, newVersion: 2}); + ok(event, "Should be able to create an event with both versions"); + is(event.oldVersion, 1, "Correct old version"); + is(event.newVersion, 2, "Correct new version"); + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_setVersion_exclusion.js b/dom/indexedDB/test/unit/test_setVersion_exclusion.js new file mode 100644 index 000000000..2a51ab090 --- /dev/null +++ b/dom/indexedDB/test/unit/test_setVersion_exclusion.js @@ -0,0 +1,95 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let request2 = indexedDB.open(name, 2); + request2.onerror = errorHandler; + request2.onupgradeneeded = unexpectedSuccessHandler; + request2.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + is(event.type, "upgradeneeded", "Expect an upgradeneeded event"); + is(event.target, request, "Event should be fired on the request"); + ok(event.target.result instanceof IDBDatabase, "Expect a database here"); + + let db = event.target.result; + is(db.version, 1, "Database has correct version"); + + db.onupgradeneeded = function() { + ok(false, "our ongoing VERSION_CHANGE transaction should exclude any others!"); + } + + db.createObjectStore("foo"); + + try { + db.transaction("foo"); + ok(false, "Transactions should be disallowed now!"); + } catch (e) { + ok(e instanceof DOMException, "Expect a DOMException"); + is(e.name, "InvalidStateError", "Expect an InvalidStateError"); + is(e.code, DOMException.INVALID_STATE_ERR, "Expect an INVALID_STATE_ERR"); + } + + request.onupgradeneeded = unexpectedSuccessHandler; + request.transaction.oncomplete = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "complete", "Got complete event"); + + try { + db.transaction("foo"); + ok(true, "Transactions should be allowed now!"); + } catch (e) { + ok(false, "Transactions should be allowed now!"); + } + + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "Expect a success event"); + is(event.target.result, db, "Same database"); + + db.onversionchange = function() { + ok(true, "next setVersion was unblocked appropriately"); + db.close(); + } + + try { + db.transaction("foo"); + ok(true, "Transactions should be allowed now!"); + } catch (e) { + ok(false, "Transactions should be allowed now!"); + } + + request.onsuccess = unexpectedSuccessHandler; + request2.onupgradeneeded = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "upgradeneeded", "Expect an upgradeneeded event"); + + db = event.target.result; + is(db.version, 2, "Database has correct version"); + + request2.onupgradeneeded = unexpectedSuccessHandler; + request2.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "Expect a success event"); + is(event.target.result, db, "Same database"); + is(db.version, 2, "Database has correct version"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_setVersion_throw.js b/dom/indexedDB/test/unit/test_setVersion_throw.js new file mode 100644 index 000000000..a589bb385 --- /dev/null +++ b/dom/indexedDB/test/unit/test_setVersion_throw.js @@ -0,0 +1,54 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "test_setVersion_throw"; + + // This test requires two databases. The first needs to be a low version + // number that gets closed when a second higher version number database is + // created. Then the upgradeneeded event for the second database throws an + // exception and triggers an abort/close. + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + request.onupgradeneeded = function(event) { + info("Got upgradeneeded event for db 1"); + }; + let event = yield undefined; + + is(event.type, "success", "Got success event for db 1"); + + let db = event.target.result; + db.onversionchange = function(event) { + info("Got versionchange event for db 1"); + event.target.close(); + } + + executeSoon(continueToNextStepSync); + yield undefined; + + request = indexedDB.open(name, 2); + request.onerror = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + request.onupgradeneeded = function(event) { + info("Got upgradeneeded event for db 2"); + expectUncaughtException(true); + trigger_js_exception_by_calling_a_nonexistent_function(); + }; + event = yield undefined; + + event.preventDefault(); + + is(event.type, "error", "Got an error event for db 2"); + ok(event.target.error instanceof DOMError, "Request has a DOMError"); + is(event.target.error.name, "AbortError", "Request has AbortError"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_snappyUpgrade.js b/dom/indexedDB/test/unit/test_snappyUpgrade.js new file mode 100644 index 000000000..c2c1b1668 --- /dev/null +++ b/dom/indexedDB/test/unit/test_snappyUpgrade.js @@ -0,0 +1,44 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = "test_snappyUpgrade.js"; + const objectStoreName = "test"; + const testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis."; + + info("Installing profile"); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("snappyUpgrade_profile"); + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Getting string"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(1); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, testString); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js b/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js new file mode 100644 index 000000000..ce138f138 --- /dev/null +++ b/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js @@ -0,0 +1,66 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/default/http+++www.mozilla.org + { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }, + + // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net + { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net", + dbName: "dbN", dbVersion: 1 }, + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let uri = ios.newURI(params.url, null, null); + let principal = + ssm.createCodebasePrincipal(uri, + {appId: params.appId || ssm.NO_APPID, + inIsolatedMozBrowser: params.inIsolatedMozBrowser}); + let request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbVersion); + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("storagePersistentUpgrade_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_storage_manager_estimate.js b/dom/indexedDB/test/unit/test_storage_manager_estimate.js new file mode 100644 index 000000000..0e3dc2d35 --- /dev/null +++ b/dom/indexedDB/test/unit/test_storage_manager_estimate.js @@ -0,0 +1,63 @@ +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : + "test_storage_manager_estimate.js"; + const objectStoreName = "storagesManager"; + const arraySize = 1e6; + + ok('estimate' in navigator.storage, 'Has estimate function'); + is(typeof navigator.storage.estimate, 'function', 'estimate is function'); + ok(navigator.storage.estimate() instanceof Promise, + 'estimate() method exists and returns a Promise'); + + navigator.storage.estimate().then(estimation => { + testGenerator.send(estimation.usage); + }); + + let before = yield undefined; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = continueToNextStep; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + let objectStore = db.createObjectStore(objectStoreName, { }); + yield undefined; + + navigator.storage.estimate().then(estimation => { + testGenerator.send(estimation.usage); + }); + let usageAfterCreate = yield undefined; + ok(usageAfterCreate > before, 'estimated usage must increase after createObjectStore'); + + let txn = db.transaction(objectStoreName, "readwrite"); + objectStore = txn.objectStore(objectStoreName); + objectStore.put(new Uint8Array(arraySize), 'k'); + txn.oncomplete = continueToNextStep; + txn.onabort = errorHandler; + txn.onerror = errorHandler; + event = yield undefined; + + navigator.storage.estimate().then(estimation => { + testGenerator.send(estimation.usage); + }); + let usageAfterPut = yield undefined; + ok(usageAfterPut > usageAfterCreate, 'estimated usage must increase after putting large object'); + db.close(); + + finishTest(); + yield undefined; +} + +function setup() +{ + SpecialPowers.pushPrefEnv({ + "set": [["dom.storageManager.enabled", true]] + }, runTest); +} diff --git a/dom/indexedDB/test/unit/test_success_events_after_abort.js b/dom/indexedDB/test/unit/test_success_events_after_abort.js new file mode 100644 index 000000000..efd138370 --- /dev/null +++ b/dom/indexedDB/test/unit/test_success_events_after_abort.js @@ -0,0 +1,60 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + + event.target.onsuccess = continueToNextStep; + + let objectStore = db.createObjectStore("foo"); + objectStore.add({}, 1).onerror = errorHandler; + + yield undefined; + + objectStore = db.transaction("foo").objectStore("foo"); + + let transaction = objectStore.transaction; + transaction.oncomplete = unexpectedSuccessHandler; + transaction.onabort = grabEventAndContinueHandler; + + let sawError = false; + + request = objectStore.get(1); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = function(event) { + is(event.target.error.name, "AbortError", "Good error"); + sawError = true; + event.preventDefault(); + } + + transaction.abort(); + + event = yield undefined; + + is(event.type, "abort", "Got abort event"); + is(sawError, true, "Saw get() error"); + if (this.window) { + // Make sure the success event isn't queued somehow. + let comp = SpecialPowers.wrap(Components); + let thread = comp.classes["@mozilla.org/thread-manager;1"] + .getService(comp.interfaces.nsIThreadManager) + .currentThread; + while (thread.hasPendingEvents()) { + thread.processNextEvent(false); + } + } + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_table_locks.js b/dom/indexedDB/test/unit/test_table_locks.js new file mode 100644 index 000000000..900f78edf --- /dev/null +++ b/dom/indexedDB/test/unit/test_table_locks.js @@ -0,0 +1,116 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const dbName = ("window" in this) ? window.location.pathname : "test"; +const dbVersion = 1; +const objName1 = "o1"; +const objName2 = "o2"; +const idxName1 = "i1"; +const idxName2 = "i2"; +const idxKeyPathProp = "idx"; +const objDataProp = "data"; +const objData = "1234567890"; +const objDataCount = 5; +const loopCount = 100; + +var testGenerator = testSteps(); + +function testSteps() +{ + let req = indexedDB.open(dbName, dbVersion); + req.onerror = errorHandler; + req.onupgradeneeded = grabEventAndContinueHandler; + req.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + let db = event.target.result; + + let objectStore1 = db.createObjectStore(objName1); + objectStore1.createIndex(idxName1, idxKeyPathProp); + + let objectStore2 = db.createObjectStore(objName2); + objectStore2.createIndex(idxName2, idxKeyPathProp); + + for (let i = 0; i < objDataCount; i++) { + var data = { }; + data[objDataProp] = objData; + data[idxKeyPathProp] = objDataCount - i - 1; + + objectStore1.add(data, i); + objectStore2.add(data, i); + } + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + doReadOnlyTransaction(db, 0, loopCount); + doReadWriteTransaction(db, 0, loopCount); + + // Wait for readonly and readwrite transaction loops to complete. + yield undefined; + yield undefined; + + finishTest(); + yield undefined; +} + +function doReadOnlyTransaction(db, key, remaining) +{ + if (!remaining) { + info("Finished all readonly transactions"); + continueToNextStep(); + return; + } + + info("Starting readonly transaction for key " + key + ", " + remaining + + " loops left"); + + let objectStore = db.transaction(objName1, "readonly").objectStore(objName1); + let index = objectStore.index(idxName1); + + index.openKeyCursor(key, "prev").onsuccess = function(event) { + let cursor = event.target.result; + ok(cursor, "Got readonly cursor"); + + objectStore.get(cursor.primaryKey).onsuccess = function(event) { + if (++key == objDataCount) { + key = 0; + } + doReadOnlyTransaction(db, key, remaining - 1); + } + }; +} + +function doReadWriteTransaction(db, key, remaining) +{ + if (!remaining) { + info("Finished all readwrite transactions"); + continueToNextStep(); + return; + } + + info("Starting readwrite transaction for key " + key + ", " + remaining + + " loops left"); + + let objectStore = db.transaction(objName2, "readwrite").objectStore(objName2); + objectStore.openCursor(key).onsuccess = function(event) { + let cursor = event.target.result; + ok(cursor, "Got readwrite cursor"); + + let value = cursor.value; + value[idxKeyPathProp]++; + + cursor.update(value).onsuccess = function(event) { + if (++key == objDataCount) { + key = 0; + } + doReadWriteTransaction(db, key, remaining - 1); + } + }; +} diff --git a/dom/indexedDB/test/unit/test_table_rollback.js b/dom/indexedDB/test/unit/test_table_rollback.js new file mode 100644 index 000000000..afe194ad5 --- /dev/null +++ b/dom/indexedDB/test/unit/test_table_rollback.js @@ -0,0 +1,115 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName = ("window" in this) ? window.location.pathname : "test"; + const objName1 = "foo"; + const objName2 = "bar"; + const data1 = "1234567890"; + const data2 = "0987654321"; + const dataCount = 500; + + let request = indexedDB.open(dbName, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded"); + + request.onupgradeneeded = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let db = request.result; + + let objectStore1 = db.createObjectStore(objName1, { autoIncrement: true }); + let objectStore2 = db.createObjectStore(objName2, { autoIncrement: true }); + + info("Created object stores, adding data"); + + for (let i = 0; i < dataCount; i++) { + objectStore1.add(data1); + objectStore2.add(data2); + } + + info("Done adding data"); + + event = yield undefined; + + is(event.type, "success", "Got success"); + + let readResult = null; + let readError = null; + let writeAborted = false; + + info("Creating readwrite transaction"); + + objectStore1 = db.transaction(objName1, "readwrite").objectStore(objName1); + objectStore1.openCursor().onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + let cursor = event.target.result; + is(cursor.value, data1, "Got correct data for readwrite transaction"); + + info("Modifying object store on readwrite transaction"); + + cursor.update(data2); + cursor.continue(); + + event = yield undefined; + + info("Done modifying object store on readwrite transaction, creating " + + "readonly transaction"); + + objectStore2 = db.transaction(objName2, "readonly").objectStore(objName2); + request = objectStore2.getAll(); + request.onsuccess = function(event) { + readResult = event.target.result; + is(readResult.length, + dataCount, + "Got correct number of results on readonly transaction"); + for (let i = 0; i < readResult.length; i++) { + is(readResult[i], data2, "Got correct data for readonly transaction"); + } + if (writeAborted) { + continueToNextStep(); + } + }; + request.onerror = function(event) { + readResult = null; + readError = event.target.error; + + ok(false, "Got read error: " + readError.name); + event.preventDefault(); + + if (writeAborted) { + continueToNextStep(); + } + } + + cursor = event.target.result; + is(cursor.value, data1, "Got correct data for readwrite transaction"); + + info("Aborting readwrite transaction"); + + cursor.source.transaction.abort(); + writeAborted = true; + + if (!readError && !readResult) { + info("Waiting for readonly transaction to complete"); + yield undefined; + } + + ok(readResult, "Got result from readonly transaction"); + is(readError, null, "No read error"); + is(writeAborted, true, "Aborted readwrite transaction"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_temporary_storage.js b/dom/indexedDB/test/unit/test_temporary_storage.js new file mode 100644 index 000000000..15426abbb --- /dev/null +++ b/dom/indexedDB/test/unit/test_temporary_storage.js @@ -0,0 +1,258 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? + window.location.pathname : + "test_temporary_storage.js"; + const finalVersion = 2; + + const tempStorageLimitKB = 1024; + const checkpointSleepTimeSec = 5; + + function getSpec(index) { + return "http://foo" + index + ".com"; + } + + for (let temporary of [true, false]) { + info("Testing '" + (temporary ? "temporary" : "default") + "' storage"); + + setTemporaryStorageLimit(tempStorageLimitKB); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + info("Stage 1 - Creating empty databases until we reach the quota limit"); + + let databases = []; + let options = { version: finalVersion }; + if (temporary) { + options.storage = "temporary"; + } + + while (true) { + let spec = getSpec(databases.length); + + info("Opening database for " + spec + " with version " + options.version); + + let gotUpgradeIncomplete = false; + let gotUpgradeComplete = false; + + let request = + indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = function(event) { + is(request.error.name, + gotUpgradeIncomplete ? "AbortError" : "QuotaExceededError", + "Reached quota limit"); + event.preventDefault(); + testGenerator.send(false); + } + request.onupgradeneeded = function(event) { + event.target.transaction.onabort = function(e) { + gotUpgradeIncomplete = true; + is(e.target.error.name, "QuotaExceededError", "Reached quota limit"); + } + event.target.transaction.oncomplete = function() { + gotUpgradeComplete = true; + } + } + request.onsuccess = function(event) { + let db = event.target.result; + is(db.version, finalVersion, "Correct version " + finalVersion); + databases.push(db); + testGenerator.send(true); + } + + let shouldContinue = yield undefined; + if (shouldContinue) { + is(gotUpgradeComplete, true, "Got upgradeneeded event"); + ok(true, "Got success event"); + } else { + break; + } + } + + while (true) { + info("Sleeping for " + checkpointSleepTimeSec + " seconds to let all " + + "checkpoints finish so that we know we have reached quota limit"); + setTimeout(continueToNextStepSync, checkpointSleepTimeSec * 1000); + yield undefined; + + let spec = getSpec(databases.length); + + info("Opening database for " + spec + " with version " + options.version); + + let gotUpgradeIncomplete = false; + let gotUpgradeComplete = false; + + let request = + indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = function(event) { + is(request.error.name, + gotUpgradeIncomplete ? "AbortError" : "QuotaExceededError", + "Reached quota limit"); + event.preventDefault(); + testGenerator.send(false); + } + request.onupgradeneeded = function(event) { + event.target.transaction.onabort = function(e) { + gotUpgradeIncomplete = true; + is(e.target.error.name, "QuotaExceededError", "Reached quota limit"); + } + event.target.transaction.oncomplete = function() { + gotUpgradeComplete = true; + } + } + request.onsuccess = function(event) { + let db = event.target.result; + is(db.version, finalVersion, "Correct version " + finalVersion); + databases.push(db); + testGenerator.send(true); + } + + let shouldContinue = yield undefined; + if (shouldContinue) { + is(gotUpgradeComplete, true, "Got upgradeneeded event"); + ok(true, "Got success event"); + } else { + break; + } + } + + let databaseCount = databases.length; + info("Created " + databaseCount + " databases before quota limit reached"); + + info("Stage 2 - " + + "Closing all databases and then attempting to create one more, then " + + "verifying that the oldest origin was cleared"); + + for (let i = 0; i < databases.length; i++) { + info("Closing database for " + getSpec(i)); + databases[i].close(); + + // Timer resolution on Windows is low so wait for 40ms just to be safe. + setTimeout(continueToNextStepSync, 40); + yield undefined; + } + databases = null; + + let spec = getSpec(databaseCount); + info("Opening database for " + spec + " with version " + options.version); + + let request = indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got success event"); + + let db = event.target.result; + is(db.version, finalVersion, "Correct version " + finalVersion); + db.close(); + db = null; + + setTemporaryStorageLimit(tempStorageLimitKB * 2); + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + delete options.version; + + spec = getSpec(0); + info("Opening database for " + spec + " with unspecified version"); + + request = indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Got upgradeneeded event"); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got success event"); + + db = event.target.result; + is(db.version, 1, "Correct version 1 (database was recreated)"); + db.close(); + db = null; + + info("Stage 3 - " + + "Cutting storage limit in half to force deletion of some databases"); + + setTemporaryStorageLimit(tempStorageLimitKB / 2); + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + info("Opening database for " + spec + " with unspecified version"); + + // Open the same db again to force QM to delete others. The first origin (0) + // should be the most recent so it should not be deleted and we should not + // get an upgradeneeded event here. + request = indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + db = event.target.result; + is(db.version, 1, "Correct version 1"); + db.close(); + db = null; + + setTemporaryStorageLimit(tempStorageLimitKB * 2); + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + options.version = finalVersion; + + let newDatabaseCount = 0; + for (let i = 0; i < databaseCount; i++) { + let spec = getSpec(i); + info("Opening database for " + spec + " with version " + options.version); + + let request = + indexedDB.openForPrincipal(getPrincipal(spec), name, options); + request.onerror = errorHandler; + request.onupgradeneeded = function(event) { + if (!event.oldVersion) { + newDatabaseCount++; + } + } + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + let db = request.result; + is(db.version, finalVersion, "Correct version " + finalVersion); + db.close(); + } + + info("Needed to recreate " + newDatabaseCount + " databases"); + ok(newDatabaseCount, "Created some new databases"); + ok(newDatabaseCount < databaseCount, "Didn't recreate all databases"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_traffic_jam.js b/dom/indexedDB/test/unit/test_traffic_jam.js new file mode 100644 index 000000000..f09985b13 --- /dev/null +++ b/dom/indexedDB/test/unit/test_traffic_jam.js @@ -0,0 +1,87 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let requests = []; + function doOpen(version, errorCallback, upgradeNeededCallback, successCallback) { + let request = indexedDB.open(name, version); + request.onerror = errorCallback; + request.onupgradeneeded = upgradeNeededCallback; + request.onsuccess = successCallback; + requests.push(request); + } + + doOpen(1, errorHandler, grabEventAndContinueHandler, grabEventAndContinueHandler); + doOpen(2, errorHandler, unexpectedSuccessHandler, unexpectedSuccessHandler); + + let event = yield undefined; + is(event.type, "upgradeneeded", "expect an upgradeneeded event"); + is(event.target, requests[0], "fired at the right request"); + + let db = event.target.result; + db.createObjectStore("foo"); + + doOpen(3, errorHandler, unexpectedSuccessHandler, unexpectedSuccessHandler); + doOpen(2, errorHandler, unexpectedSuccessHandler, unexpectedSuccessHandler); + doOpen(3, errorHandler, unexpectedSuccessHandler, unexpectedSuccessHandler); + + event.target.transaction.oncomplete = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "complete", "expect a complete event"); + is(event.target, requests[0].transaction, "expect it to be fired at the transaction"); + + event = yield undefined; + is(event.type, "success", "expect a success event"); + is(event.target, requests[0], "fired at the right request"); + event.target.result.close(); + + requests[1].onupgradeneeded = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "upgradeneeded", "expect an upgradeneeded event"); + is(event.target, requests[1], "fired at the right request"); + + requests[1].onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "expect a success event"); + is(event.target, requests[1], "fired at the right request"); + event.target.result.close(); + + requests[2].onupgradeneeded = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "upgradeneeded", "expect an upgradeneeded event"); + is(event.target, requests[2], "fired at the right request"); + + requests[2].onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "expect a success event"); + is(event.target, requests[2], "fired at the right request"); + event.target.result.close(); + + requests[3].onerror = null; + requests[3].addEventListener("error", new ExpectError("VersionError", true)); + + event = yield undefined; + + requests[4].onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "success", "expect a success event"); + is(event.target, requests[4], "fired at the right request"); + event.target.result.close(); + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_transaction_abort.js b/dom/indexedDB/test/unit/test_transaction_abort.js new file mode 100644 index 000000000..0f051f968 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_abort.js @@ -0,0 +1,384 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +var abortFired = false; + +function abortListener(evt) +{ + abortFired = true; + is(evt.target.error, null, "Expect a null error for an aborted transaction"); +} + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onabort = abortListener; + + let transaction; + let objectStore; + let index; + + transaction = event.target.transaction; + + is(transaction.error, null, "Expect a null error"); + + objectStore = db.createObjectStore("foo", { autoIncrement: true }); + index = objectStore.createIndex("fooindex", "indexKey", { unique: true }); + + is(transaction.db, db, "Correct database"); + is(transaction.mode, "versionchange", "Correct mode"); + is(transaction.objectStoreNames.length, 1, "Correct names length"); + is(transaction.objectStoreNames.item(0), "foo", "Correct name"); + is(transaction.objectStore("foo"), objectStore, "Can get stores"); + is(transaction.oncomplete, null, "No complete listener"); + is(transaction.onabort, null, "No abort listener"); + + is(objectStore.name, "foo", "Correct name"); + is(objectStore.keyPath, null, "Correct keyPath"); + + is(objectStore.indexNames.length, 1, "Correct indexNames length"); + is(objectStore.indexNames[0], "fooindex", "Correct indexNames name"); + is(objectStore.index("fooindex"), index, "Can get index"); + + // Wait until it's complete! + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(transaction.db, db, "Correct database"); + is(transaction.mode, "versionchange", "Correct mode"); + is(transaction.objectStoreNames.length, 1, "Correct names length"); + is(transaction.objectStoreNames.item(0), "foo", "Correct name"); + is(transaction.onabort, null, "No abort listener"); + + try { + is(transaction.objectStore("foo").name, "foo", "Can't get stores"); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Out of scope transaction can't make stores"); + } + + is(objectStore.name, "foo", "Correct name"); + is(objectStore.keyPath, null, "Correct keyPath"); + + is(objectStore.indexNames.length, 1, "Correct indexNames length"); + is(objectStore.indexNames[0], "fooindex", "Correct indexNames name"); + + try { + objectStore.add({}); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Add threw"); + } + + try { + objectStore.put({}, 1); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Put threw"); + } + + try { + objectStore.put({}, 1); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Put threw"); + } + + try { + objectStore.delete(1); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Remove threw"); + } + + try { + objectStore.get(1); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Get threw"); + } + + try { + objectStore.getAll(null); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "GetAll threw"); + } + + try { + objectStore.openCursor(); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "OpenCursor threw"); + } + + try { + objectStore.createIndex("bar", "id"); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "CreateIndex threw"); + } + + try { + objectStore.index("bar"); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "Index threw"); + } + + try { + objectStore.deleteIndex("bar"); + ok(false, "Should have thrown"); + } + catch (e) { + ok(true, "RemoveIndex threw"); + } + + yield undefined; + + request = db.transaction("foo", "readwrite").objectStore("foo").add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + event.target.transaction.onabort = function(event) { + ok(false, "Shouldn't see an abort event!"); + }; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", "Right kind of event"); + + let key; + + request = db.transaction("foo", "readwrite").objectStore("foo").add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + key = event.target.result; + + event.target.transaction.onabort = grabEventAndContinueHandler; + event.target.transaction.oncomplete = function(event) { + ok(false, "Shouldn't see a complete event here!"); + }; + + event.target.transaction.abort(); + + event = yield undefined; + + is(event.type, "abort", "Right kind of event"); + + request = db.transaction("foo").objectStore("foo").get(key); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Object was removed"); + + executeSoon(function() { testGenerator.next(); }); + yield undefined; + + let keys = []; + let abortEventCount = 0; + function abortErrorHandler(event) { + is(event.target.error.name, "AbortError", + "Good error"); + abortEventCount++; + event.preventDefault(); + }; + objectStore = db.transaction("foo", "readwrite").objectStore("foo"); + + for (let i = 0; i < 10; i++) { + request = objectStore.add({}); + request.onerror = abortErrorHandler; + request.onsuccess = function(event) { + keys.push(event.target.result); + if (keys.length == 5) { + event.target.transaction.onabort = grabEventAndContinueHandler; + event.target.transaction.abort(); + } + }; + } + event = yield undefined; + + is(event.type, "abort", "Got abort event"); + is(keys.length, 5, "Added 5 items in this transaction"); + is(abortEventCount, 5, "Got 5 abort error events"); + + for (let i in keys) { + request = db.transaction("foo").objectStore("foo").get(keys[i]); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.target.result, undefined, "Object was removed by abort"); + } + + // Set up some predictible data + transaction = db.transaction("foo", "readwrite"); + objectStore = transaction.objectStore("foo"); + objectStore.clear(); + objectStore.add({}, 1); + objectStore.add({}, 2); + request = objectStore.add({}, 1); + request.onsuccess = function() { + ok(false, "inserting duplicate key should fail"); + } + request.onerror = function(event) { + ok(true, "inserting duplicate key should fail"); + event.preventDefault(); + } + transaction.oncomplete = grabEventAndContinueHandler; + yield undefined; + + // Check when aborting is allowed + abortEventCount = 0; + let expectedAbortEventCount = 0; + + // During INITIAL + transaction = db.transaction("foo"); + transaction.abort(); + try { + transaction.abort(); + ok(false, "second abort should throw an error"); + } + catch (ex) { + ok(true, "second abort should throw an error"); + } + + // During LOADING + transaction = db.transaction("foo"); + transaction.objectStore("foo").get(1).onerror = abortErrorHandler; + expectedAbortEventCount++; + transaction.abort(); + try { + transaction.abort(); + ok(false, "second abort should throw an error"); + } + catch (ex) { + ok(true, "second abort should throw an error"); + } + + // During LOADING from callback + transaction = db.transaction("foo"); + transaction.objectStore("foo").get(1).onsuccess = grabEventAndContinueHandler; + event = yield undefined; + transaction.objectStore("foo").get(1).onerror = abortErrorHandler; + expectedAbortEventCount++ + transaction.abort(); + try { + transaction.abort(); + ok(false, "second abort should throw an error"); + } + catch (ex) { + ok(true, "second abort should throw an error"); + } + + // During LOADING from error callback + transaction = db.transaction("foo", "readwrite"); + transaction.objectStore("foo").add({}, 1).onerror = function(event) { + event.preventDefault(); + + transaction.objectStore("foo").get(1).onerror = abortErrorHandler; + expectedAbortEventCount++ + + transaction.abort(); + continueToNextStep(); + } + yield undefined; + + // In between callbacks + transaction = db.transaction("foo"); + function makeNewRequest() { + let r = transaction.objectStore("foo").get(1); + r.onsuccess = makeNewRequest; + r.onerror = abortErrorHandler; + } + makeNewRequest(); + transaction.objectStore("foo").get(1).onsuccess = function(event) { + executeSoon(function() { + transaction.abort(); + expectedAbortEventCount++; + continueToNextStep(); + }); + }; + yield undefined; + + // During COMMITTING + transaction = db.transaction("foo", "readwrite"); + transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) { + continueToNextStep(); + }; + yield undefined; + try { + transaction.abort(); + ok(false, "second abort should throw an error"); + } + catch (ex) { + ok(true, "second abort should throw an error"); + } + transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + // Since the previous transaction shouldn't have caused any error events, + // we know that all events should have fired by now. + is(abortEventCount, expectedAbortEventCount, + "All abort errors fired"); + + // Abort both failing and succeeding requests + transaction = db.transaction("foo", "readwrite"); + transaction.onabort = transaction.oncomplete = grabEventAndContinueHandler; + transaction.objectStore("foo").add({indexKey: "key"}).onsuccess = function(event) { + transaction.abort(); + }; + let request1 = transaction.objectStore("foo").add({indexKey: "key"}); + request1.onsuccess = grabEventAndContinueHandler; + request1.onerror = grabEventAndContinueHandler; + let request2 = transaction.objectStore("foo").get(1); + request2.onsuccess = grabEventAndContinueHandler; + request2.onerror = grabEventAndContinueHandler; + + event = yield undefined; + is(event.type, "error", "abort() should make all requests fail"); + is(event.target, request1, "abort() should make all requests fail"); + is(event.target.error.name, "AbortError", "abort() should make all requests fail"); + event.preventDefault(); + + event = yield undefined; + is(event.type, "error", "abort() should make all requests fail"); + is(event.target, request2, "abort() should make all requests fail"); + is(event.target.error.name, "AbortError", "abort() should make all requests fail"); + event.preventDefault(); + + event = yield undefined; + is(event.type, "abort", "transaction should fail"); + is(event.target, transaction, "transaction should fail"); + + ok(abortFired, "Abort should have fired!"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_abort_hang.js b/dom/indexedDB/test/unit/test_transaction_abort_hang.js new file mode 100644 index 000000000..cb59174f6 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_abort_hang.js @@ -0,0 +1,91 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +var self = this; + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName = self.window ? + window.location.pathname : + "test_transaction_abort_hang"; + const objStoreName = "foo"; + const transactionCount = 30; + + let completedTransactionCount = 0; + let caughtError = false; + + let abortedTransactionIndex = Math.floor(transactionCount / 2); + if (abortedTransactionIndex % 2 == 0) { + abortedTransactionIndex++; + } + + let request = indexedDB.open(dbName, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + request.result.createObjectStore(objStoreName, { autoIncrement: true }); + + request.onupgradeneeded = null; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let db = event.target.result; + + for (let i = 0; i < transactionCount; i++) { + const readonly = i % 2 == 0; + const mode = readonly ? "readonly" : "readwrite"; + + let transaction = db.transaction(objStoreName, mode); + + if (i == transactionCount - 1) { + // Last one, finish the test. + transaction.oncomplete = grabEventAndContinueHandler; + } else if (i == abortedTransactionIndex - 1) { + transaction.oncomplete = function(event) { + ok(true, "Completed transaction " + ++completedTransactionCount + + " (We may hang after this!)"); + }; + } else if (i == abortedTransactionIndex) { + // Special transaction that we abort outside the normal event flow. + transaction.onerror = function(event) { + ok(true, "Aborted transaction " + ++completedTransactionCount + + " (We didn't hang!)"); + is(event.target.error.name, "AbortError", + "AbortError set as the error on the request"); + is(event.target.transaction.error, null, + "No error set on the transaction"); + ok(!caughtError, "Haven't seen the error event yet"); + caughtError = true; + event.preventDefault(); + }; + // This has to happen after the we return to the event loop but before the + // transaction starts running. + executeSoon(function() { transaction.abort(); }); + } else { + transaction.oncomplete = function(event) { + ok(true, "Completed transaction " + ++completedTransactionCount); + }; + } + + if (readonly) { + transaction.objectStore(objStoreName).get(0); + } else { + try { transaction.objectStore(objStoreName).add({}); } catch(e) { } + } + } + ok(true, "Created all transactions"); + + event = yield undefined; + + ok(true, "Completed transaction " + ++completedTransactionCount); + ok(caughtError, "Caught the error event when we aborted the transaction"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_duplicate_store_names.js b/dom/indexedDB/test/unit/test_transaction_duplicate_store_names.js new file mode 100644 index 000000000..f37b14bab --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_duplicate_store_names.js @@ -0,0 +1,43 @@ + +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() { + const dbName = this.window ? + window.location.pathname : + "test_transaction_duplicate_store_names"; + const dbVersion = 1; + const objectStoreName = "foo"; + const data = { }; + const dataKey = 1; + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + objectStore.add(data, dataKey); + + event = yield undefined; + + db = event.target.result; + + let transaction = db.transaction([objectStoreName, objectStoreName], "readwrite"); + transaction.onerror = errorHandler; + transaction.oncomplete = grabEventAndContinueHandler; + + event = yield undefined; + + ok(true, "Transaction created successfully"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_error.js b/dom/indexedDB/test/unit/test_transaction_error.js new file mode 100644 index 000000000..14217eba8 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_error.js @@ -0,0 +1,136 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() { + const dbName = this.window ? + window.location.pathname : + "test_transaction_error"; + const dbVersion = 1; + const objectStoreName = "foo"; + const data = { }; + const dataKey = 1; + const expectedError = "ConstraintError"; + + let request = indexedDB.open(dbName, dbVersion); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + + let event = yield undefined; + + info("Creating database"); + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + objectStore.add(data, dataKey); + + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + db = event.target.result; + + try { + db.transaction(objectStoreName, "versionchange"); + ok(false, "TypeError shall be thrown if transaction mode is wrong."); + } catch (e) { + ok(e instanceof DOMException, "got a database exception"); + is(e.name, "TypeError", "correct error"); + } + + let transaction = db.transaction(objectStoreName, "readwrite"); + transaction.onerror = grabEventAndContinueHandler; + transaction.oncomplete = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(objectStoreName); + + info("Adding duplicate entry with preventDefault()"); + + request = objectStore.add(data, dataKey); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "error", "Got an error event"); + is(event.target, request, "Error event targeted request"); + is(event.currentTarget, request, "Got request error first"); + is(event.currentTarget.error.name, expectedError, + "Request has correct error"); + event.preventDefault(); + + event = yield undefined; + + is(event.type, "error", "Got an error event"); + is(event.target, request, "Error event targeted request"); + is(event.currentTarget, transaction, "Got transaction error second"); + is(event.currentTarget.error, null, "Transaction has null error"); + + event = yield undefined; + + is(event.type, "complete", "Got a complete event"); + is(event.target, transaction, "Complete event targeted transaction"); + is(event.currentTarget, transaction, + "Complete event only targeted transaction"); + is(event.currentTarget.error, null, "Transaction has null error"); + + // Try again without preventDefault(). + + transaction = db.transaction(objectStoreName, "readwrite"); + transaction.onerror = grabEventAndContinueHandler; + transaction.onabort = grabEventAndContinueHandler; + + objectStore = transaction.objectStore(objectStoreName); + + info("Adding duplicate entry without preventDefault()"); + + if ("SimpleTest" in this) { + SimpleTest.expectUncaughtException(); + } else if ("DedicatedWorkerGlobalScope" in self && + self instanceof DedicatedWorkerGlobalScope) { + let oldErrorFunction = self.onerror; + self.onerror = function(message, file, line) { + self.onerror = oldErrorFunction; + oldErrorFunction = null; + + is(message, + "ConstraintError", + "Got expected ConstraintError on DedicatedWorkerGlobalScope"); + return true; + }; + } + + request = objectStore.add(data, dataKey); + request.onsuccess = unexpectedSuccessHandler; + request.onerror = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "error", "Got an error event"); + is(event.target, request, "Error event targeted request"); + is(event.currentTarget, request, "Got request error first"); + is(event.currentTarget.error.name, expectedError, + "Request has correct error"); + + event = yield undefined; + + is(event.type, "error", "Got an error event"); + is(event.target, request, "Error event targeted request"); + is(event.currentTarget, transaction, "Got transaction error second"); + is(event.currentTarget.error, null, "Transaction has null error"); + + event = yield undefined; + + is(event.type, "abort", "Got an abort event"); + is(event.target, transaction, "Abort event targeted transaction"); + is(event.currentTarget, transaction, + "Abort event only targeted transaction"); + is(event.currentTarget.error.name, expectedError, + "Transaction has correct error"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_lifetimes.js b/dom/indexedDB/test/unit/test_transaction_lifetimes.js new file mode 100644 index 000000000..e42f7a218 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_lifetimes.js @@ -0,0 +1,91 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + event.target.transaction.onerror = errorHandler; + event.target.transaction.oncomplete = grabEventAndContinueHandler; + + let os = db.createObjectStore("foo", { autoIncrement: true }); + let index = os.createIndex("bar", "foo.bar"); + event = yield undefined; + + is(request.transaction, event.target, + "request.transaction should still be set"); + + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(request.transaction, null, "request.transaction should be cleared"); + + let transaction = db.transaction("foo", "readwrite"); + + os = transaction.objectStore("foo"); + // Place a request to keep the transaction alive long enough for our + // executeSoon. + let requestComplete = false; + + let wasAbleToGrabObjectStoreOutsideOfCallback = false; + let wasAbleToGrabIndexOutsideOfCallback = false; + executeSoon(function() { + ok(!requestComplete, "Ordering is correct."); + wasAbleToGrabObjectStoreOutsideOfCallback = !!transaction.objectStore("foo"); + wasAbleToGrabIndexOutsideOfCallback = + !!transaction.objectStore("foo").index("bar"); + }); + + request = os.add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + requestComplete = true; + + ok(wasAbleToGrabObjectStoreOutsideOfCallback, + "Should be able to get objectStore"); + ok(wasAbleToGrabIndexOutsideOfCallback, + "Should be able to get index"); + + transaction.oncomplete = grabEventAndContinueHandler; + yield undefined; + + try { + transaction.objectStore("foo"); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got database exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + continueToNextStep(); + yield undefined; + + try { + transaction.objectStore("foo"); + ok(false, "Should have thrown!"); + } + catch (e) { + ok(e instanceof DOMException, "Got database exception."); + is(e.name, "InvalidStateError", "Good error."); + is(e.code, DOMException.INVALID_STATE_ERR, "Good error code."); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js b/dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js new file mode 100644 index 000000000..5d63945ba --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_lifetimes_nested.js @@ -0,0 +1,52 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var disableWorkerTest = "This test uses SpecialPowers"; + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + event.target.onsuccess = continueToNextStep; + db.createObjectStore("foo"); + yield undefined; + + let transaction1 = db.transaction("foo"); + + let transaction2; + + let comp = this.window ? SpecialPowers.wrap(Components) : Components; + let thread = comp.classes["@mozilla.org/thread-manager;1"] + .getService(comp.interfaces.nsIThreadManager) + .currentThread; + + let eventHasRun; + + thread.dispatch(function() { + eventHasRun = true; + + transaction2 = db.transaction("foo"); + }, Components.interfaces.nsIThread.DISPATCH_NORMAL); + + while (!eventHasRun) { + thread.processNextEvent(false); + } + + ok(transaction2, "Non-null transaction2"); + + continueToNextStep(); + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_transaction_ordering.js b/dom/indexedDB/test/unit/test_transaction_ordering.js new file mode 100644 index 000000000..5e3c2fd74 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_ordering.js @@ -0,0 +1,49 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + request.onsuccess = continueToNextStep; + + db.createObjectStore("foo"); + yield undefined; + + let trans1 = db.transaction("foo", "readwrite"); + let trans2 = db.transaction("foo", "readwrite"); + + let request1 = trans2.objectStore("foo").put("2", 42); + let request2 = trans1.objectStore("foo").put("1", 42); + + request1.onerror = errorHandler; + request2.onerror = errorHandler; + + trans1.oncomplete = grabEventAndContinueHandler; + trans2.oncomplete = grabEventAndContinueHandler; + + yield undefined; + yield undefined; + + let trans3 = db.transaction("foo", "readonly"); + request = trans3.objectStore("foo").get(42); + request.onsuccess = grabEventAndContinueHandler; + request.onerror = errorHandler; + + event = yield undefined; + is(event.target.result, "2", "Transactions were ordered properly."); + + finishTest(); + yield undefined; +} + diff --git a/dom/indexedDB/test/unit/test_unique_index_update.js b/dom/indexedDB/test/unit/test_unique_index_update.js new file mode 100644 index 000000000..97caef324 --- /dev/null +++ b/dom/indexedDB/test/unit/test_unique_index_update.js @@ -0,0 +1,64 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + let db = event.target.result; + + for (let autoIncrement of [false, true]) { + let objectStore = + db.createObjectStore(autoIncrement, { keyPath: "id", + autoIncrement: autoIncrement }); + objectStore.createIndex("", "index", { unique: true }); + + for (let i = 0; i < 10; i++) { + objectStore.add({ id: i, index: i }); + } + } + + event = yield undefined; + is(event.type, "success", "expect a success event"); + + for (let autoIncrement of [false, true]) { + objectStore = db.transaction(autoIncrement, "readwrite") + .objectStore(autoIncrement); + + request = objectStore.put({ id: 5, index: 6 }); + request.onsuccess = unexpectedSuccessHandler; + request.addEventListener("error", new ExpectError("ConstraintError", true)); + event = yield undefined; + + event.preventDefault(); + + let keyRange = IDBKeyRange.only(5); + + objectStore.index("").openCursor(keyRange).onsuccess = function(event) { + let cursor = event.target.result; + ok(cursor, "Must have a cursor here"); + + is(cursor.value.index, 5, "Still have the right index value"); + + cursor.value.index = 6; + + request = cursor.update(cursor.value); + request.onsuccess = unexpectedSuccessHandler; + request.addEventListener("error", new ExpectError("ConstraintError", true)); + }; + + yield undefined; + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_view_put_get_values.js b/dom/indexedDB/test/unit/test_view_put_get_values.js new file mode 100644 index 000000000..51b9db572 --- /dev/null +++ b/dom/indexedDB/test/unit/test_view_put_get_values.js @@ -0,0 +1,102 @@ +/** + * 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 name = + this.window ? window.location.pathname : "test_view_put_get_values.js"; + + const objectStoreName = "Views"; + + const viewData = { key: 1, view: getRandomView(100000) }; + + for (let external of [false, true]) { + if (external) { + info("Setting data threshold pref"); + + if (this.window) { + SpecialPowers.pushPrefEnv( + { "set": [["dom.indexedDB.dataThreshold", 0]] }, continueToNextStep); + yield undefined; + } else { + setDataThreshold(0); + } + } + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing view"); + + let objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + request = objectStore.add(viewData.view, viewData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, viewData.key, "Got correct key"); + + info("Getting view"); + + request = objectStore.get(viewData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + verifyView(request.result, viewData.view); + yield undefined; + + info("Getting view in new transaction"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(viewData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + verifyView(request.result, viewData.view); + yield undefined; + + getCurrentUsage(grabFileUsageAndContinueHandler); + let fileUsage = yield undefined; + + if (external) { + ok(fileUsage > 0, "File usage is not zero"); + } else { + ok(fileUsage == 0, "File usage is zero"); + } + + db.close(); + + request = indexedDB.deleteDatabase(name); + request.onerror = errorHandler; + request.onsuccess = continueToNextStepSync; + yield undefined; + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_cursors.js b/dom/indexedDB/test/unit/test_wasm_cursors.js new file mode 100644 index 000000000..08987fe46 --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_cursors.js @@ -0,0 +1,67 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_cursors.js"; + + const objectStoreName = "Wasm"; + + const wasmData = { key: 1, value: null }; + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (nop)))'); + let binary = yield undefined; + wasmData.value = getWasmModule(binary); + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing wasm"); + + let objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + request = objectStore.add(wasmData.value, wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, wasmData.key, "Got correct key"); + + info("Opening cursor"); + + request = objectStore.openCursor(); + request.addEventListener("error", new ExpectError("UnknownError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_getAll.js b/dom/indexedDB/test/unit/test_wasm_getAll.js new file mode 100644 index 000000000..23ed7b9e4 --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_getAll.js @@ -0,0 +1,136 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_getAll.js"; + + const objectStoreName = "Wasm"; + + const wasmData = [ + { key: 1, value: 42 }, + { key: 2, value: [null, null, null] }, + { key: 3, value: [null, null, null, null, null] } + ]; + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (result i32) (i32.const 1)))'); + let binary = yield undefined; + wasmData[1].value[0] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 2)))'); + binary = yield undefined; + wasmData[1].value[1] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 3)))'); + binary = yield undefined; + wasmData[1].value[2] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 4)))'); + binary = yield undefined; + wasmData[2].value[0] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 5)))'); + binary = yield undefined; + wasmData[2].value[1] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 6)))'); + binary = yield undefined; + wasmData[2].value[2] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 7)))'); + binary = yield undefined; + wasmData[2].value[3] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 8)))'); + binary = yield undefined; + wasmData[2].value[4] = getWasmModule(binary); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing values"); + + let objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + let addedCount = 0; + for (let i in wasmData) { + request = objectStore.add(wasmData[i].value, wasmData[i].key); + request.onsuccess = function(event) { + if (++addedCount == wasmData.length) { + continueToNextStep(); + } + } + } + yield undefined; + + info("Getting values"); + + request = db.transaction(objectStoreName) + .objectStore(objectStoreName) + .getAll(); + request.onsuccess = continueToNextStepSync; + yield undefined; + + info("Verifying values"); + + // Can't call yield inside of the verify function. + let modulesToProcess = []; + + function verifyArray(array1, array2) { + is(array1 instanceof Array, true, "Got an array object"); + is(array1.length, array2.length, "Same length"); + } + + function verifyData(data1, data2) { + if (data2 instanceof Array) { + verifyArray(data1, data2); + for (let i in data2) { + verifyData(data1[i], data2[i]); + } + } else if (data2 instanceof WebAssembly.Module) { + modulesToProcess.push({ module1: data1, module2: data2 }); + } else { + is(data1, data2, "Same value"); + } + } + + verifyArray(request.result, wasmData); + for (let i in wasmData) { + verifyData(request.result[i], wasmData[i].value); + } + + for (let moduleToProcess of modulesToProcess) { + verifyWasmModule(moduleToProcess.module1, moduleToProcess.module2); + yield undefined; + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_index_getAllObjects.js b/dom/indexedDB/test/unit/test_wasm_index_getAllObjects.js new file mode 100644 index 000000000..f03e79e5c --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_index_getAllObjects.js @@ -0,0 +1,111 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_getAll.js"; + + const objectStoreName = "Wasm"; + + const wasmData = [ + { key: 1, value: { name: "foo1", data: 42 } }, + { key: 2, value: { name: "foo2", data: [null, null, null] } }, + { key: 3, value: { name: "foo3", data: [null, null, null, null, null] } } + ]; + + const indexData = { name: "nameIndex", keyPath: "name", options: { } }; + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (result i32) (i32.const 1)))'); + let binary = yield undefined; + wasmData[1].value.data[0] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 2)))'); + binary = yield undefined; + wasmData[1].value.data[1] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 3)))'); + binary = yield undefined; + wasmData[1].value.data[2] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 4)))'); + binary = yield undefined; + wasmData[2].value.data[0] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 5)))'); + binary = yield undefined; + wasmData[2].value.data[1] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 6)))'); + binary = yield undefined; + wasmData[2].value.data[2] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 7)))'); + binary = yield undefined; + wasmData[2].value.data[3] = getWasmModule(binary); + + getWasmBinary('(module (func (result i32) (i32.const 8)))'); + binary = yield undefined; + wasmData[2].value.data[4] = getWasmModule(binary); + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + let objectStore = request.result.createObjectStore(objectStoreName); + + info("Creating index"); + + objectStore.createIndex(indexData.name, indexData.keyPath, indexData.options); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing values"); + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + let addedCount = 0; + for (let i in wasmData) { + request = objectStore.add(wasmData[i].value, wasmData[i].key); + request.onsuccess = function(event) { + if (++addedCount == wasmData.length) { + continueToNextStep(); + } + } + } + yield undefined; + + info("Getting values"); + + request = db.transaction(objectStoreName) + .objectStore(objectStoreName) + .index("nameIndex") + .getAll(); + request.addEventListener("error", new ExpectError("UnknownError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_indexes.js b/dom/indexedDB/test/unit/test_wasm_indexes.js new file mode 100644 index 000000000..467e0c297 --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_indexes.js @@ -0,0 +1,80 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_indexes.js"; + + const objectStoreName = "Wasm"; + + const wasmData = { key: 1, value: { name: "foo", data: null } }; + + const indexData = { name: "nameIndex", keyPath: "name", options: { } }; + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (nop)))'); + let binary = yield undefined; + wasmData.value.data = getWasmModule(binary); + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + let objectStore = request.result.createObjectStore(objectStoreName); + + info("Creating index"); + + objectStore.createIndex(indexData.name, indexData.keyPath, indexData.options); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing wasm"); + + objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + request = objectStore.add(wasmData.value, wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, wasmData.key, "Got correct key"); + + info("Getting wasm"); + + request = objectStore.index("nameIndex").get("foo"); + request.addEventListener("error", new ExpectError("UnknownError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + info("Opening cursor"); + + request = objectStore.index("nameIndex").openCursor(); + request.addEventListener("error", new ExpectError("UnknownError", true)); + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_put_get_values.js b/dom/indexedDB/test/unit/test_wasm_put_get_values.js new file mode 100644 index 000000000..fb8827009 --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_put_get_values.js @@ -0,0 +1,83 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = + this.window ? window.location.pathname : "test_wasm_put_get_values.js"; + + const objectStoreName = "Wasm"; + + const wasmData = { key: 1, value: null }; + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (nop)))'); + let binary = yield undefined; + wasmData.value = getWasmModule(binary); + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = continueToNextStepSync; + request.onsuccess = unexpectedSuccessHandler; + yield undefined; + + // upgradeneeded + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + + info("Creating objectStore"); + + request.result.createObjectStore(objectStoreName); + + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Storing wasm"); + + let objectStore = db.transaction([objectStoreName], "readwrite") + .objectStore(objectStoreName); + request = objectStore.add(wasmData.value, wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + is(request.result, wasmData.key, "Got correct key"); + + info("Getting wasm"); + + request = objectStore.get(wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + info("Verifying wasm"); + + verifyWasmModule(request.result, wasmData.value); + yield undefined; + + info("Getting wasm in new transaction"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + info("Verifying wasm"); + + verifyWasmModule(request.result, wasmData.value); + yield undefined; + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_wasm_recompile.js b/dom/indexedDB/test/unit/test_wasm_recompile.js new file mode 100644 index 000000000..512dc3eff --- /dev/null +++ b/dom/indexedDB/test/unit/test_wasm_recompile.js @@ -0,0 +1,124 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = "test_wasm_recompile.js"; + + const objectStoreName = "Wasm"; + + const wasmData = { key: 1, wasm: null }; + + // The goal of this test is to prove that wasm is recompiled and the on-disk + // copy updated. + + if (!isWasmSupported()) { + finishTest(); + yield undefined; + } + + getWasmBinary('(module (func (nop)))'); + let binary = yield undefined; + + wasmData.wasm = getWasmModule(binary); + + info("Installing profile"); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + // The profile was created by an older build (buildId: 20161116145318, + // cpuId: X64=0x2). It contains one stored wasm module (file id 1 - bytecode + // and file id 2 - compiled/machine code). The file create_db.js in the + // package was run locally (specifically it was temporarily added to + // xpcshell-parent-process.ini and then executed: + // mach xpcshell-test dom/indexedDB/test/unit/create_db.js + installPackagedProfile("wasm_recompile_profile"); + + let filesDir = getChromeFilesDir(); + + let file = filesDir.clone(); + file.append("2"); + + info("Reading out contents of compiled blob"); + + let fileReader = new FileReader(); + fileReader.onload = continueToNextStepSync; + fileReader.readAsArrayBuffer(File.createFromNsIFile(file)); + + yield undefined; + + let compiledBuffer = fileReader.result; + + info("Opening database"); + + let request = indexedDB.open(name); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = continueToNextStepSync; + yield undefined; + + // success + let db = request.result; + db.onerror = errorHandler; + + info("Getting wasm"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + info("Verifying wasm module"); + + verifyWasmModule(request.result, wasmData.wasm); + yield undefined; + + info("Reading out contents of new compiled blob"); + + fileReader = new FileReader(); + fileReader.onload = continueToNextStepSync; + fileReader.readAsArrayBuffer(File.createFromNsIFile(file)); + + yield undefined; + + let newCompiledBuffer = fileReader.result; + + info("Verifying blobs differ"); + + ok(!compareBuffers(newCompiledBuffer, compiledBuffer), "Blobs differ"); + + info("Getting wasm again"); + + request = db.transaction([objectStoreName]) + .objectStore(objectStoreName).get(wasmData.key); + request.onsuccess = continueToNextStepSync; + yield undefined; + + info("Verifying wasm module"); + + verifyWasmModule(request.result, wasmData.wasm); + yield undefined; + + info("Reading contents of new compiled blob again"); + + fileReader = new FileReader(); + fileReader.onload = continueToNextStepSync; + fileReader.readAsArrayBuffer(File.createFromNsIFile(file)); + + yield undefined; + + let newCompiledBuffer2 = fileReader.result; + + info("Verifying blob didn't change"); + + ok(compareBuffers(newCompiledBuffer2, newCompiledBuffer), + "Blob didn't change"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_writer_starvation.js b/dom/indexedDB/test/unit/test_writer_starvation.js new file mode 100644 index 000000000..141bd1d93 --- /dev/null +++ b/dom/indexedDB/test/unit/test_writer_starvation.js @@ -0,0 +1,104 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +if (!this.window) { + this.runTest = function() { + todo(false, "Test disabled in xpcshell test suite for now"); + finishTest(); + } +} + +var testGenerator = testSteps(); + +function testSteps() +{ + const name = this.window ? window.location.pathname : "Splendid Test"; + + // Needs to be enough to saturate the thread pool. + const SYNC_REQUEST_COUNT = 25; + + let request = indexedDB.open(name, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + let db = event.target.result; + db.onerror = errorHandler; + + is(event.target.transaction.mode, "versionchange", "Correct mode"); + + let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + + request = objectStore.add({}); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + let key = event.target.result; + ok(key, "Got a key"); + + yield undefined; + + let continueReading = true; + let readerCount = 0; + let writerCount = 0; + let callbackCount = 0; + + // Generate a bunch of reads right away without returning to the event + // loop. + info("Generating " + SYNC_REQUEST_COUNT + " readonly requests"); + + for (let i = 0; i < SYNC_REQUEST_COUNT; i++) { + readerCount++; + let request = db.transaction("foo").objectStore("foo").get(key); + request.onsuccess = function(event) { + is(event.target.transaction.mode, "readonly", "Correct mode"); + callbackCount++; + }; + } + + while (continueReading) { + readerCount++; + info("Generating additional readonly request (" + readerCount + ")"); + let request = db.transaction("foo").objectStore("foo").get(key); + request.onsuccess = function(event) { + callbackCount++; + info("Received readonly request callback (" + callbackCount + ")"); + is(event.target.transaction.mode, "readonly", "Correct mode"); + if (callbackCount == SYNC_REQUEST_COUNT) { + writerCount++; + info("Generating 1 readwrite request with " + readerCount + + " previous readonly requests"); + let request = db.transaction("foo", "readwrite") + .objectStore("foo") + .add({}, readerCount); + request.onsuccess = function(event) { + callbackCount++; + info("Received readwrite request callback (" + callbackCount + ")"); + is(event.target.transaction.mode, "readwrite", "Correct mode"); + is(event.target.result, callbackCount, + "write callback came before later reads"); + } + } + else if (callbackCount == SYNC_REQUEST_COUNT + 5) { + continueReading = false; + } + }; + + setTimeout(function() { testGenerator.next(); }, writerCount ? 1000 : 100); + yield undefined; + } + + while (callbackCount < (readerCount + writerCount)) { + executeSoon(function() { testGenerator.next(); }); + yield undefined; + } + + is(callbackCount, readerCount + writerCount, "All requests accounted for"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/wasm_recompile_profile.zip b/dom/indexedDB/test/unit/wasm_recompile_profile.zip Binary files differnew file mode 100644 index 000000000..50ca3ef89 --- /dev/null +++ b/dom/indexedDB/test/unit/wasm_recompile_profile.zip diff --git a/dom/indexedDB/test/unit/xpcshell-child-process.ini b/dom/indexedDB/test/unit/xpcshell-child-process.ini new file mode 100644 index 000000000..970fe8c3d --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-child-process.ini @@ -0,0 +1,19 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +dupe-manifest = +head = xpcshell-head-child-process.js +tail = +skip-if = toolkit == 'android' || toolkit == 'gonk' +support-files = + GlobalObjectsChild.js + GlobalObjectsComponent.js + GlobalObjectsComponent.manifest + GlobalObjectsModule.jsm + GlobalObjectsSandbox.js + xpcshell-head-parent-process.js + xpcshell-shared.ini + +[include:xpcshell-shared.ini] diff --git a/dom/indexedDB/test/unit/xpcshell-head-child-process.js b/dom/indexedDB/test/unit/xpcshell-head-child-process.js new file mode 100644 index 000000000..2e704f8dc --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-head-child-process.js @@ -0,0 +1,27 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function run_test() { + const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; + + const INDEXEDDB_HEAD_FILE = "xpcshell-head-parent-process.js"; + const INDEXEDDB_PREF_EXPERIMENTAL = "dom.indexedDB.experimental"; + + // IndexedDB needs a profile. + do_get_profile(); + + let thisTest = _TEST_FILE.toString().replace(/\\/g, "/"); + thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1); + + _HEAD_FILES.push(do_get_file(INDEXEDDB_HEAD_FILE).path.replace(/\\/g, "/")); + + + let prefs = + Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService) + .getBranch(null); + prefs.setBoolPref(INDEXEDDB_PREF_EXPERIMENTAL, true); + + run_test_in_child(thisTest); +} diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js new file mode 100644 index 000000000..def791f52 --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -0,0 +1,700 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; + +if (!("self" in this)) { + this.self = this; +} + +const DOMException = Ci.nsIDOMDOMException; + +var bufferCache = []; + +function is(a, b, msg) { + do_check_eq(a, b, Components.stack.caller); +} + +function ok(cond, msg) { + do_check_true(!!cond, Components.stack.caller); +} + +function isnot(a, b, msg) { + do_check_neq(a, b, Components.stack.caller); +} + +function executeSoon(fun) { + do_execute_soon(fun); +} + +function todo(condition, name, diag) { + todo_check_true(condition, Components.stack.caller); +} + +function info(name, message) { + do_print(name); +} + +function run_test() { + runTest(); +}; + +if (!this.runTest) { + this.runTest = function() + { + if (SpecialPowers.isMainProcess()) { + // XPCShell does not get a profile by default. + do_get_profile(); + + enableTesting(); + enableExperimental(); + enableWasm(); + } + + Cu.importGlobalProperties(["indexedDB", "Blob", "File", "FileReader"]); + + do_test_pending(); + testGenerator.next(); + } +} + +function finishTest() +{ + if (SpecialPowers.isMainProcess()) { + resetWasm(); + resetExperimental(); + resetTesting(); + + SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", + "free"); + } + + SpecialPowers.removeFiles(); + + do_execute_soon(function(){ + testGenerator.close(); + do_test_finished(); + }) +} + +function grabEventAndContinueHandler(event) +{ + testGenerator.send(event); +} + +function continueToNextStep() +{ + do_execute_soon(function() { + testGenerator.next(); + }); +} + +function errorHandler(event) +{ + try { + dump("indexedDB error: " + event.target.error.name); + } catch(e) { + dump("indexedDB error: " + e); + } + do_check_true(false); + finishTest(); +} + +function unexpectedSuccessHandler() +{ + do_check_true(false); + finishTest(); +} + +function expectedErrorHandler(name) +{ + return function(event) { + do_check_eq(event.type, "error"); + do_check_eq(event.target.error.name, name); + event.preventDefault(); + grabEventAndContinueHandler(event); + }; +} + +function expectUncaughtException(expecting) +{ + // This is dummy for xpcshell test. +} + +function ExpectError(name, preventDefault) +{ + this._name = name; + this._preventDefault = preventDefault; +} +ExpectError.prototype = { + handleEvent: function(event) + { + do_check_eq(event.type, "error"); + do_check_eq(this._name, event.target.error.name); + if (this._preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } + grabEventAndContinueHandler(event); + } +}; + +function continueToNextStepSync() +{ + testGenerator.next(); +} + +function compareKeys(k1, k2) { + let t = typeof k1; + if (t != typeof k2) + return false; + + if (t !== "object") + return k1 === k2; + + if (k1 instanceof Date) { + return (k2 instanceof Date) && + k1.getTime() === k2.getTime(); + } + + if (k1 instanceof Array) { + if (!(k2 instanceof Array) || + k1.length != k2.length) + return false; + + for (let i = 0; i < k1.length; ++i) { + if (!compareKeys(k1[i], k2[i])) + return false; + } + + return true; + } + + return false; +} + +function addPermission(permission, url) +{ + throw "addPermission"; +} + +function removePermission(permission, url) +{ + throw "removePermission"; +} + +function allowIndexedDB(url) +{ + throw "allowIndexedDB"; +} + +function disallowIndexedDB(url) +{ + throw "disallowIndexedDB"; +} + +function enableExperimental() +{ + SpecialPowers.setBoolPref("dom.indexedDB.experimental", true); +} + +function resetExperimental() +{ + SpecialPowers.clearUserPref("dom.indexedDB.experimental"); +} + +function enableTesting() +{ + SpecialPowers.setBoolPref("dom.indexedDB.testing", true); +} + +function resetTesting() +{ + SpecialPowers.clearUserPref("dom.indexedDB.testing"); +} + +function enableWasm() +{ + SpecialPowers.setBoolPref("javascript.options.wasm", true); +} + +function resetWasm() +{ + SpecialPowers.clearUserPref("javascript.options.wasm"); +} + +function gc() +{ + Cu.forceGC(); + Cu.forceCC(); +} + +function scheduleGC() +{ + SpecialPowers.exactGC(continueToNextStep); +} + +function setTimeout(fun, timeout) { + let timer = Components.classes["@mozilla.org/timer;1"] + .createInstance(Components.interfaces.nsITimer); + var event = { + notify: function (timer) { + fun(); + } + }; + timer.initWithCallback(event, timeout, + Components.interfaces.nsITimer.TYPE_ONE_SHOT); + return timer; +} + +function resetOrClearAllDatabases(callback, clear) { + if (!SpecialPowers.isMainProcess()) { + throw new Error("clearAllDatabases not implemented for child processes!"); + } + + let quotaManagerService = Cc["@mozilla.org/dom/quota-manager-service;1"] + .getService(Ci.nsIQuotaManagerService); + + const quotaPref = "dom.quotaManager.testing"; + + let oldPrefValue; + if (SpecialPowers._getPrefs().prefHasUserValue(quotaPref)) { + oldPrefValue = SpecialPowers.getBoolPref(quotaPref); + } + + SpecialPowers.setBoolPref(quotaPref, true); + + let request; + + try { + if (clear) { + request = quotaManagerService.clear(); + } else { + request = quotaManagerService.reset(); + } + } catch(e) { + if (oldPrefValue !== undefined) { + SpecialPowers.setBoolPref(quotaPref, oldPrefValue); + } else { + SpecialPowers.clearUserPref(quotaPref); + } + throw e; + } + + request.callback = callback; +} + +function resetAllDatabases(callback) { + resetOrClearAllDatabases(callback, false); +} + +function clearAllDatabases(callback) { + resetOrClearAllDatabases(callback, true); +} + +function installPackagedProfile(packageName) +{ + let directoryService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + + let profileDir = directoryService.get("ProfD", Ci.nsIFile); + + let currentDir = directoryService.get("CurWorkD", Ci.nsIFile); + + let packageFile = currentDir.clone(); + packageFile.append(packageName + ".zip"); + + let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"] + .createInstance(Ci.nsIZipReader); + zipReader.open(packageFile); + + let entryNames = []; + let entries = zipReader.findEntries(null); + while (entries.hasMore()) { + let entry = entries.getNext(); + if (entry != "create_db.html") { + entryNames.push(entry); + } + } + entryNames.sort(); + + for (let entryName of entryNames) { + let zipentry = zipReader.getEntry(entryName); + + let file = profileDir.clone(); + let split = entryName.split("/"); + for(let i = 0; i < split.length; i++) { + file.append(split[i]); + } + + if (zipentry.isDirectory) { + file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); + } else { + let istream = zipReader.getInputStream(entryName); + + var ostream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, parseInt("0644", 8), 0); + + let bostream = Cc['@mozilla.org/network/buffered-output-stream;1'] + .createInstance(Ci.nsIBufferedOutputStream); + bostream.init(ostream, 32768); + + bostream.writeFrom(istream, istream.available()); + + istream.close(); + bostream.close(); + } + } + + zipReader.close(); +} + +function getChromeFilesDir() +{ + let dirService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + + let profileDir = dirService.get("ProfD", Ci.nsIFile); + + let idbDir = profileDir.clone(); + idbDir.append("storage"); + idbDir.append("permanent"); + idbDir.append("chrome"); + idbDir.append("idb"); + + let idbEntries = idbDir.directoryEntries; + while (idbEntries.hasMoreElements()) { + let entry = idbEntries.getNext(); + let file = entry.QueryInterface(Ci.nsIFile); + if (file.isDirectory()) { + return file; + } + } + + throw new Error("files directory doesn't exist!"); +} + +function getView(size) +{ + let buffer = new ArrayBuffer(size); + let view = new Uint8Array(buffer); + is(buffer.byteLength, size, "Correct byte length"); + return view; +} + +function getRandomView(size) +{ + let view = getView(size); + for (let i = 0; i < size; i++) { + view[i] = parseInt(Math.random() * 255) + } + return view; +} + +function getBlob(str) +{ + return new Blob([str], {type: "type/text"}); +} + +function getFile(name, type, str) +{ + return new File([str], name, {type: type}); +} + +function isWasmSupported() +{ + let testingFunctions = Cu.getJSTestingFunctions(); + return testingFunctions.wasmIsSupported(); +} + +function getWasmBinarySync(text) +{ + let testingFunctions = Cu.getJSTestingFunctions(); + let binary = testingFunctions.wasmTextToBinary(text); + return binary; +} + +function getWasmBinary(text) +{ + let binary = getWasmBinarySync(text); + executeSoon(function() { + testGenerator.send(binary); + }); +} + +function getWasmModule(binary) +{ + let module = new WebAssembly.Module(binary); + return module; +} + +function compareBuffers(buffer1, buffer2) +{ + if (buffer1.byteLength != buffer2.byteLength) { + return false; + } + + let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1); + let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2); + for (let i = 0; i < buffer1.byteLength; i++) { + if (view1[i] != view2[i]) { + return false; + } + } + return true; +} + +function verifyBuffers(buffer1, buffer2) +{ + ok(compareBuffers(buffer1, buffer2), "Correct buffer data"); +} + +function verifyBlob(blob1, blob2) +{ + is(blob1 instanceof Components.interfaces.nsIDOMBlob, true, + "Instance of nsIDOMBlob"); + is(blob1 instanceof File, blob2 instanceof File, + "Instance of DOM File"); + is(blob1.size, blob2.size, "Correct size"); + is(blob1.type, blob2.type, "Correct type"); + if (blob2 instanceof File) { + is(blob1.name, blob2.name, "Correct name"); + } + + let buffer1; + let buffer2; + + for (let i = 0; i < bufferCache.length; i++) { + if (bufferCache[i].blob == blob2) { + buffer2 = bufferCache[i].buffer; + break; + } + } + + if (!buffer2) { + let reader = new FileReader(); + reader.readAsArrayBuffer(blob2); + reader.onload = function(event) { + buffer2 = event.target.result; + bufferCache.push({ blob: blob2, buffer: buffer2 }); + if (buffer1) { + verifyBuffers(buffer1, buffer2); + testGenerator.next(); + } + } + } + + let reader = new FileReader(); + reader.readAsArrayBuffer(blob1); + reader.onload = function(event) { + buffer1 = event.target.result; + if (buffer2) { + verifyBuffers(buffer1, buffer2); + testGenerator.next(); + } + } +} + +function verifyMutableFile(mutableFile1, file2) +{ + is(mutableFile1 instanceof IDBMutableFile, true, + "Instance of IDBMutableFile"); + is(mutableFile1.name, file2.name, "Correct name"); + is(mutableFile1.type, file2.type, "Correct type"); + continueToNextStep(); +} + +function verifyView(view1, view2) +{ + is(view1.byteLength, view2.byteLength, "Correct byteLength"); + verifyBuffers(view1, view2); + continueToNextStep(); +} + +function verifyWasmModule(module1, module2) +{ + let testingFunctions = Cu.getJSTestingFunctions(); + let exp1 = testingFunctions.wasmExtractCode(module1); + let exp2 = testingFunctions.wasmExtractCode(module2); + let code1 = exp1.code; + let code2 = exp2.code; + ok(code1 instanceof Uint8Array, "Instance of Uint8Array"); + ok(code1.length == code2.length, "Correct length"); + verifyBuffers(code1, code2); + continueToNextStep(); +} + +function grabResultAndContinueHandler(request) +{ + testGenerator.send(request.result); +} + +function grabFileUsageAndContinueHandler(request) +{ + testGenerator.send(request.result.fileUsage); +} + +function getUsage(usageHandler, getAll) +{ + let qms = Cc["@mozilla.org/dom/quota-manager-service;1"] + .getService(Ci.nsIQuotaManagerService); + qms.getUsage(usageHandler, getAll) +} + +function getCurrentUsage(usageHandler) +{ + let qms = Cc["@mozilla.org/dom/quota-manager-service;1"] + .getService(Ci.nsIQuotaManagerService); + let principal = Cc["@mozilla.org/systemprincipal;1"] + .createInstance(Ci.nsIPrincipal); + qms.getUsageForPrincipal(principal, usageHandler); +} + +function setTemporaryStorageLimit(limit) +{ + const pref = "dom.quotaManager.temporaryStorage.fixedLimit"; + if (limit) { + info("Setting temporary storage limit to " + limit); + SpecialPowers.setIntPref(pref, limit); + } else { + info("Removing temporary storage limit"); + SpecialPowers.clearUserPref(pref); + } +} + +function setDataThreshold(threshold) +{ + info("Setting data threshold to " + threshold); + SpecialPowers.setIntPref("dom.indexedDB.dataThreshold", threshold); +} + +function setMaxSerializedMsgSize(aSize) +{ + info("Setting maximal size of a serialized message to " + aSize); + SpecialPowers.setIntPref("dom.indexedDB.maxSerializedMsgSize", aSize); +} + +function getPrincipal(url) +{ + let uri = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService) + .newURI(url, null, null); + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + return ssm.createCodebasePrincipal(uri, {}); +} + +var SpecialPowers = { + isMainProcess: function() { + return Components.classes["@mozilla.org/xre/app-info;1"] + .getService(Components.interfaces.nsIXULRuntime) + .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + }, + notifyObservers: function(subject, topic, data) { + var obsvc = Cc['@mozilla.org/observer-service;1'] + .getService(Ci.nsIObserverService); + obsvc.notifyObservers(subject, topic, data); + }, + notifyObserversInParentProcess: function(subject, topic, data) { + if (subject) { + throw new Error("Can't send subject to another process!"); + } + return this.notifyObservers(subject, topic, data); + }, + getBoolPref: function(prefName) { + return this._getPrefs().getBoolPref(prefName); + }, + setBoolPref: function(prefName, value) { + this._getPrefs().setBoolPref(prefName, value); + }, + setIntPref: function(prefName, value) { + this._getPrefs().setIntPref(prefName, value); + }, + clearUserPref: function(prefName) { + this._getPrefs().clearUserPref(prefName); + }, + // Copied (and slightly adjusted) from specialpowersAPI.js + exactGC: function(callback) { + let count = 0; + + function doPreciseGCandCC() { + function scheduledGCCallback() { + Components.utils.forceCC(); + + if (++count < 2) { + doPreciseGCandCC(); + } else { + callback(); + } + } + + Components.utils.schedulePreciseGC(scheduledGCCallback); + } + + doPreciseGCandCC(); + }, + + _getPrefs: function() { + var prefService = + Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService); + return prefService.getBranch(null); + }, + + get Cc() { + return Cc; + }, + + get Ci() { + return Ci; + }, + + get Cu() { + return Cu; + }, + + // Based on SpecialPowersObserver.prototype.receiveMessage + createFiles: function(requests, callback) { + let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + let filePaths = new Array; + if (!this._createdFiles) { + this._createdFiles = new Array; + } + let createdFiles = this._createdFiles; + requests.forEach(function(request) { + const filePerms = 0o666; + let testFile = dirSvc.get("ProfD", Ci.nsIFile); + if (request.name) { + testFile.append(request.name); + } else { + testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms); + } + let outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE + filePerms, 0); + if (request.data) { + outStream.write(request.data, request.data.length); + outStream.close(); + } + filePaths.push(File.createFromFileName(testFile.path, request.options)); + createdFiles.push(testFile); + }); + + setTimeout(function () { + callback(filePaths); + }, 0); + }, + + removeFiles: function() { + if (this._createdFiles) { + this._createdFiles.forEach(function (testFile) { + try { + testFile.remove(false); + } catch (e) {} + }); + this._createdFiles = null; + } + }, +}; diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini new file mode 100644 index 000000000..22bc861cc --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -0,0 +1,72 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +dupe-manifest = +head = xpcshell-head-parent-process.js +tail = +skip-if = toolkit == 'gonk' +support-files = + bug1056939_profile.zip + defaultStorageUpgrade_profile.zip + idbSubdirUpgrade1_profile.zip + idbSubdirUpgrade2_profile.zip + mutableFileUpgrade_profile.zip + oldDirectories_profile.zip + GlobalObjectsChild.js + GlobalObjectsComponent.js + GlobalObjectsComponent.manifest + GlobalObjectsModule.jsm + GlobalObjectsSandbox.js + getUsage_profile.zip + metadata2Restore_profile.zip + metadataRestore_profile.zip + schema18upgrade_profile.zip + schema21upgrade_profile.zip + schema23upgrade_profile.zip + snappyUpgrade_profile.zip + storagePersistentUpgrade_profile.zip + wasm_recompile_profile.zip + xpcshell-shared.ini + +[include:xpcshell-shared.ini] + +[test_blob_file_backed.js] +[test_bug1056939.js] +[test_cleanup_transaction.js] +[test_database_close_without_onclose.js] +[test_database_onclose.js] +[test_defaultStorageUpgrade.js] +[test_file_copy_failure.js] +[test_getUsage.js] +[test_idbSubdirUpgrade.js] +[test_globalObjects_ipc.js] +skip-if = toolkit == 'android' +[test_idle_maintenance.js] +[test_invalidate.js] +# disabled for the moment. +skip-if = true +[test_lowDiskSpace.js] +[test_maximal_serialized_object_size.js] +[test_metadata2Restore.js] +[test_metadataRestore.js] +[test_mutableFileUpgrade.js] +[test_oldDirectories.js] +[test_quotaExceeded_recovery.js] +[test_readwriteflush_disabled.js] +[test_schema18upgrade.js] +[test_schema21upgrade.js] +[test_schema23upgrade.js] +[test_snappyUpgrade.js] +[test_storagePersistentUpgrade.js] +[test_temporary_storage.js] +# bug 951017: intermittent failure on Android x86 emulator +skip-if = os == "android" && processor == "x86" +[test_view_put_get_values.js] +[test_wasm_cursors.js] +[test_wasm_getAll.js] +[test_wasm_index_getAllObjects.js] +[test_wasm_indexes.js] +[test_wasm_put_get_values.js] +[test_wasm_recompile.js] diff --git a/dom/indexedDB/test/unit/xpcshell-shared.ini b/dom/indexedDB/test/unit/xpcshell-shared.ini new file mode 100644 index 000000000..05359097b --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-shared.ini @@ -0,0 +1,96 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[test_abort_deleted_index.js] +[test_abort_deleted_objectStore.js] +[test_add_put.js] +[test_add_twice_failure.js] +[test_advance.js] +[test_autoIncrement.js] +[test_autoIncrement_indexes.js] +[test_blocked_order.js] +[test_clear.js] +[test_complex_keyPaths.js] +[test_count.js] +[test_create_index.js] +[test_create_index_with_integer_keys.js] +[test_create_locale_aware_index.js] +skip-if = toolkit == 'android' # bug 864843 +[test_create_objectStore.js] +[test_cursor_cycle.js] +[test_cursor_mutation.js] +[test_cursor_update_updates_indexes.js] +[test_cursors.js] +[test_deleteDatabase.js] +[test_deleteDatabase_interactions.js] +[test_deleteDatabase_onblocked.js] +[test_deleteDatabase_onblocked_duringVersionChange.js] +[test_event_source.js] +[test_getAll.js] +[test_globalObjects_other.js] +skip-if = toolkit == 'android' # bug 1079278 +[test_globalObjects_xpc.js] +[test_global_data.js] +[test_index_empty_keyPath.js] +[test_index_getAll.js] +[test_index_getAllObjects.js] +[test_index_object_cursors.js] +[test_index_update_delete.js] +[test_indexes.js] +[test_indexes_bad_values.js] +[test_indexes_funny_things.js] +[test_invalid_cursor.js] +[test_invalid_version.js] +[test_key_requirements.js] +[test_keys.js] +[test_locale_aware_indexes.js] +skip-if = toolkit == 'android' # bug 864843 +[test_locale_aware_index_getAll.js] +skip-if = toolkit == 'android' # bug 864843 +[test_locale_aware_index_getAllObjects.js] +skip-if = toolkit == 'android' # bug 864843 +[test_multientry.js] +[test_names_sorted.js] +[test_object_identity.js] +[test_objectCursors.js] +[test_objectStore_getAllKeys.js] +[test_objectStore_inline_autoincrement_key_added_on_put.js] +[test_objectStore_openKeyCursor.js] +[test_objectStore_remove_values.js] +[test_odd_result_order.js] +[test_open_empty_db.js] +[test_open_for_principal.js] +[test_open_objectStore.js] +[test_optionalArguments.js] +[test_overlapping_transactions.js] +[test_persistenceType.js] +[test_put_get_values.js] +[test_put_get_values_autoIncrement.js] +[test_readonly_transactions.js] +[test_remove_index.js] +[test_rename_index.js] +[test_rename_index_errors.js] +[test_remove_objectStore.js] +[test_rename_objectStore.js] +[test_rename_objectStore_errors.js] +[test_request_readyState.js] +[test_sandbox.js] +[test_setVersion.js] +[test_setVersion_abort.js] +[test_setVersion_events.js] +[test_setVersion_exclusion.js] +[test_setVersion_throw.js] +[test_success_events_after_abort.js] +[test_table_locks.js] +[test_table_rollback.js] +[test_traffic_jam.js] +[test_transaction_abort.js] +[test_transaction_abort_hang.js] +[test_transaction_duplicate_store_names.js] +[test_transaction_error.js] +[test_transaction_lifetimes.js] +[test_transaction_lifetimes_nested.js] +[test_transaction_ordering.js] +[test_unique_index_update.js] +[test_writer_starvation.js] |