summaryrefslogtreecommitdiffstats
path: root/LayoutTests/storage
diff options
context:
space:
mode:
Diffstat (limited to 'LayoutTests/storage')
-rw-r--r--LayoutTests/storage/change-version-handle-reuse-expected.txt2
-rw-r--r--LayoutTests/storage/change-version-handle-reuse.html69
-rw-r--r--LayoutTests/storage/change-version-handle-reuse.js46
-rw-r--r--LayoutTests/storage/change-version-no-crash-on-preflight-failure-expected.txt3
-rw-r--r--LayoutTests/storage/change-version-no-crash-on-preflight-failure.html36
-rw-r--r--LayoutTests/storage/change-version.html80
-rw-r--r--LayoutTests/storage/database-lock-after-reload.html34
-rw-r--r--LayoutTests/storage/domstorage/complex-values-expected.txt36
-rw-r--r--LayoutTests/storage/domstorage/events/documentURI-expected.txt6
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/documentURI.js8
-rw-r--r--LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html2
-rw-r--r--LayoutTests/storage/domstorage/quota-expected.txt2
-rw-r--r--LayoutTests/storage/domstorage/script-tests/quota.js6
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html2
-rw-r--r--LayoutTests/storage/execute-sql-args.html108
-rw-r--r--LayoutTests/storage/execute-sql-args.js86
-rw-r--r--LayoutTests/storage/executesql-accepts-only-one-statement-expected.txt3
-rw-r--r--LayoutTests/storage/executesql-accepts-only-one-statement.html78
-rw-r--r--LayoutTests/storage/hash-change-with-xhr-expected.txt1
-rw-r--r--LayoutTests/storage/hash-change-with-xhr.html140
-rw-r--r--LayoutTests/storage/hash-change-with-xhr.js112
-rw-r--r--LayoutTests/storage/indexeddb/basics-expected.txt34
-rw-r--r--LayoutTests/storage/indexeddb/basics.html64
-rw-r--r--LayoutTests/storage/indexeddb/constants-expected.txt35
-rw-r--r--LayoutTests/storage/indexeddb/constants.html58
-rw-r--r--LayoutTests/storage/indexeddb/database-basics-expected.txt129
-rw-r--r--LayoutTests/storage/indexeddb/database-basics.html132
-rw-r--r--LayoutTests/storage/indexeddb/database-quota-expected.txt87
-rw-r--r--LayoutTests/storage/indexeddb/database-quota.html124
-rw-r--r--LayoutTests/storage/indexeddb/duplicates-expected.txt541
-rw-r--r--LayoutTests/storage/indexeddb/duplicates.html213
-rw-r--r--LayoutTests/storage/indexeddb/index-basics-expected.txt310
-rw-r--r--LayoutTests/storage/indexeddb/index-basics.html246
-rw-r--r--LayoutTests/storage/indexeddb/index-cursor-expected.txt2102
-rw-r--r--LayoutTests/storage/indexeddb/index-cursor.html266
-rw-r--r--LayoutTests/storage/indexeddb/keyrange-expected.txt159
-rw-r--r--LayoutTests/storage/indexeddb/keyrange.html171
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-basics-expected.txt191
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-basics.html241
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-cursor-expected.txt880
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-cursor.html243
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-removeobjectstore-expected.txt129
-rw-r--r--LayoutTests/storage/indexeddb/objectstore-removeobjectstore.html126
-rw-r--r--LayoutTests/storage/indexeddb/open-cursor-expected.txt100
-rw-r--r--LayoutTests/storage/indexeddb/open-cursor.html110
-rw-r--r--LayoutTests/storage/indexeddb/queued-commands-expected.txt104
-rw-r--r--LayoutTests/storage/indexeddb/queued-commands.html87
-rw-r--r--LayoutTests/storage/indexeddb/resources/shared.js98
-rw-r--r--LayoutTests/storage/indexeddb/transaction-basics-expected.txt186
-rw-r--r--LayoutTests/storage/indexeddb/transaction-basics.html254
-rw-r--r--LayoutTests/storage/indexeddb/tutorial-expected.txt4
-rw-r--r--LayoutTests/storage/indexeddb/tutorial.html437
-rw-r--r--LayoutTests/storage/multiple-databases-garbage-collection.html77
-rw-r--r--LayoutTests/storage/multiple-databases-garbage-collection.js56
-rw-r--r--LayoutTests/storage/multiple-transactions-on-different-handles.html96
-rw-r--r--LayoutTests/storage/multiple-transactions-on-different-handles.js78
-rw-r--r--LayoutTests/storage/multiple-transactions.html53
-rw-r--r--LayoutTests/storage/multiple-transactions.js32
-rw-r--r--LayoutTests/storage/null-callbacks-expected.txt2
-rw-r--r--LayoutTests/storage/null-callbacks.html46
-rw-r--r--LayoutTests/storage/open-database-creation-callback-expected.txt2
-rw-r--r--LayoutTests/storage/open-database-creation-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/open-database-creation-callback-isolated-world.html46
-rw-r--r--LayoutTests/storage/open-database-creation-callback.html91
-rw-r--r--LayoutTests/storage/open-database-over-quota-expected.txt2
-rw-r--r--LayoutTests/storage/open-database-over-quota.html24
-rw-r--r--LayoutTests/storage/open-database-while-transaction-in-progress.html71
-rw-r--r--LayoutTests/storage/open-database-while-transaction-in-progress.js41
-rw-r--r--LayoutTests/storage/private-browsing-readonly.html1
-rw-r--r--LayoutTests/storage/quota-tracking-expected.txt2
-rw-r--r--LayoutTests/storage/quota-tracking.html26
-rw-r--r--LayoutTests/storage/read-and-write-transactions-dont-run-together.html103
-rw-r--r--LayoutTests/storage/read-and-write-transactions-dont-run-together.js81
-rw-r--r--LayoutTests/storage/resources/database-common.js29
-rw-r--r--LayoutTests/storage/resources/database-lock-after-reload-2.html18
-rw-r--r--LayoutTests/storage/sql-error-codes-expected.txt11
-rw-r--r--LayoutTests/storage/sql-error-codes.html12
-rw-r--r--LayoutTests/storage/sql-error-codes.js114
-rw-r--r--LayoutTests/storage/statement-error-callback-expected.txt12
-rw-r--r--LayoutTests/storage/statement-error-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/statement-error-callback-isolated-world.html52
-rw-r--r--LayoutTests/storage/statement-error-callback.html75
-rw-r--r--LayoutTests/storage/statement-success-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/statement-success-callback-isolated-world.html52
-rw-r--r--LayoutTests/storage/test-authorizer.html175
-rw-r--r--LayoutTests/storage/test-authorizer.js153
-rw-r--r--LayoutTests/storage/transaction-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/transaction-callback-isolated-world.html48
-rw-r--r--LayoutTests/storage/transaction-error-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/transaction-error-callback-isolated-world.html52
-rw-r--r--LayoutTests/storage/transaction-success-callback-isolated-world-expected.txt4
-rw-r--r--LayoutTests/storage/transaction-success-callback-isolated-world.html48
92 files changed, 9548 insertions, 978 deletions
diff --git a/LayoutTests/storage/change-version-handle-reuse-expected.txt b/LayoutTests/storage/change-version-handle-reuse-expected.txt
index 805c9fb..d0c3444 100644
--- a/LayoutTests/storage/change-version-handle-reuse-expected.txt
+++ b/LayoutTests/storage/change-version-handle-reuse-expected.txt
@@ -1,4 +1,4 @@
-This tests that a database can be accessed after changing its version. You should see an error about FooBar table below, not about mismatching versions. Also, reloading the page should not cause an assertion failure.
+This tests that a database can be accessed after changing its version. You should see an error about FooBar table below, not about mismatching versions.
changeVersion: transaction callback
changeVersion: success callback
transaction: statement error callback: no such table: FooBar
diff --git a/LayoutTests/storage/change-version-handle-reuse.html b/LayoutTests/storage/change-version-handle-reuse.html
index de3a5f4..af3fbf4 100644
--- a/LayoutTests/storage/change-version-handle-reuse.html
+++ b/LayoutTests/storage/change-version-handle-reuse.html
@@ -1,70 +1,11 @@
<html>
<head>
-<script>
-function log(message)
-{
- document.getElementById("result").innerText += message + "\n";
-}
-
-function finishTest()
-{
- log("TEST COMPLETE.");
-
- if (window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.waitUntilDone();
- layoutTestController.dumpAsText();
- }
-
- document.getElementById("result").innerText = "";
-
- try {
- db = openDatabase("ChangeVersion", "", "Test that changing a database version doesn\'t kill our handle to it", 1);
- var version = db.version;
- var newVersion = version ? (parseInt(version) + 1).toString() : "1";
- db.changeVersion(version, newVersion, function(tx) {
- log("changeVersion: transaction callback");
- }, function(error) {
- log("changeVersion: error callback: " + error.message);
- }, function() {
- log("changeVersion: success callback");
- });
-
- setTimeout(runTest2, 1000);
-
- } catch (e) {
- log("changeVersion exception: " + e);
- finishTest();
- }
-}
-
-function runTest2()
-{
- try {
- db.transaction(function(tx) {
- tx.executeSql("SELECT * from FooBar", [], function(tx) {
- log("transaction: statement callback");
- finishTest();
- }, function(tx, error) {
- log("transaction: statement error callback: " + error.message);
- finishTest();
- });
- });
- } catch (e) {
- log("transaction exception: " + e);
- finishTest();
- }
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="change-version-handle-reuse.js"></script>
</head>
-<body onload="runTest()">
-<div>This tests that a database can be accessed after changing its version. You should see an error about FooBar table below, not about mismatching versions. Also, reloading the page should not cause an assertion failure.
-<pre id="result">
+<body onload="setupAndRunTest()">
+<div>This tests that a database can be accessed after changing its version. You should see an error about FooBar table below, not about mismatching versions.
+<pre id="console">
FAILURE: test didn't run.
</pre>
</body>
diff --git a/LayoutTests/storage/change-version-handle-reuse.js b/LayoutTests/storage/change-version-handle-reuse.js
new file mode 100644
index 0000000..847a58c
--- /dev/null
+++ b/LayoutTests/storage/change-version-handle-reuse.js
@@ -0,0 +1,46 @@
+function finishTest()
+{
+ log("TEST COMPLETE.");
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ try {
+ db = openDatabaseWithSuffix("ChangeVersion", "", "Test that changing a database version doesn't kill our handle to it", 1);
+ var version = db.version;
+ var newVersion = version ? (parseInt(version) + 1).toString() : "1";
+ db.changeVersion(version, newVersion, function(tx) {
+ log("changeVersion: transaction callback");
+ }, function(error) {
+ log("changeVersion: error callback: " + error.message);
+ }, function() {
+ log("changeVersion: success callback");
+ runTest2();
+ });
+
+ } catch (e) {
+ log("changeVersion exception: " + e);
+ finishTest();
+ }
+}
+
+function runTest2()
+{
+ try {
+ db.transaction(function(tx) {
+ tx.executeSql("SELECT * from FooBar", [], function(tx) {
+ log("transaction: statement callback");
+ finishTest();
+ }, function(tx, error) {
+ log("transaction: statement error callback: " + error.message);
+ finishTest();
+ });
+ });
+ } catch (e) {
+ log("transaction exception: " + e);
+ finishTest();
+ }
+}
diff --git a/LayoutTests/storage/change-version-no-crash-on-preflight-failure-expected.txt b/LayoutTests/storage/change-version-no-crash-on-preflight-failure-expected.txt
new file mode 100644
index 0000000..bb0d549
--- /dev/null
+++ b/LayoutTests/storage/change-version-no-crash-on-preflight-failure-expected.txt
@@ -0,0 +1,3 @@
+This test verifies that no assertion is triggered when changeVersion()'s preflight step fails.
+PASS: db.changeVersion() failed as expected, and no assertions were triggered.
+
diff --git a/LayoutTests/storage/change-version-no-crash-on-preflight-failure.html b/LayoutTests/storage/change-version-no-crash-on-preflight-failure.html
new file mode 100644
index 0000000..2a512fb
--- /dev/null
+++ b/LayoutTests/storage/change-version-no-crash-on-preflight-failure.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script>
+function finishTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function log(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ var db = window.openDatabase("ChangeVersionFailureTest", "1", "Test the preflight step", 1024);
+ db.changeVersion("2", "3", null, function(error) {
+ log("PASS: db.changeVersion() failed as expected, and no assertions were triggered.");
+ finishTest();
+ }, function() {
+ log("FAIL: db.changeVersion() was expected to fail.");
+ finishTest();
+ });
+}
+</script>
+</head>
+<body onload="runTest();">
+This test verifies that no assertion is triggered when changeVersion()'s preflight step fails.
+<pre id="console"></pre>
+</body>
+</html>
diff --git a/LayoutTests/storage/change-version.html b/LayoutTests/storage/change-version.html
index d81c6b5..79bb9df 100644
--- a/LayoutTests/storage/change-version.html
+++ b/LayoutTests/storage/change-version.html
@@ -1,26 +1,26 @@
-<html>
+<html>
<head>
<title>Test database.changeVersion</title>
-<script>
-var db;
+<script>
+var db1;
var EXPECTED_VERSION_AFTER_HIXIE_TEST = '2';
var EXPECTED_VERSION_AFTER_RELOAD = '3';
-
+
function emptyFunction() { }
-
+
function changeVersionCallback(tx)
{
tx.executeSql("DROP table if exists info;", [], emptyFunction, emptyFunction);
tx.executeSql("CREATE table if not exists info (version INTEGER);", [], emptyFunction, emptyFunction);
tx.executeSql("INSERT into info values(?);", [EXPECTED_VERSION_AFTER_RELOAD], emptyFunction, emptyFunction);
}
-
+
function changeVersionSuccess()
{
- log("Successfully changed version to ' + db.version + '. Reloading.");
+ log("Successfully changed version to " + db1.version + ". Reloading.");
window.location.href = window.location + '?2';
}
-
+
function changeVersionError(error)
{
log("Error: " + error.message);
@@ -42,8 +42,12 @@ function log(message)
function runTest()
{
if (window.location.search == "?2") {
- db = window.openDatabase("changeversion-test", "", "Test for the database.changeVersion() function", 1024);
- log("Finished tests with version " + db.version + "; expected version: " + EXPECTED_VERSION_AFTER_RELOAD);
+ db1 = window.openDatabase("changeversion-test", "", "Test for the database.changeVersion() function", 1024);
+ log("Finished tests with version " + db1.version + "; expected version: " + EXPECTED_VERSION_AFTER_RELOAD);
+
+ // Reset the DB version or the next run might fail.
+ db1.changeVersion(db1.version, "1");
+
finishTest();
} else
testPart1();
@@ -55,31 +59,43 @@ function testPart1() {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
-
- db = window.openDatabase("changeversion-test", "", "Test for the database.changeVersion() function", 1024);
- if (db.version != EXPECTED_VERSION_AFTER_RELOAD) {
- // First run Hixie's test to ensure basic changeVersion functionality works (see bug 28418).
- db.changeVersion("", EXPECTED_VERSION_AFTER_HIXIE_TEST, emptyFunction, function (e) {
- log('FAIL in changeVersion:' + e);
- finishTest();
- }, function () {
- try {
- var db2 = openDatabase("change-version-test", EXPECTED_VERSION_AFTER_HIXIE_TEST, "", 0);
- } catch (e) {
- log('FAIL in openDatabase: ' + e);
- finishTest();
- }
- // The two database versions should match.
- if (db.version == db2.version)
- log("PASS: db.version(" + db.version + ") matches db2.version(" + db2.version +") as expected.");
- else
- log("FAIL: db.version(" + db.version + ") does not match db2.version(" + db2.version +")");
+ db1 = window.openDatabase("changeversion-test", "1", "Test for the database.changeVersion() function", 1024);
+ var db2 = window.openDatabase("changeversion-test", "1", "Test for the database.changeVersion() function", 1024);
- // Now try a test to ensure the version persists after reloading (see bug 27836)
- db.changeVersion(EXPECTED_VERSION_AFTER_HIXIE_TEST, EXPECTED_VERSION_AFTER_RELOAD, changeVersionCallback, changeVersionError, changeVersionSuccess);
+ // First run Hixie's test to ensure basic changeVersion functionality works (see bug 28418).
+ db1.changeVersion("1", EXPECTED_VERSION_AFTER_HIXIE_TEST, null, function (e) {
+ log("FAIL in changeVersion:" + e);
+ finishTest();
+ }, function () {
+ // Make sure the version change has propagated to db2 too.
+ // All transactions on db2 should fail.
+ if (db2.version != db1.version) {
+ log("FAIL: changing db1's version (" + db1.version + ") did not change db2's version (" + db2.version + ") as expected.");
+ finishTest();
+ }
+ db2.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo INT)");
+ }, function(error) { }, function() {
+ log("FAIL: The DB version changed, all transactions on db2 should fail.");
+ finishTest();
});
- }
+
+ // Make sure any new handle to the same DB sees the new version
+ try {
+ var db3 = openDatabase("change-version-test", EXPECTED_VERSION_AFTER_HIXIE_TEST, "", 0);
+ } catch (e) {
+ log("FAIL in openDatabase: " + e);
+ finishTest();
+ }
+ if (db1.version != db3.version) {
+ log("FAIL: db.version(" + db1.version + ") does not match db3.version(" + db3.version +")");
+ finishTest();
+ }
+
+ // Now try a test to ensure the version persists after reloading (see bug 27836)
+ db1.changeVersion(EXPECTED_VERSION_AFTER_HIXIE_TEST, EXPECTED_VERSION_AFTER_RELOAD, changeVersionCallback, changeVersionError, changeVersionSuccess);
+ });
}
</script>
</head>
diff --git a/LayoutTests/storage/database-lock-after-reload.html b/LayoutTests/storage/database-lock-after-reload.html
index ad34d5b..5b5989f 100644
--- a/LayoutTests/storage/database-lock-after-reload.html
+++ b/LayoutTests/storage/database-lock-after-reload.html
@@ -1,8 +1,6 @@
<html>
<head>
<script>
-var database;
-
function log(message)
{
document.getElementById("console").innerHTML += message + "<br>";
@@ -15,7 +13,7 @@ function finishTest()
layoutTestController.notifyDone();
}
-function errorFunction(tx, error)
+function errorFunction(error)
{
log("Test failed - " + error.message);
finishTest();
@@ -23,16 +21,16 @@ function errorFunction(tx, error)
function addData(db)
{
- db.transaction(function(tx) {
+ db.transaction(function(tx) {
log("Inserting some data");
- // Insert a large amount of data that will take a little while to run. Schedule a timout to run that will load a new page
- // whilst the transaction is still in progress, interrupting the transaction. This should not leave the database locked and on
- // the next page we should be able to insert some more data.
- tx.executeSql("INSERT INTO DataTest (testData) VALUES (randomBlob(524200 ))", [], function(tx, result) { }, errorFunction);
- location.href = "./resources/database-lock-after-reload-2.html";
- }, errorFunction, function() {
- finishTest();
- });
+ // Load a new page while the transaction is still in progress, interrupting the transaction.
+ // This should not leave the database locked and on the next page we should be able to insert
+ // some more data.
+ tx.executeSql("INSERT INTO DataTest (testData) VALUES (ZEROBLOB(524200))", [],
+ function(tx, result) { location.href = "./resources/database-lock-after-reload-2.html"; },
+ function(tx, error) { errorFunction(error); });
+ tx.executeSql("INSERT INTO DataTest (testData) VALUES (ZEROBLOB(524200))");
+ }, errorFunction, function() { finishTest(); });
}
function runTest()
@@ -42,20 +40,20 @@ function runTest()
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
-
+
+ var database;
try {
database = openDatabase("DatabaseLockTest", "1.0", "Test for database locking", 5242880);
} catch (e) {
log("Error - could not open database");
finishTest();
}
-
+
database.transaction(function(tx) {
log("Adding a table");
- tx.executeSql("CREATE TABLE DataTest (testData)", [], function(tx, result) { }, errorFunction);
- }, errorFunction, function() {
- addData(database);
- });
+ tx.executeSql("CREATE TABLE DataTest (testData)", [], function(tx, result) { },
+ function(tx, error) { errorFunction(error); });
+ }, errorFunction, function() { addData(database); });
}
</script>
diff --git a/LayoutTests/storage/domstorage/complex-values-expected.txt b/LayoutTests/storage/domstorage/complex-values-expected.txt
index 69d7644..e1665e6 100644
--- a/LayoutTests/storage/domstorage/complex-values-expected.txt
+++ b/LayoutTests/storage/domstorage/complex-values-expected.txt
@@ -82,25 +82,25 @@ PASS storage.getItem('foo9') is "2"
storage.foo10 = k
PASS typeof storage['foo10'] is "string"
-PASS storage['foo10'] is "ÿ찡hello"
+PASS storage['foo10'] is "ÿ찡\0hello"
PASS typeof storage.foo10 is "string"
-PASS storage.foo10 is "ÿ찡hello"
+PASS storage.foo10 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo10') is "string"
-PASS storage.getItem('foo10') is "ÿ찡hello"
+PASS storage.getItem('foo10') is "ÿ찡\0hello"
storage['foo11'] = k
PASS typeof storage['foo11'] is "string"
-PASS storage['foo11'] is "ÿ찡hello"
+PASS storage['foo11'] is "ÿ찡\0hello"
PASS typeof storage.foo11 is "string"
-PASS storage.foo11 is "ÿ찡hello"
+PASS storage.foo11 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo11') is "string"
-PASS storage.getItem('foo11') is "ÿ찡hello"
+PASS storage.getItem('foo11') is "ÿ찡\0hello"
storage.setItem('foo12', k)
PASS typeof storage['foo12'] is "string"
-PASS storage['foo12'] is "ÿ찡hello"
+PASS storage['foo12'] is "ÿ찡\0hello"
PASS typeof storage.foo12 is "string"
-PASS storage.foo12 is "ÿ찡hello"
+PASS storage.foo12 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo12') is "string"
-PASS storage.getItem('foo12') is "ÿ찡hello"
+PASS storage.getItem('foo12') is "ÿ찡\0hello"
Testing localStorage
@@ -182,25 +182,25 @@ PASS storage.getItem('foo9') is "2"
storage.foo10 = k
PASS typeof storage['foo10'] is "string"
-PASS storage['foo10'] is "ÿ찡hello"
+PASS storage['foo10'] is "ÿ찡\0hello"
PASS typeof storage.foo10 is "string"
-PASS storage.foo10 is "ÿ찡hello"
+PASS storage.foo10 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo10') is "string"
-PASS storage.getItem('foo10') is "ÿ찡hello"
+PASS storage.getItem('foo10') is "ÿ찡\0hello"
storage['foo11'] = k
PASS typeof storage['foo11'] is "string"
-PASS storage['foo11'] is "ÿ찡hello"
+PASS storage['foo11'] is "ÿ찡\0hello"
PASS typeof storage.foo11 is "string"
-PASS storage.foo11 is "ÿ찡hello"
+PASS storage.foo11 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo11') is "string"
-PASS storage.getItem('foo11') is "ÿ찡hello"
+PASS storage.getItem('foo11') is "ÿ찡\0hello"
storage.setItem('foo12', k)
PASS typeof storage['foo12'] is "string"
-PASS storage['foo12'] is "ÿ찡hello"
+PASS storage['foo12'] is "ÿ찡\0hello"
PASS typeof storage.foo12 is "string"
-PASS storage.foo12 is "ÿ찡hello"
+PASS storage.foo12 is "ÿ찡\0hello"
PASS typeof storage.getItem('foo12') is "string"
-PASS storage.getItem('foo12') is "ÿ찡hello"
+PASS storage.getItem('foo12') is "ÿ찡\0hello"
PASS successfullyParsed is true
TEST COMPLETE
diff --git a/LayoutTests/storage/domstorage/events/documentURI-expected.txt b/LayoutTests/storage/domstorage/events/documentURI-expected.txt
index 5952523..d53d4dc 100644
--- a/LayoutTests/storage/domstorage/events/documentURI-expected.txt
+++ b/LayoutTests/storage/domstorage/events/documentURI-expected.txt
@@ -1,4 +1,4 @@
-Test that changing documentURI has no effects on the uri passed into storage events.
+Test that changing documentURI has no effects on the url passed into storage events.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -10,7 +10,7 @@ Reset storage event list
storageEventList = new Array()
storage.foo = '123'
PASS storageEventList.length is 1
-Saving URI
+Saving url
document.documentURI = 'abc'
PASS document.documentURI is "abc"
storage.foo = 'xyz'
@@ -25,7 +25,7 @@ Reset storage event list
storageEventList = new Array()
storage.foo = '123'
PASS storageEventList.length is 1
-Saving URI
+Saving url
document.documentURI = 'abc'
PASS document.documentURI is "abc"
storage.foo = 'xyz'
diff --git a/LayoutTests/storage/domstorage/events/script-tests/documentURI.js b/LayoutTests/storage/domstorage/events/script-tests/documentURI.js
index 4b4f4cb..8df6fe9 100644
--- a/LayoutTests/storage/domstorage/events/script-tests/documentURI.js
+++ b/LayoutTests/storage/domstorage/events/script-tests/documentURI.js
@@ -1,4 +1,4 @@
-description("Test that changing documentURI has no effects on the uri passed into storage events.");
+description("Test that changing documentURI has no effects on the url passed into storage events.");
function test(storageString, callback)
{
@@ -29,8 +29,8 @@ function step1()
function step2()
{
shouldBe("storageEventList.length", "1");
- debug("Saving URI");
- window.lastURI = storageEventList[0].uri;
+ debug("Saving url");
+ window.lastURL = storageEventList[0].url;
evalAndLog("document.documentURI = 'abc'");
shouldBeEqualToString("document.documentURI", "abc");
@@ -42,7 +42,7 @@ function step2()
function step3()
{
shouldBe("storageEventList.length", "2");
- shouldBeTrue(String(window.lastURI == storageEventList[1].uri));
+ shouldBeTrue(String(window.lastURL == storageEventList[1].url));
completionCallback();
}
diff --git a/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html b/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html
index 2ab8ba1..bed860f 100644
--- a/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html
+++ b/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html
@@ -7,7 +7,7 @@ function handleStorageEvent(e)
parent.log("Key - " + e.key);
parent.log("New Value - " + e.newValue);
parent.log("Old Value - " + e.oldValue);
- parent.log("URI - " + parent.normalizeURL(e.uri));
+ parent.log("url - " + parent.normalizeURL(e.url));
parent.log("Storage Area - " + ((e.storageArea == window.localStorage) ? "This window's window.localStorage" : "Another window's window.localStorage"));
parent.log("");
diff --git a/LayoutTests/storage/domstorage/quota-expected.txt b/LayoutTests/storage/domstorage/quota-expected.txt
index 248ff79..1836cc1 100644
--- a/LayoutTests/storage/domstorage/quota-expected.txt
+++ b/LayoutTests/storage/domstorage/quota-expected.txt
@@ -8,7 +8,7 @@ storage.clear()
PASS storage.length is 0
Creating 'data' which contains 64K of data
PASS data.length is 65536
-Putting 'data' into 40 sessionStorage buckets.
+Putting 'data' into 39 sessionStorage buckets.
Putting 'data' into another bucket.h
PASS Insertion worked.
diff --git a/LayoutTests/storage/domstorage/script-tests/quota.js b/LayoutTests/storage/domstorage/script-tests/quota.js
index ad9afe9..21f3e39 100644
--- a/LayoutTests/storage/domstorage/script-tests/quota.js
+++ b/LayoutTests/storage/domstorage/script-tests/quota.js
@@ -65,13 +65,13 @@ function testNoQuota(storageString)
data += data;
shouldBe("data.length", "65536");
- debug("Putting 'data' into 40 " + storageString + " buckets.");
- for (var i=0; i<40; i++)
+ debug("Putting 'data' into 39 " + storageString + " buckets.");
+ for (var i=0; i<39; i++)
storage[i] = data;
debug("Putting 'data' into another bucket.h");
try {
- storage[40] = data;
+ storage[39] = data;
testPassed("Insertion worked.");
} catch (e) {
testFailed("Exception: " + e);
diff --git a/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html b/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html
index 2d7b004..97781b1 100644
--- a/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html
+++ b/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html
@@ -7,7 +7,7 @@ function handleStorageEvent(e)
parent.log("Key - " + e.key);
parent.log("New Value - " + e.newValue);
parent.log("Old Value - " + e.oldValue);
- parent.log("URI - " + parent.normalizeURL(e.uri));
+ parent.log("url - " + parent.normalizeURL(e.url));
parent.log("Storage Area - " + ((e.storageArea == window.sessionStorage) ? "This window's window.sessionStorage" : "Another window's window.sessionStorage"));
parent.log("");
diff --git a/LayoutTests/storage/execute-sql-args.html b/LayoutTests/storage/execute-sql-args.html
index 43814c6..b9f2a43 100644
--- a/LayoutTests/storage/execute-sql-args.html
+++ b/LayoutTests/storage/execute-sql-args.html
@@ -1,109 +1,13 @@
<html>
-
<head>
-<script>
-
-var throwOnToStringObject = { };
-throwOnToStringObject.toString = function () { throw "Cannot call toString on this object." };
-
-var throwOnGetLengthObject = { };
-throwOnGetLengthObject.__defineGetter__("length", function () { throw "Cannot get length of this object."; });
-
-var throwOnGetZeroObject = { length: 1 };
-throwOnGetZeroObject.__defineGetter__("0", function () { throw "Cannot get 0 property of this object."; });
-
-var expectNoException = [
- 'null',
- 'undefined',
- '0',
- '""',
- '"", null',
- '"", undefined',
- '"", []',
- '"", [ "arg0" ]',
- '"", { }',
- '"", { length: 0 }',
- '"", { length: 1, 0: "arg0" }',
- '"", null, null',
- '"", null, undefined',
- '"", null, { }',
- '"", null, null, null',
- '"", null, null, undefined',
- '"", null, null, { }',
-];
-
-var expectException = [
- '',
- 'throwOnToStringObject',
- '"", throwOnGetLengthObject',
- '"", throwOnGetZeroObject',
- '"", [ throwOnToStringObject ]',
- '"", 0',
- '"", ""',
- '"", null, 0',
- '"", null, ""',
- '"", null, null, 0',
- '"", null, null, ""',
-];
-
-function writeMessageToLog(message)
-{
- document.getElementById("console").innerText += message + "\n";
-}
-
-function tryExecuteSql(transaction, parameterList)
-{
- try {
- eval('transaction.executeSql(' + parameterList + ')');
- return null;
- } catch (exception) {
- return exception;
- }
-}
-
-function runTransactionTest(transaction, parameterList, expectException)
-{
- var exception = tryExecuteSql(transaction, parameterList);
- if (expectException) {
- if (exception)
- writeMessageToLog("PASS. executeSql(" + parameterList + ") threw an exception as expected.");
- else
- writeMessageToLog("*FAIL*. executeSql(" + parameterList + ") did not throw an exception");
- } else {
- if (exception)
- writeMessageToLog("*FAIL*. executeSql(" + parameterList + ") threw an exception: " + exception);
- else
- writeMessageToLog("PASS. executeSql(" + parameterList + ") did not throw an exception");
- }
-}
-
-function runTransactionTests(transaction)
-{
- for (i in expectNoException)
- runTransactionTest(transaction, expectNoException[i], false);
- for (i in expectException)
- runTransactionTest(transaction, expectException[i], true);
-
- if (window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- var db = openDatabase("ExecuteSQLArgsTest", "1.0", "Test of handling of the arguments to SQLTransaction.executeSql", 1);
- db.transaction(runTransactionTests);
-}
-
-</script>
+<script src="resources/database-common.js"></script>
+<script src="execute-sql-args.js"></script>
</head>
-<body onload="runTest()">
-<pre id="console"></pre>
+<body onload="setupAndRunTest()">
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/execute-sql-args.js b/LayoutTests/storage/execute-sql-args.js
new file mode 100644
index 0000000..3b9414d
--- /dev/null
+++ b/LayoutTests/storage/execute-sql-args.js
@@ -0,0 +1,86 @@
+var throwOnToStringObject = { };
+throwOnToStringObject.toString = function () { throw "Cannot call toString on this object." };
+
+var throwOnGetLengthObject = { };
+throwOnGetLengthObject.__defineGetter__("length", function () { throw "Cannot get length of this object."; });
+
+var throwOnGetZeroObject = { length: 1 };
+throwOnGetZeroObject.__defineGetter__("0", function () { throw "Cannot get 0 property of this object."; });
+
+var expectNoException = [
+ 'null',
+ 'undefined',
+ '0',
+ '""',
+ '"", null',
+ '"", undefined',
+ '"", []',
+ '"", [ "arg0" ]',
+ '"", { }',
+ '"", { length: 0 }',
+ '"", { length: 1, 0: "arg0" }',
+ '"", null, null',
+ '"", null, undefined',
+ '"", null, { }',
+ '"", null, null, null',
+ '"", null, null, undefined',
+ '"", null, null, { }',
+];
+
+var expectException = [
+ '',
+ 'throwOnToStringObject',
+ '"", throwOnGetLengthObject',
+ '"", throwOnGetZeroObject',
+ '"", [ throwOnToStringObject ]',
+ '"", 0',
+ '"", ""',
+ '"", null, 0',
+ '"", null, ""',
+ '"", null, null, 0',
+ '"", null, null, ""',
+];
+
+function tryExecuteSql(transaction, parameterList)
+{
+ try {
+ eval('transaction.executeSql(' + parameterList + ')');
+ return null;
+ } catch (exception) {
+ return exception;
+ }
+}
+
+function runTransactionTest(transaction, parameterList, expectException)
+{
+ var exception = tryExecuteSql(transaction, parameterList);
+ if (expectException) {
+ if (exception)
+ log("PASS. executeSql(" + parameterList + ") threw an exception as expected.");
+ else
+ log("*FAIL*. executeSql(" + parameterList + ") did not throw an exception");
+ } else {
+ if (exception)
+ log("*FAIL*. executeSql(" + parameterList + ") threw an exception: " + exception);
+ else
+ log("PASS. executeSql(" + parameterList + ") did not throw an exception");
+ }
+}
+
+function runTransactionTests(transaction)
+{
+ for (i in expectNoException)
+ runTransactionTest(transaction, expectNoException[i], false);
+ for (i in expectException)
+ runTransactionTest(transaction, expectException[i], true);
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+
+ var db = openDatabaseWithSuffix("ExecuteSQLArgsTest", "1.0", "Test of handling of the arguments to SQLTransaction.executeSql", 1);
+ db.transaction(runTransactionTests);
+}
diff --git a/LayoutTests/storage/executesql-accepts-only-one-statement-expected.txt b/LayoutTests/storage/executesql-accepts-only-one-statement-expected.txt
new file mode 100644
index 0000000..b95ceee
--- /dev/null
+++ b/LayoutTests/storage/executesql-accepts-only-one-statement-expected.txt
@@ -0,0 +1,3 @@
+This test tests that executeSql() fails when called with a string that has more than one valid statement in it.
+Test passed.
+
diff --git a/LayoutTests/storage/executesql-accepts-only-one-statement.html b/LayoutTests/storage/executesql-accepts-only-one-statement.html
new file mode 100644
index 0000000..a3860c8
--- /dev/null
+++ b/LayoutTests/storage/executesql-accepts-only-one-statement.html
@@ -0,0 +1,78 @@
+<html>
+<head>
+<script>
+
+var TOTAL_STATEMENTS = 8;
+var statements = 0;
+var db = null;
+
+function log(message)
+{
+ document.body.innerText += message + "\n";
+}
+
+function terminateTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function executeStatement(expectedToPass, statement)
+{
+ db.transaction(function(tx) {
+ tx.executeSql(statement, [],
+ function(tx, data) {
+ if (!expectedToPass) {
+ log("Statement " + statement + " was expected to fail, but passed.");
+ terminateTest();
+ }
+ if (++statements == TOTAL_STATEMENTS) {
+ log("Test passed.");
+ terminateTest();
+ }
+ }, function(tx, error) {
+ if (expectedToPass) {
+ log("Statement " + statement + " was expected to pass, but failed.");
+ terminateTest();
+ }
+ if (++statements == TOTAL_STATEMENTS) {
+ log("Test passed.");
+ terminateTest();
+ }
+ });
+ });
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ db = openDatabase("ExecuteSQLAcceptsOnlyOneStatementTest", "1.0", "", 1);
+ db.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo INT)");
+ }, function(error) {
+ log("Test failed: " + error.message);
+ terminateTest();
+ }, function() {
+ executeStatement(true, "INSERT INTO Test VALUES (1)");
+ executeStatement(true, "INSERT INTO Test VALUES (2);");
+ executeStatement(true, " INSERT INTO Test VALUES (3) ");
+ executeStatement(true, " INSERT INTO Test VALUES (4); ");
+ executeStatement(true, "INSERT INTO Test VALUES (5) ;");
+ executeStatement(false, "INSERT INTO Test VALUES (6); garbage");
+ executeStatement(false, "INSERT INTO Test VALUES (7); INSERT INTO Test VALUES (8)");
+ executeStatement(false, " INSERT INTO Test VALUES (9); INSERT INTO Test VALUES (10); ");
+ });
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test tests that executeSql() fails when called with a string that has more than one valid statement in it.<br>
+</body>
+</body>
+</html>
diff --git a/LayoutTests/storage/hash-change-with-xhr-expected.txt b/LayoutTests/storage/hash-change-with-xhr-expected.txt
index a481746..8e964e6 100644
--- a/LayoutTests/storage/hash-change-with-xhr-expected.txt
+++ b/LayoutTests/storage/hash-change-with-xhr-expected.txt
@@ -1,3 +1,4 @@
Changing the hash to create history entries.
Db is warmed up
Test Complete, SUCCESS
+
diff --git a/LayoutTests/storage/hash-change-with-xhr.html b/LayoutTests/storage/hash-change-with-xhr.html
index 9cbf276..584320f 100644
--- a/LayoutTests/storage/hash-change-with-xhr.html
+++ b/LayoutTests/storage/hash-change-with-xhr.html
@@ -1,140 +1,12 @@
<html>
<head>
<title>Hash Change with an Open XHR should not stop database transactions</title>
-
-<script type="text/javascript">
-
-var DB_UPDATE_INTERVAL = 100;
-var SEND_XHR_INTERVAL = 100;
-var BACK_INTERVAL = 100;
-var CREATE_HEALTH_TABLE = 'CREATE TABLE IF NOT EXISTS health (key VARCHAR(16) PRIMARY KEY);';
-var UPDATE_DATA = 'REPLACE INTO health VALUES("health-check-key");';
-
-var db = window.openDatabase('bug25710', '1.0', 'LayoutTest for bug 25710', 102400);
-var backIterations;
-var msgDiv;
-var xhrFctIntervalId;
-var backFctIntervalId;
-var successCheckIntervalId;
-var dbFctIntervalId;
-var successes;
-var databaseUpdates;
-var stoppedIntervals;
-
-function log(msg)
-{
- var newMsg = document.createElement('div');
- newMsg.innerText = msg;
- msgDiv.appendChild(newMsg);
-}
-
-function stopIntervals()
-{
- stoppedIntervals = true;
- window.clearInterval(dbFctIntervalId);
- window.clearInterval(xhrFctIntervalId);
- window.clearInterval(backFctIntervalId);
-}
-
-function stopTest(message)
-{
- if (!stoppedIntervals)
- stopIntervals();
-
- log(message);
-
- if (window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function updateDatabase()
-{
- databaseUpdates++;
- db.transaction(function(transaction) {
- transaction.executeSql(UPDATE_DATA, [], function() {}, errorHandler);
- }, errorHandler, function() {
- successes++;
- });
-}
-
-function checkForSuccess()
-{
- if (successes == databaseUpdates) {
- stopTest('Test Complete, SUCCESS');
- window.clearInterval(successCheckIntervalId);
- }
-}
-
-function errorHandler(tx, error)
-{
- log('DB error, code: ' + error.code + ', msg: ' + error.message);
- stopTest('Test Complete, FAILED');
-}
-
-function sendXhr()
-{
- xhr = new XMLHttpRequest();
- xhr.open('GET', location.href, true);
- xhr.send('');
-}
-
-function invokeBack()
-{
- backIterations--;
- if (backIterations) {
- history.back();
- } else {
- stopIntervals();
- // Allow a little time for all the database transactions to complete now we've stopped making them.
- successCheckIntervalId = window.setInterval(checkForSuccess, 250);
- // If we don't finish before this time, then we consider the test failed.
- window.setTimeout(function() { stopTest('Timed out waiting for transactions to complete. FAILED'); }, 20000);
-
- }
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- msgDiv = document.getElementById('msgs');
-
- msgDiv.innerHTML = '';
- backIterations = 10;
- consecutiveFailures = 0;
- successes = 0;
- databaseUpdates = 0;
- stoppedIntervals = false;
-
- // Create some hashes so we can call history.back().
- log('Changing the hash to create history entries.');
- for (var i = 0; i < backIterations; i++) {
- location.hash = i;
- }
-
- // Init the database.
- db.transaction(function(transaction) {
- transaction.executeSql(CREATE_HEALTH_TABLE, [], function() {}, errorHandler);
- }, errorHandler, function() {
- // Give a little for the database to 'warm up' before making xhr requests
- // and calling history.back().
- window.setTimeout(function() {
- log('Db is warmed up');
-
- // NOTE: If we don't make any xhr requests, then the test
- // successfully passes (comment this line out).
- xhrFctIntervalId = window.setInterval(sendXhr, SEND_XHR_INTERVAL);
- backFctIntervalId = window.setInterval(invokeBack, BACK_INTERVAL);
- dbFctIntervalId = window.setInterval(updateDatabase, DB_UPDATE_INTERVAL);
- }, 500);
- });
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="hash-change-with-xhr.js"></script>
</head>
-<body onload="runTest()">
-<div id="msgs"></div>
+<body onload="setupAndRunTest()">
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/hash-change-with-xhr.js b/LayoutTests/storage/hash-change-with-xhr.js
new file mode 100644
index 0000000..4290681
--- /dev/null
+++ b/LayoutTests/storage/hash-change-with-xhr.js
@@ -0,0 +1,112 @@
+var DB_UPDATE_INTERVAL = 100;
+var SEND_XHR_INTERVAL = 100;
+var BACK_INTERVAL = 100;
+var CREATE_HEALTH_TABLE = 'CREATE TABLE IF NOT EXISTS health (key VARCHAR(16) PRIMARY KEY);';
+var UPDATE_DATA = 'REPLACE INTO health VALUES("health-check-key");';
+
+var db = openDatabaseWithSuffix('bug25710', '1.0', 'LayoutTest for bug 25710', 102400);
+var backIterations;
+var xhrFctIntervalId;
+var backFctIntervalId;
+var successCheckIntervalId;
+var dbFctIntervalId;
+var successes;
+var databaseUpdates;
+var stoppedIntervals;
+
+function stopIntervals()
+{
+ stoppedIntervals = true;
+ clearInterval(dbFctIntervalId);
+ clearInterval(xhrFctIntervalId);
+ clearInterval(backFctIntervalId);
+}
+
+function stopTest(message)
+{
+ if (!stoppedIntervals)
+ stopIntervals();
+
+ log(message);
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function updateDatabase()
+{
+ databaseUpdates++;
+ db.transaction(function(transaction) {
+ transaction.executeSql(UPDATE_DATA, [], function() {}, errorHandler);
+ }, errorHandler, function() {
+ successes++;
+ });
+}
+
+function checkForSuccess()
+{
+ if (successes == databaseUpdates) {
+ stopTest('Test Complete, SUCCESS');
+ clearInterval(successCheckIntervalId);
+ }
+}
+
+function errorHandler(tx, error)
+{
+ log('DB error, code: ' + error.code + ', msg: ' + error.message);
+ stopTest('Test Complete, FAILED');
+}
+
+function sendXhr()
+{
+ xhr = new XMLHttpRequest();
+ xhr.open('GET', location.href, true);
+ xhr.send('');
+}
+
+function invokeBack()
+{
+ backIterations--;
+ if (backIterations) {
+ history.back();
+ } else {
+ stopIntervals();
+ // Allow a little time for all the database transactions to complete now we've stopped making them.
+ successCheckIntervalId = setInterval(checkForSuccess, 250);
+ // If we don't finish before this time, then we consider the test failed.
+ setTimeout(function() { stopTest('Timed out waiting for transactions to complete. FAILED'); }, 20000);
+
+ }
+}
+
+function runTest()
+{
+ backIterations = 10;
+ consecutiveFailures = 0;
+ successes = 0;
+ databaseUpdates = 0;
+ stoppedIntervals = false;
+
+ // Create some hashes so we can call history.back().
+ log('Changing the hash to create history entries.');
+ for (var i = 0; i < backIterations; i++) {
+ setLocationHash(i);
+ }
+
+ // Init the database.
+ db.transaction(function(transaction) {
+ transaction.executeSql(CREATE_HEALTH_TABLE, [], function() {}, errorHandler);
+ }, errorHandler, function() {
+ // Give a little for the database to 'warm up' before making xhr requests
+ // and calling history.back().
+ setTimeout(function() {
+ log('Db is warmed up');
+
+ // NOTE: If we don't make any xhr requests, then the test
+ // successfully passes (comment this line out).
+ xhrFctIntervalId = setInterval(sendXhr, SEND_XHR_INTERVAL);
+ backFctIntervalId = setInterval(invokeBack, BACK_INTERVAL);
+ dbFctIntervalId = setInterval(updateDatabase, DB_UPDATE_INTERVAL);
+ }, 500);
+ });
+}
diff --git a/LayoutTests/storage/indexeddb/basics-expected.txt b/LayoutTests/storage/indexeddb/basics-expected.txt
new file mode 100644
index 0000000..b7b8b94
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/basics-expected.txt
@@ -0,0 +1,34 @@
+Test IndexedDB's basics.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'webkitIndexedDB' in window is true
+PASS webkitIndexedDB == null is false
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+webkitIndexedDB.open('name');
+PASS Exception thrown.
+webkitIndexedDB.open('name', null);
+PASS Exception thrown.
+webkitIndexedDB.open('name', undefined);
+PASS Exception thrown.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/basics.html b/LayoutTests/storage/indexeddb/basics.html
new file mode 100644
index 0000000..2e382b2
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/basics.html
@@ -0,0 +1,64 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's basics.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ shouldBeTrue("'webkitIndexedDB' in window");
+ shouldBeFalse("webkitIndexedDB == null");
+
+ // FIXME: Verify other IndexedDatabaseRequest constructors, once they're implemented.
+
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openCallback;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function openCallback()
+{
+ verifySuccessEvent(event);
+ try {
+ debug("webkitIndexedDB.open('name');");
+ webkitIndexedDB.open('name');
+ testFailed("Calling IDBFactory::open without a description should have thrown.");
+ } catch (err) {
+ testPassed("Exception thrown.");
+ }
+ try {
+ debug("webkitIndexedDB.open('name', null);");
+ webkitIndexedDB.open('name', null);
+ testFailed("Calling IDBFactory::open with a null description should have thrown.");
+ } catch (err) {
+ testPassed("Exception thrown.");
+ }
+ try {
+ debug("webkitIndexedDB.open('name', undefined);");
+ webkitIndexedDB.open('name', undefined);
+ testFailed("Calling IDBFactory::open with an undefined description should have thrown.");
+ } catch (err) {
+ testPassed("Exception thrown.");
+ }
+ done();
+}
+
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/constants-expected.txt b/LayoutTests/storage/indexeddb/constants-expected.txt
new file mode 100644
index 0000000..8f53b56
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/constants-expected.txt
@@ -0,0 +1,35 @@
+Test IndexedDB's constants.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS webkitIDBKeyRange.SINGLE is 0
+PASS webkitIDBKeyRange.LEFT_OPEN is 1
+PASS webkitIDBKeyRange.RIGHT_OPEN is 2
+PASS webkitIDBKeyRange.LEFT_BOUND is 4
+PASS webkitIDBKeyRange.RIGHT_BOUND is 8
+PASS webkitIDBDatabaseException.UNKNOWN_ERR is 1
+PASS webkitIDBDatabaseException.NON_TRANSIENT_ERR is 1
+PASS webkitIDBDatabaseException.NOT_FOUND_ERR is 2
+PASS webkitIDBDatabaseException.CONSTRAINT_ERR is 3
+PASS webkitIDBDatabaseException.DATA_ERR is 4
+PASS webkitIDBDatabaseException.NOT_ALLOWED_ERR is 5
+PASS webkitIDBDatabaseException.SERIAL_ERR is 11
+PASS webkitIDBDatabaseException.RECOVERABLE_ERR is 21
+PASS webkitIDBDatabaseException.TRANSIENT_ERR is 31
+PASS webkitIDBDatabaseException.TIMEOUT_ERR is 32
+PASS webkitIDBDatabaseException.DEADLOCK_ERR is 33
+PASS webkitIDBRequest.LOADING is 1
+PASS webkitIDBRequest.DONE is 2
+PASS webkitIDBCursor.NEXT is 0
+PASS webkitIDBCursor.NEXT_NO_DUPLICATE is 1
+PASS webkitIDBCursor.PREV is 2
+PASS webkitIDBCursor.PREV_NO_DUPLICATE is 3
+PASS webkitIDBTransaction.READ_WRITE is 0
+PASS webkitIDBTransaction.READ_ONLY is 1
+PASS webkitIDBTransaction.SNAPSHOT_READ is 2
+PASS webkitIDBTransaction.VERSION_CHANGE is 3
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/constants.html b/LayoutTests/storage/indexeddb/constants.html
new file mode 100644
index 0000000..9cdf4c4
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/constants.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's constants.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ shouldBe("webkitIDBKeyRange.SINGLE", "0");
+ shouldBe("webkitIDBKeyRange.LEFT_OPEN", "1");
+ shouldBe("webkitIDBKeyRange.RIGHT_OPEN", "2");
+ shouldBe("webkitIDBKeyRange.LEFT_BOUND", "4");
+ shouldBe("webkitIDBKeyRange.RIGHT_BOUND", "8");
+
+ shouldBe("webkitIDBDatabaseException.UNKNOWN_ERR", "1");
+ shouldBe("webkitIDBDatabaseException.NON_TRANSIENT_ERR", "1");
+ shouldBe("webkitIDBDatabaseException.NOT_FOUND_ERR", "2");
+ shouldBe("webkitIDBDatabaseException.CONSTRAINT_ERR", "3");
+ shouldBe("webkitIDBDatabaseException.DATA_ERR", "4");
+ shouldBe("webkitIDBDatabaseException.NOT_ALLOWED_ERR", "5");
+ shouldBe("webkitIDBDatabaseException.SERIAL_ERR", "11");
+ shouldBe("webkitIDBDatabaseException.RECOVERABLE_ERR", "21");
+ shouldBe("webkitIDBDatabaseException.TRANSIENT_ERR", "31");
+ shouldBe("webkitIDBDatabaseException.TIMEOUT_ERR", "32");
+ shouldBe("webkitIDBDatabaseException.DEADLOCK_ERR", "33");
+
+ shouldBe("webkitIDBRequest.LOADING", "1");
+ shouldBe("webkitIDBRequest.DONE", "2");
+
+ shouldBe("webkitIDBCursor.NEXT", "0");
+ shouldBe("webkitIDBCursor.NEXT_NO_DUPLICATE", "1");
+ shouldBe("webkitIDBCursor.PREV", "2");
+ shouldBe("webkitIDBCursor.PREV_NO_DUPLICATE", "3");
+
+ shouldBe("webkitIDBTransaction.READ_WRITE", "0");
+ shouldBe("webkitIDBTransaction.READ_ONLY", "1");
+ shouldBe("webkitIDBTransaction.SNAPSHOT_READ", "2");
+ shouldBe("webkitIDBTransaction.VERSION_CHANGE", "3");
+}
+
+test();
+
+var successfullyParsed = true;
+done();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/database-basics-expected.txt b/LayoutTests/storage/indexeddb/database-basics-expected.txt
new file mode 100644
index 0000000..2a5fa2f
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/database-basics-expected.txt
@@ -0,0 +1,129 @@
+Test the basics of IndexedDB's IDBDatabase.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+Testing setVersion.
+db.setVersion("version a")
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db.setVersion("version b")
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS db.version is "version b"
+PASS db.name is "name"
+PASS db.objectStores is []
+PASS db.objectStores.length is 0
+PASS db.objectStores.contains('') is false
+PASS db.objectStores[0] is null
+PASS db.objectStores.item(0) is null
+db.createObjectStore("test123")
+PASS db.objectStores is ['test123']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('') is false
+PASS db.objectStores.contains('test456') is false
+PASS db.objectStores.contains('test123') is true
+db.setVersion("version c")
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS db.version is "version c"
+PASS db.name is "name"
+PASS db.objectStores is ['test123']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('') is false
+PASS db.objectStores.contains('test456') is false
+PASS db.objectStores.contains('test123') is true
+db.createObjectStore("test456")
+setVersionTrans = event.result
+PASS setVersionTrans !== null is true
+PASS db.version is "version b"
+PASS db.objectStores is ['test123']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('') is false
+PASS db.objectStores.contains('test456') is false
+PASS db.objectStores.contains('test123') is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/database-basics.html b/LayoutTests/storage/indexeddb/database-basics.html
new file mode 100644
index 0000000..31c887b
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/database-basics.html
@@ -0,0 +1,132 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test the basics of IndexedDB's IDBDatabase.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = setVersionSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, testSetVersion);
+}
+
+function testSetVersion()
+{
+ debug("Testing setVersion.");
+ result = evalAndLog('db.setVersion("version a")');
+ verifyResult(result);
+ result.onsuccess = setVersionAgain;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionAgain()
+{
+ verifySuccessEvent(event);
+
+ result = evalAndLog('db.setVersion("version b")');
+ verifyResult(result);
+ result.onsuccess = createObjectStore;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function createObjectStore()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("db.version", "version b");
+ shouldBeEqualToString("db.name", "name");
+ shouldBe("db.objectStores", "[]");
+ shouldBe("db.objectStores.length", "0");
+ shouldBe("db.objectStores.contains('')", "false");
+ shouldBeNull("db.objectStores[0]");
+ shouldBeNull("db.objectStores.item(0)");
+
+ objectStore = evalAndLog('db.createObjectStore("test123")');
+ checkObjectStore();
+ commitAndContinue();
+}
+
+function checkObjectStore()
+{
+ shouldBe("db.objectStores", "['test123']");
+ shouldBe("db.objectStores.length", "1");
+ shouldBe("db.objectStores.contains('')", "false");
+ shouldBe("db.objectStores.contains('test456')", "false");
+ shouldBe("db.objectStores.contains('test123')", "true");
+}
+
+function commitAndContinue()
+{
+ window.setTimeout(testSetVersionAbort, 0);
+}
+
+function testSetVersionAbort()
+{
+ result = evalAndLog('db.setVersion("version c")');
+ verifyResult(result);
+ result.onsuccess = createAnotherObjectStore;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function createAnotherObjectStore()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("db.version", "version c");
+ shouldBeEqualToString("db.name", "name");
+ checkObjectStore();
+
+ objectStore = evalAndLog('db.createObjectStore("test456")');
+ var setVersionTrans = evalAndLog("setVersionTrans = event.result");
+ shouldBeTrue("setVersionTrans !== null");
+ setVersionTrans.oncomplete = unexpectedCompleteCallback;
+ setVersionTrans.onabort = checkMetadata;
+ setVersionTrans.abort();
+}
+
+function checkMetadata()
+{
+ shouldBeEqualToString("db.version", "version b");
+ checkObjectStore();
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/database-quota-expected.txt b/LayoutTests/storage/indexeddb/database-quota-expected.txt
new file mode 100644
index 0000000..91d3ac7
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/database-quota-expected.txt
@@ -0,0 +1,87 @@
+Tests IndexedDB's quota enforcing mechanism.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS db.version is "new version"
+PASS db.name is "name"
+PASS db.objectStores is []
+PASS db.objectStores.length is 0
+PASS db.objectStores.contains('') is false
+db.createObjectStore("test123")
+PASS db.objectStores is ['test123']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('') is false
+PASS db.objectStores.contains('test456') is false
+PASS db.objectStores.contains('test123') is true
+trans = db.transaction()
+Creating 'data' which contains 64K of data
+PASS data.length is 65536
+store = trans.objectStore('test123')
+Error function called: (1) Error writing data to stable storage.
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS Adding data failed due to quota error. Data added was about 5 MB
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/database-quota.html b/LayoutTests/storage/indexeddb/database-quota.html
new file mode 100644
index 0000000..aefe6b1
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/database-quota.html
@@ -0,0 +1,124 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Tests IndexedDB's quota enforcing mechanism.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = setVersionSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createNewObjectStore);
+}
+
+function createNewObjectStore()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("db.version", "new version");
+ shouldBeEqualToString("db.name", "name");
+ shouldBe("db.objectStores", "[]");
+ shouldBe("db.objectStores.length", "0");
+ shouldBe("db.objectStores.contains('')", "false");
+
+ objectStore = evalAndLog('db.createObjectStore("test123")');
+ checkObjectStore();
+ commitAndContinue();
+}
+
+function checkObjectStore()
+{
+ shouldBe("db.objectStores", "['test123']");
+ shouldBe("db.objectStores.length", "1");
+ shouldBe("db.objectStores.contains('')", "false");
+ shouldBe("db.objectStores.contains('test456')", "false");
+ shouldBe("db.objectStores.contains('test123')", "true");
+}
+
+function commitAndContinue()
+{
+ window.setTimeout(checkQuotaEnforcing, 0);
+}
+
+function checkQuotaEnforcing()
+{
+ var trans = evalAndLog("trans = db.transaction()");
+ trans.onabort = testComplete;
+ trans.oncomplete = unexpectedCompleteCallback;
+ debug("Creating 'data' which contains 64K of data");
+ window.data = "X";
+ for (var i = 0; i < 16; i++)
+ data += data;
+ shouldBe("data.length", "65536");
+ window.dataAdded = 0;
+ window.store = evalAndLog("store = trans.objectStore('test123')");
+ addData();
+}
+
+function addData()
+{
+ if (dataAdded < 5 * 1024 * 1024) {
+ if (dataAdded > 0)
+ store = event.source;
+ } else {
+ testFailed("added more than quota");
+ done();
+ return;
+ }
+ dataAdded += 65536;
+ result = store.add({x: data}, dataAdded);
+ result.onsuccess = addData;
+ result.onerror = logError;
+}
+
+function logError()
+{
+ debug("Error function called: (" + event.code + ") " + event.message);
+ verifyErrorEvent(event);
+}
+
+function testComplete()
+{
+ testPassed("Adding data failed due to quota error. Data added was about " + Math.round(dataAdded / 1024 / 1024) + " MB");
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/duplicates-expected.txt b/LayoutTests/storage/indexeddb/duplicates-expected.txt
new file mode 100644
index 0000000..2ea8c36
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/duplicates-expected.txt
@@ -0,0 +1,541 @@
+Verify that you can put the same data in 2 different databases without uniqueness constraints firing.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('storeName', null)
+store.createIndex('indexName', 'x')
+store.add({x: 'value', y: 'zzz'}, 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+event.source.add({x: 'value2', y: 'zzz2'}, 'key2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+indexObject.getKey('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "key"
+indexObject.get('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result.x is "value"
+PASS event.result.y is "zzz"
+indexObject.getKey('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.get('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.openKeyCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value is "key"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value is "key2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+indexObject.openCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value.x is "value"
+PASS event.result.value.y is "zzz"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value.x is "value2"
+PASS event.result.value.y is "zzz2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+webkitIndexedDB.open('name2', 'description2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('storeName', null)
+store.createIndex('indexName', 'x')
+store.add({x: 'value', y: 'zzz'}, 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+event.source.add({x: 'value2', y: 'zzz2'}, 'key2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+indexObject.getKey('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "key"
+indexObject.get('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result.x is "value"
+PASS event.result.y is "zzz"
+indexObject.getKey('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.get('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.openKeyCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value is "key"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value is "key2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+indexObject.openCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value.x is "value"
+PASS event.result.value.y is "zzz"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value.x is "value2"
+PASS event.result.value.y is "zzz2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/duplicates.html b/LayoutTests/storage/indexeddb/duplicates.html
new file mode 100644
index 0000000..70cf8c4
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/duplicates.html
@@ -0,0 +1,213 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Verify that you can put the same data in 2 different databases without uniqueness constraints firing.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+testCount = 0;
+function test()
+{
+ if (testCount++ == 0)
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ else
+ result = evalAndLog("webkitIndexedDB.open('name2', 'description2')");
+ verifyResult(result);
+ result.onsuccess = setVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+ verifySuccessEvent(event);
+ db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createIndex);
+}
+
+function createIndex()
+{
+ window.store = evalAndLog("db.createObjectStore('storeName', null)");
+ window.indexObject = evalAndLog("store.createIndex('indexName', 'x')");
+ addData();
+}
+
+function addData()
+{
+ result = evalAndLog("store.add({x: 'value', y: 'zzz'}, 'key')");
+ verifyResult(result);
+ result.onsuccess = addMore;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function addMore()
+{
+ verifySuccessEvent(event);
+
+ result = evalAndLog("event.source.add({x: 'value2', y: 'zzz2'}, 'key2')");
+ verifyResult(result);
+ result.onsuccess = getData;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getData()
+{
+ verifySuccessEvent(event);
+
+ result = evalAndLog("indexObject.getKey('value')");
+ verifyResult(result);
+ result.onsuccess = getObjectData;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getObjectData()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result", "key");
+
+ result = evalAndLog("indexObject.get('value')");
+ verifyResult(result);
+ result.onsuccess = getDataFail;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getDataFail()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result.x", "value");
+ shouldBeEqualToString("event.result.y", "zzz");
+
+ result = evalAndLog("indexObject.getKey('does not exist')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = getObjectDataFail;
+}
+
+function getObjectDataFail()
+{
+ verifyErrorEvent(event);
+ shouldBe("event.code", "webkitIDBDatabaseException.NOT_FOUND_ERR");
+
+ result = evalAndLog("indexObject.get('does not exist')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = openKeyCursor;
+}
+
+function openKeyCursor()
+{
+ verifyErrorEvent(event);
+ shouldBe("event.code", "webkitIDBDatabaseException.NOT_FOUND_ERR");
+
+ window.result = evalAndLog("indexObject.openKeyCursor()");
+ verifyResult(result);
+ result.onsuccess = cursor1Continue;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cursor1Continue()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value");
+ shouldBeEqualToString("event.result.value", "key");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = cursor1Continue2;
+}
+
+function cursor1Continue2()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value2");
+ shouldBeEqualToString("event.result.value", "key2");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = openObjectCursor;
+}
+
+function openObjectCursor()
+{
+ verifySuccessEvent(event);
+ shouldBeTrue("event.result === null");
+
+ window.result = evalAndLog("indexObject.openCursor()");
+ verifyResult(result);
+ result.onsuccess = cursor2Continue;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cursor2Continue()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value");
+ shouldBeEqualToString("event.result.value.x", "value");
+ shouldBeEqualToString("event.result.value.y", "zzz");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = cursor2Continue2;
+}
+
+function cursor2Continue2()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value2");
+ shouldBeEqualToString("event.result.value.x", "value2");
+ shouldBeEqualToString("event.result.value.y", "zzz2");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = last;
+}
+
+function last()
+{
+ verifySuccessEvent(event);
+ shouldBeTrue("event.result === null");
+
+ if (testCount == 1)
+ test();
+ else
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/index-basics-expected.txt b/LayoutTests/storage/indexeddb/index-basics-expected.txt
new file mode 100644
index 0000000..f886a19
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/index-basics-expected.txt
@@ -0,0 +1,310 @@
+Test the basics of IndexedDB's webkitIDBIndex.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('storeName', null)
+store.createIndex('indexName', 'x')
+store.createIndex('indexName2', 'y', false)
+PASS 'name' in indexObject is true
+PASS indexObject.name is "indexName"
+PASS 'storeName' in indexObject is true
+PASS indexObject.storeName is "storeName"
+PASS 'keyPath' in indexObject is true
+PASS indexObject.keyPath is "x"
+PASS 'unique' in indexObject is true
+PASS indexObject.unique is false
+PASS 'openKeyCursor' in indexObject is true
+PASS 'openCursor' in indexObject is true
+PASS 'getKey' in indexObject is true
+PASS 'get' in indexObject is true
+store.add({x: 'value', y: 'zzz'}, 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+event.source.add({x: 'value2', y: 'zzz2'}, 'key2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+indexObject.getKey('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "key"
+indexObject2.getKey('zzz')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "key"
+indexObject.get('value')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result.x is "value"
+PASS event.result.y is "zzz"
+indexObject.getKey('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.get('does not exist')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.NOT_FOUND_ERR
+indexObject.openKeyCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value is "key"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value is "key2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+indexObject.openCursor()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value"
+PASS event.result.value.x is "value"
+PASS event.result.value.y is "zzz"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is false
+PASS event.result.key is "value2"
+PASS event.result.value.x is "value2"
+PASS event.result.value.y is "zzz2"
+event.result.continue()
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result === null is true
+Passing an invalid key into indexObject.get().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into indexObject.getKey().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/index-basics.html b/LayoutTests/storage/indexeddb/index-basics.html
new file mode 100644
index 0000000..7a16c3a
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/index-basics.html
@@ -0,0 +1,246 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test the basics of IndexedDB's webkitIDBIndex.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = setVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+ verifySuccessEvent(event);
+ db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createIndex);
+}
+
+function createIndex()
+{
+ window.store = evalAndLog("db.createObjectStore('storeName', null)");
+ window.indexObject = evalAndLog("store.createIndex('indexName', 'x')");
+ window.indexObject2 = evalAndLog("store.createIndex('indexName2', 'y', false)");
+ addData();
+}
+
+function addData()
+{
+ shouldBeTrue("'name' in indexObject");
+ shouldBeEqualToString("indexObject.name", "indexName");
+ shouldBeTrue("'storeName' in indexObject");
+ shouldBeEqualToString("indexObject.storeName", "storeName");
+ shouldBeTrue("'keyPath' in indexObject");
+ shouldBeEqualToString("indexObject.keyPath", "x");
+ shouldBeTrue("'unique' in indexObject");
+ shouldBeFalse("indexObject.unique");
+ shouldBeTrue("'openKeyCursor' in indexObject");
+ shouldBeTrue("'openCursor' in indexObject");
+ shouldBeTrue("'getKey' in indexObject");
+ shouldBeTrue("'get' in indexObject");
+
+ result = evalAndLog("store.add({x: 'value', y: 'zzz'}, 'key')");
+ verifyResult(result);
+ result.onsuccess = addMore;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function addMore()
+{
+ verifySuccessEvent(event);
+
+ result = evalAndLog("event.source.add({x: 'value2', y: 'zzz2'}, 'key2')");
+ verifyResult(result);
+ result.onsuccess = getData;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getData()
+{
+ verifySuccessEvent(event);
+
+ result = evalAndLog("indexObject.getKey('value')");
+ verifyResult(result);
+ result.onsuccess = getObjectData;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getObjectData()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result", "key");
+
+ result = evalAndLog("indexObject2.getKey('zzz')");
+ verifyResult(result);
+ result.onsuccess = getObjectData2;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getObjectData2()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result", "key");
+
+ result = evalAndLog("indexObject.get('value')");
+ verifyResult(result);
+ result.onsuccess = getDataFail;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getDataFail()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result.x", "value");
+ shouldBeEqualToString("event.result.y", "zzz");
+
+ result = evalAndLog("indexObject.getKey('does not exist')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = getObjectDataFail;
+}
+
+function getObjectDataFail()
+{
+ verifyErrorEvent(event);
+ shouldBe("event.code", "webkitIDBDatabaseException.NOT_FOUND_ERR");
+
+ result = evalAndLog("indexObject.get('does not exist')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = openKeyCursor;
+}
+
+function openKeyCursor()
+{
+ verifyErrorEvent(event);
+ shouldBe("event.code", "webkitIDBDatabaseException.NOT_FOUND_ERR");
+
+ window.result = evalAndLog("indexObject.openKeyCursor()");
+ verifyResult(result);
+ result.onsuccess = cursor1Continue;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cursor1Continue()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value");
+ shouldBeEqualToString("event.result.value", "key");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = cursor1Continue2;
+}
+
+function cursor1Continue2()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value2");
+ shouldBeEqualToString("event.result.value", "key2");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = openObjectCursor;
+}
+
+function openObjectCursor()
+{
+ verifySuccessEvent(event);
+ shouldBeTrue("event.result === null");
+
+ window.result = evalAndLog("indexObject.openCursor()");
+ verifyResult(result);
+ result.onsuccess = cursor2Continue;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cursor2Continue()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value");
+ shouldBeEqualToString("event.result.value.x", "value");
+ shouldBeEqualToString("event.result.value.y", "zzz");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = cursor2Continue2;
+}
+
+function cursor2Continue2()
+{
+ verifySuccessEvent(event);
+ shouldBeFalse("event.result === null");
+ shouldBeEqualToString("event.result.key", "value2");
+ shouldBeEqualToString("event.result.value.x", "value2");
+ shouldBeEqualToString("event.result.value.y", "zzz2");
+
+ // We re-use the last result object.
+ evalAndLog("event.result.continue()");
+ verifyResult(window.result);
+ window.result.onsuccess = last;
+}
+
+function last()
+{
+ verifySuccessEvent(event);
+ shouldBeTrue("event.result === null");
+
+ try {
+ debug("Passing an invalid key into indexObject.get().");
+ indexObject.get([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into indexObject.getKey().");
+ indexObject.getKey([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/index-cursor-expected.txt b/LayoutTests/storage/indexeddb/index-cursor-expected.txt
new file mode 100644
index 0000000..bc01070
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/index-cursor-expected.txt
@@ -0,0 +1,2102 @@
+Test IndexedDB's webkitIDBIndex.openCursor + the cursor it produces in depth.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('someDB', 'some description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('someObjectStore')
+objectStore.createIndex('someIndex', 'x')
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add({'x': testData[nextToAdd]}, nextToAdd)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Scheduling tests...
+Running tests...
+
+Next test: lower open bound is 0; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 0; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 0; sorted ascending.
+PASS event.result is null
+
+Next test: upper bound is 0; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower open bound is 0; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 0; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper open bound is 0; sorted descending.
+PASS event.result is null
+
+Next test: upper bound is 0; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 1; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 1; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 1; sorted ascending.
+PASS event.result is null
+
+Next test: upper bound is 1; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower open bound is 1; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 1; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper open bound is 1; sorted descending.
+PASS event.result is null
+
+Next test: upper bound is 1; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 2; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 2; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 2; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: upper bound is 2; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower open bound is 2; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 2; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: upper open bound is 2; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 2; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 3; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 3; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 3; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: upper bound is 3; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower open bound is 3; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 3; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: upper open bound is 3; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 3; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 4; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 4; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 4; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: upper bound is 4; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 4; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: upper open bound is 4; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 4; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 5; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 5; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 5; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: upper bound is 5; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 5; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: upper open bound is 5; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 5; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: upper open bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 7; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 7; upper bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper bound is 6; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 7; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 7; upper bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper bound is 6; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 7; upper open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 7; upper bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper open bound is 7; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper bound is 7; sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: lower open bound is 7; upper open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 7; upper bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper open bound is 7; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 7; upper bound is 7; sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: null key path sorted ascending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result is null
+
+Next test: null key path sorted descending.
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[7]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[6]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[5]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[4]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[3]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[2]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[1]
+PASS event.result.value is expectedIndex
+PASS event.result.key is testData[0]
+PASS event.result is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/index-cursor.html b/LayoutTests/storage/indexeddb/index-cursor.html
new file mode 100644
index 0000000..6a9d1ac
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/index-cursor.html
@@ -0,0 +1,266 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's webkitIDBIndex.openCursor + the cursor it produces in depth.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+// In order of how it should be sorted by IndexedDB.
+window.testData = [
+ null,
+ null,
+ 2,
+ 2,
+ 10,
+ // FIXME: Dates.
+ "A big string",
+ "the BIGEST string",
+ "the BIGEST string"
+];
+
+function openDatabase()
+{
+ result = evalAndLog("webkitIndexedDB.open('someDB', 'some description')");
+ verifyResult(result);
+ result.onsuccess = setVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, startAddingData);
+}
+
+function startAddingData()
+{
+ window.objectStore = evalAndLog("db.createObjectStore('someObjectStore')");
+ window.indexObject = evalAndLog("objectStore.createIndex('someIndex', 'x')");
+ window.nextToAdd = 0;
+ addData();
+}
+
+function addData()
+{
+ if (window.nextToAdd > 0)
+ verifySuccessEvent(event);
+
+ result = evalAndLog("objectStore.add({'x': testData[nextToAdd]}, nextToAdd)");
+ verifyResult(result);
+ result.onsuccess = ++window.nextToAdd < testData.length ? addData : scheduleTests;
+}
+
+function scheduleTests()
+{
+ debug("Scheduling tests...");
+ window.scheduledTests = [];
+ for (var i = 0; i < testData.length; ++i) {
+ /* left bound, is open, right bound, is open, ascending */
+ scheduledTests.unshift([i, true, null, null, true]);
+ scheduledTests.unshift([i, false, null, null, true]);
+ scheduledTests.unshift([null, null, i, true, true]);
+ scheduledTests.unshift([null, null, i, false, true]);
+ scheduledTests.unshift([i, true, null, null, false]);
+ scheduledTests.unshift([i, false, null, null, false]);
+ scheduledTests.unshift([null, null, i, true, false]);
+ scheduledTests.unshift([null, null, i, false, false]);
+ for (var j = 6; j < testData.length; ++j) {
+ scheduledTests.unshift([i, true, j, true, true]);
+ scheduledTests.unshift([i, true, j, false, true]);
+ scheduledTests.unshift([i, false, j, true, true]);
+ scheduledTests.unshift([i, false, j, false, true]);
+ scheduledTests.unshift([i, true, j, true, false]);
+ scheduledTests.unshift([i, true, j, false, false]);
+ scheduledTests.unshift([i, false, j, true, false]);
+ scheduledTests.unshift([i, false, j, false, false]);
+ }
+ }
+
+ debug("Running tests...");
+ runNextTest();
+}
+
+function runNextTest()
+{
+ if (!scheduledTests.length) {
+ testNullKeyRange();
+ return;
+ }
+
+ var test = scheduledTests.pop();
+ window.lower = test[0];
+ window.lowerIsOpen = test[1];
+ window.upper = test[2];
+ window.upperIsOpen = test[3];
+ window.ascending = test[4];
+
+ str = "Next test: ";
+ if (lower !== null) {
+ str += "lower ";
+ if (lowerIsOpen)
+ str += "open ";
+ str += "bound is " + lower + "; ";
+ }
+ if (upper !== null) {
+ str += "upper ";
+ if (upperIsOpen)
+ str += "open ";
+ str += "bound is " + upper + "; ";
+ }
+ if (ascending)
+ str += "sorted ascending.";
+ else
+ str += "sorted descending.";
+
+ debug("");
+ debug(str);
+
+ // Work around for duplicate values.
+ if (upper !== null) {
+ if (upperIsOpen) {
+ while (upper > 0 && testData[upper] === testData[upper - 1])
+ --window.upper;
+ } else {
+ while (upper < testData.length - 1 && testData[upper] === testData[upper + 1])
+ ++window.upper;
+ }
+ }
+ if (lower !== null) {
+ if (lowerIsOpen) {
+ while (lower < testData.length - 1 && testData[lower] === testData[lower + 1])
+ ++window.lower;
+ } else {
+ while (lower > 0 && testData[lower] === testData[lower - 1])
+ --window.lower;
+ }
+ }
+
+ if (ascending) {
+ if (lower !== null) {
+ if (!lowerIsOpen)
+ window.expectedIndex = lower;
+ else
+ window.expectedIndex = lower + 1;
+ } else
+ window.expectedIndex = 0;
+ } else {
+ if (upper !== null) {
+ if (!upperIsOpen)
+ window.expectedIndex = upper;
+ else
+ window.expectedIndex = upper - 1;
+ } else
+ window.expectedIndex = testData.length - 1;
+ }
+ testWithinBounds();
+
+ var keyRange;
+ if (lower !== null && upper !== null)
+ keyRange = webkitIDBKeyRange.bound(testData[lower], testData[upper], lowerIsOpen, upperIsOpen);
+ else if (lower !== null)
+ keyRange = webkitIDBKeyRange.leftBound(testData[lower], lowerIsOpen);
+ else
+ keyRange = webkitIDBKeyRange.rightBound(testData[upper], upperIsOpen);
+
+ var request = indexObject.openKeyCursor(keyRange, ascending ? webkitIDBCursor.NEXT : webkitIDBCursor.PREV);
+ request.onsuccess = cursorIteration;
+ request.onerror = unexpectedErrorCallback;
+}
+
+function testWithinBounds()
+{
+ if (expectedIndex < 0 || testData.length <= expectedIndex)
+ window.expectedIndex = null;
+ if (lower !== null && expectedIndex < lower)
+ window.expectedIndex = null;
+ if (upper !== null && upper < expectedIndex)
+ window.expectedIndex = null;
+ if (lower !== null && lowerIsOpen && expectedIndex <= lower)
+ window.expectedIndex = null;
+ if (upper !== null && upperIsOpen && upper <= expectedIndex)
+ window.expectedIndex = null;
+}
+
+function cursorIteration()
+{
+ if (expectedIndex === null) {
+ shouldBeNull("event.result");
+ runNextTest();
+ return;
+ }
+ if (event.result === null) {
+ testFailed("Event.result should not be null.");
+ runNextTest();
+ return;
+ }
+
+ shouldBe("event.result.value", "expectedIndex");
+ shouldBe("event.result.key", "testData[" + expectedIndex + "]");
+ window.expectedIndex = ascending ? expectedIndex + 1 : expectedIndex - 1;
+ testWithinBounds();
+
+ event.result.continue();
+}
+
+window.nullKeyRangeStep = 0;
+function testNullKeyRange()
+{
+ window.lower = 0;
+ window.lowerIsOpen = false;
+ window.upper = testData.length-1;
+ window.upperIsOpen = false;
+
+ str = "Next test: null key path ";
+ if (window.nullKeyRangeStep == 0) {
+ str += "sorted ascending.";
+ window.ascending = true;
+ window.expectedIndex = lower;
+ window.nullKeyRangeStep = 1;
+ } else if (window.nullKeyRangeStep == 1) {
+ str += "sorted descending.";
+ window.ascending = false;
+ window.expectedIndex = upper;
+ window.nullKeyRangeStep = 2;
+ } else {
+ done();
+ return;
+ }
+
+ debug("");
+ debug(str);
+
+ var request = indexObject.openKeyCursor(null, ascending ? webkitIDBCursor.NEXT : webkitIDBCursor.PREV);
+ request.onsuccess = cursorIteration;
+ request.onerror = unexpectedErrorCallback;
+}
+
+openDatabase(); // The first step.
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/keyrange-expected.txt b/LayoutTests/storage/indexeddb/keyrange-expected.txt
new file mode 100644
index 0000000..1a7dfff
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/keyrange-expected.txt
@@ -0,0 +1,159 @@
+Test IndexedDB's KeyRange.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'SINGLE' in webkitIDBKeyRange is true
+PASS 'LEFT_OPEN' in webkitIDBKeyRange is true
+PASS 'RIGHT_OPEN' in webkitIDBKeyRange is true
+PASS 'LEFT_BOUND' in webkitIDBKeyRange is true
+PASS 'RIGHT_BOUND' in webkitIDBKeyRange is true
+PASS 'left' in webkitIDBKeyRange is false
+PASS 'right' in webkitIDBKeyRange is false
+PASS 'flags' in webkitIDBKeyRange is false
+PASS 'only' in webkitIDBKeyRange is true
+PASS 'leftBound' in webkitIDBKeyRange is true
+PASS 'rightBound' in webkitIDBKeyRange is true
+PASS 'bound' in webkitIDBKeyRange is true
+
+instance = webkitIDBKeyRange.only(1)
+PASS 'SINGLE' in instance is true
+PASS 'LEFT_OPEN' in instance is true
+PASS 'RIGHT_OPEN' in instance is true
+PASS 'LEFT_BOUND' in instance is true
+PASS 'RIGHT_BOUND' in instance is true
+PASS 'left' in instance is true
+PASS 'right' in instance is true
+PASS 'flags' in instance is true
+PASS 'only' in instance is false
+PASS 'leftBound' in instance is false
+PASS 'rightBound' in instance is false
+PASS 'bound' in instance is false
+
+webkitIDBKeyRange.only(1)
+PASS keyRange.left is 1
+PASS keyRange.right is 1
+PASS keyRange.flags is keyRange.SINGLE
+webkitIDBKeyRange.only('a')
+PASS keyRange.left is 'a'
+PASS keyRange.right is 'a'
+PASS keyRange.flags is keyRange.SINGLE
+webkitIDBKeyRange.leftBound(10,true)
+PASS keyRange.left is 10
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+webkitIDBKeyRange.leftBound(11,false)
+PASS keyRange.left is 11
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_BOUND
+webkitIDBKeyRange.leftBound(12,undefined)
+PASS keyRange.left is 12
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_BOUND
+webkitIDBKeyRange.leftBound('aa',true)
+PASS keyRange.left is 'aa'
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+webkitIDBKeyRange.leftBound('ab',false)
+PASS keyRange.left is 'ab'
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_BOUND
+webkitIDBKeyRange.leftBound('ac',undefined)
+PASS keyRange.left is 'ac'
+PASS keyRange.right is null
+PASS keyRange.flags is keyRange.LEFT_BOUND
+webkitIDBKeyRange.rightBound(20,true)
+PASS keyRange.right is 20
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+webkitIDBKeyRange.rightBound(21,false)
+PASS keyRange.right is 21
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.rightBound(22,undefined)
+PASS keyRange.right is 22
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.rightBound('ba',true)
+PASS keyRange.right is 'ba'
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+webkitIDBKeyRange.rightBound('bb',false)
+PASS keyRange.right is 'bb'
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.rightBound('bc',undefined)
+PASS keyRange.right is 'bc'
+PASS keyRange.left is null
+PASS keyRange.flags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound(30,40,undefined,undefined)
+PASS keyRange.left is 30
+PASS keyRange.right is 40
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound(31,41,false,false)
+PASS keyRange.left is 31
+PASS keyRange.right is 41
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound(32,42,false,true)
+PASS keyRange.left is 32
+PASS keyRange.right is 42
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound(33,43,true,false)
+PASS keyRange.left is 33
+PASS keyRange.right is 43
+PASS leftFlags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound(34,44,true,true)
+PASS keyRange.left is 34
+PASS keyRange.right is 44
+PASS leftFlags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aaa','aba',false,false)
+PASS keyRange.left is 'aaa'
+PASS keyRange.right is 'aba'
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aab','abb',undefined,undefined)
+PASS keyRange.left is 'aab'
+PASS keyRange.right is 'abb'
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aac','abc',false,false)
+PASS keyRange.left is 'aac'
+PASS keyRange.right is 'abc'
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aad','abd',false,true)
+PASS keyRange.left is 'aad'
+PASS keyRange.right is 'abd'
+PASS leftFlags is keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aae','abe',true,false)
+PASS keyRange.left is 'aae'
+PASS keyRange.right is 'abe'
+PASS leftFlags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_BOUND
+webkitIDBKeyRange.bound('aaf','abf',true,true)
+PASS keyRange.left is 'aaf'
+PASS keyRange.right is 'abf'
+PASS leftFlags is keyRange.LEFT_OPEN | keyRange.LEFT_BOUND
+PASS rightFlags is keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND
+Passing an invalid key into only([])
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into rightBound([])
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into leftBound([])
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into bound(null, [])
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into bound([],null)
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into bound([], [])
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/keyrange.html b/LayoutTests/storage/indexeddb/keyrange.html
new file mode 100644
index 0000000..48708d9
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/keyrange.html
@@ -0,0 +1,171 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's KeyRange.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function checkSingleKeyRange(value)
+{
+ keyRange = evalAndLog("webkitIDBKeyRange.only(" + value + ")");
+ shouldBe("keyRange.left", "" + value);
+ shouldBe("keyRange.right", "" + value);
+ shouldBe("keyRange.flags", "keyRange.SINGLE");
+}
+
+function checkLeftBoundKeyRange(value, open)
+{
+ keyRange = evalAndLog("webkitIDBKeyRange.leftBound(" + value + "," + open + ")");
+ shouldBe("keyRange.left", "" + value);
+ shouldBeNull("keyRange.right");
+ shouldBe("keyRange.flags", open ? "keyRange.LEFT_OPEN | keyRange.LEFT_BOUND" : "keyRange.LEFT_BOUND");
+}
+
+function checkRightBoundKeyRange(value, open)
+{
+ keyRange = evalAndLog("webkitIDBKeyRange.rightBound(" + value + "," + open + ")");
+ shouldBe("keyRange.right", "" + value);
+ shouldBeNull("keyRange.left");
+ shouldBe("keyRange.flags", open ? "keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND" : "keyRange.RIGHT_BOUND");
+}
+
+function checkBoundKeyRange(left, right, openLeft, openRight)
+{
+ keyRange = evalAndLog("webkitIDBKeyRange.bound(" + left + "," + right + "," + openLeft + "," + openRight + ")");
+ shouldBe("keyRange.left", "" + left);
+ shouldBe("keyRange.right", "" + right);
+ leftFlags = keyRange.flags & (keyRange.LEFT_OPEN | keyRange.LEFT_BOUND);
+ shouldBe("leftFlags", openLeft ? "keyRange.LEFT_OPEN | keyRange.LEFT_BOUND" : "keyRange.LEFT_BOUND");
+ rightFlags = keyRange.flags & (keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND);
+ shouldBe("rightFlags", openRight ? "keyRange.RIGHT_OPEN | keyRange.RIGHT_BOUND" : "keyRange.RIGHT_BOUND");
+}
+
+function test()
+{
+ shouldBeTrue("'SINGLE' in webkitIDBKeyRange");
+ shouldBeTrue("'LEFT_OPEN' in webkitIDBKeyRange");
+ shouldBeTrue("'RIGHT_OPEN' in webkitIDBKeyRange");
+ shouldBeTrue("'LEFT_BOUND' in webkitIDBKeyRange");
+ shouldBeTrue("'RIGHT_BOUND' in webkitIDBKeyRange");
+ shouldBeFalse("'left' in webkitIDBKeyRange");
+ shouldBeFalse("'right' in webkitIDBKeyRange");
+ shouldBeFalse("'flags' in webkitIDBKeyRange");
+ shouldBeTrue("'only' in webkitIDBKeyRange");
+ shouldBeTrue("'leftBound' in webkitIDBKeyRange");
+ shouldBeTrue("'rightBound' in webkitIDBKeyRange");
+ shouldBeTrue("'bound' in webkitIDBKeyRange");
+
+ debug("");
+
+ var instance = evalAndLog("instance = webkitIDBKeyRange.only(1)");
+ shouldBeTrue("'SINGLE' in instance");
+ shouldBeTrue("'LEFT_OPEN' in instance");
+ shouldBeTrue("'RIGHT_OPEN' in instance");
+ shouldBeTrue("'LEFT_BOUND' in instance");
+ shouldBeTrue("'RIGHT_BOUND' in instance");
+ shouldBeTrue("'left' in instance");
+ shouldBeTrue("'right' in instance");
+ shouldBeTrue("'flags' in instance");
+ shouldBeFalse("'only' in instance");
+ shouldBeFalse("'leftBound' in instance");
+ shouldBeFalse("'rightBound' in instance");
+ shouldBeFalse("'bound' in instance");
+
+ debug("");
+
+ checkSingleKeyRange(1);
+ checkSingleKeyRange("'a'");
+
+ checkLeftBoundKeyRange(10, true);
+ checkLeftBoundKeyRange(11, false);
+ checkLeftBoundKeyRange(12);
+ checkLeftBoundKeyRange("'aa'", true);
+ checkLeftBoundKeyRange("'ab'", false);
+ checkLeftBoundKeyRange("'ac'");
+
+ checkRightBoundKeyRange(20, true);
+ checkRightBoundKeyRange(21, false);
+ checkRightBoundKeyRange(22);
+ checkRightBoundKeyRange("'ba'", true);
+ checkRightBoundKeyRange("'bb'", false);
+ checkRightBoundKeyRange("'bc'");
+
+ checkBoundKeyRange(30, 40);
+ checkBoundKeyRange(31, 41, false, false);
+ checkBoundKeyRange(32, 42, false, true);
+ checkBoundKeyRange(33, 43, true, false);
+ checkBoundKeyRange(34, 44, true, true);
+
+ checkBoundKeyRange("'aaa'", "'aba'", false, false);
+ checkBoundKeyRange("'aab'", "'abb'");
+ checkBoundKeyRange("'aac'", "'abc'", false, false);
+ checkBoundKeyRange("'aad'", "'abd'", false, true);
+ checkBoundKeyRange("'aae'", "'abe'", true, false);
+ checkBoundKeyRange("'aaf'", "'abf'", true, true);
+
+ try {
+ debug("Passing an invalid key into only([])");
+ webkitIDBKeyRange.only([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into rightBound([])");
+ webkitIDBKeyRange.rightBound([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into leftBound([])");
+ webkitIDBKeyRange.leftBound([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into bound(null, [])");
+ webkitIDBKeyRange.bound(null, []);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into bound([],null)");
+ webkitIDBKeyRange.bound([], null);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into bound([], [])");
+ webkitIDBKeyRange.bound([], []);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+}
+
+test();
+
+var successfullyParsed = true;
+done();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt b/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt
new file mode 100644
index 0000000..e0c9770
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-basics-expected.txt
@@ -0,0 +1,191 @@
+Test the basics of IndexedDB's IDBObjectStore.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+openSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+createObjectStore():
+store = db.createObjectStore('storeName', null)
+storeNames = db.objectStores
+PASS store.name is "storeName"
+PASS store.keyPath is null
+PASS storeNames.contains('storeName') is true
+PASS storeNames.length is 1
+Ask for an index that doesn't exist:
+index = store.index('asdf')
+PASS Exception thrown.
+createIndex():
+index = store.createIndex('indexName', 'x', true)
+PASS index !== null is true
+PASS store.indexNames.contains('indexName') is true
+index = store.index('indexName')
+PASS index !== null is true
+Ask for an index that doesn't exist:
+index = store.index('asdf')
+PASS Exception thrown.
+db.setVersion("version fail")
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS db.version is "version fail"
+setVersionTrans = event.result
+PASS setVersionTrans !== null is true
+store = setVersionTrans.objectStore('storeName')
+index = store.createIndex('indexFail', 'x')
+PASS db.version is "new version"
+PASS store.indexNames is ['indexName']
+PASS store.indexNames.length is 1
+PASS store.indexNames.contains('') is false
+PASS store.indexNames.contains('indexFail') is false
+PASS store.indexNames.contains('indexName') is true
+PASS store.indexNames[0] is "indexName"
+PASS store.indexNames[1] is null
+PASS store.indexNames[100] is null
+PASS store.indexNames.item(1) is null
+PASS store.indexNames.item(100) is null
+transaction = db.transaction()
+store = transaction.objectStore('storeName')
+store.add({x: 'value'}, 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+addSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "key"
+event.source.add({x: 'value'}, 'zzz')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+addAgainFailure():
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is webkitIDBDatabaseException.UNKNOWN_ERR
+db.transaction()
+store = transaction.objectStore('storeName')
+store.get('key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+getSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result.x is "value"
+store = event.source
+store.remove('key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+removeSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is null
+store = event.source
+Passing an invalid key into store.get().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into store.remove().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into store.add().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+Passing an invalid key into store.put().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/objectstore-basics.html b/LayoutTests/storage/indexeddb/objectstore-basics.html
new file mode 100644
index 0000000..005247e
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-basics.html
@@ -0,0 +1,241 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test the basics of IndexedDB's IDBObjectStore.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+ debug("openSuccess():");
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = setVersionSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createObjectStore);
+}
+
+function createObjectStore()
+{
+ debug("createObjectStore():");
+ window.store = evalAndLog("store = db.createObjectStore('storeName', null)");
+ var storeNames = evalAndLog("storeNames = db.objectStores");
+
+ shouldBeEqualToString("store.name", "storeName");
+ shouldBeNull("store.keyPath");
+ shouldBe("storeNames.contains('storeName')", "true");
+ shouldBe("storeNames.length", "1");
+ // FIXME: test all of object store's methods.
+
+ debug("Ask for an index that doesn't exist:");
+ try {
+ debug("index = store.index('asdf')");
+ index = store.index('asdf');
+ testFailed("Asking for a store that doesn't exist should have thrown.");
+ } catch (err) {
+ testPassed("Exception thrown.");
+ // FIXME: Verify the correct exception thrown.
+ }
+
+ createIndex();
+}
+
+function createIndex()
+{
+ debug("createIndex():");
+ var index = evalAndLog("index = store.createIndex('indexName', 'x', true)"); // true == unique requirement.
+ shouldBeTrue("index !== null");
+ shouldBeTrue("store.indexNames.contains('indexName')");
+ index = evalAndLog("index = store.index('indexName')");
+ shouldBeTrue("index !== null");
+
+ debug("Ask for an index that doesn't exist:");
+ try {
+ debug("index = store.index('asdf')");
+ index = store.index('asdf');
+ testFailed("Asking for a store that doesn't exist should have thrown.");
+ } catch (err) {
+ testPassed("Exception thrown.");
+ // FIXME: Verify the correct exception thrown.
+ }
+
+ commitAndContinue();
+}
+
+function commitAndContinue()
+{
+ window.setTimeout(testSetVersionAbort, 0);
+}
+
+function testSetVersionAbort()
+{
+ result = evalAndLog('db.setVersion("version fail")');
+ verifyResult(result);
+ result.onsuccess = createAnotherIndex;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function createAnotherIndex()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("db.version", "version fail");
+
+ var setVersionTrans = evalAndLog("setVersionTrans = event.result");
+ shouldBeTrue("setVersionTrans !== null");
+ setVersionTrans.oncomplete = unexpectedCompleteCallback;
+ setVersionTrans.onabort = checkMetadata;
+ window.store = evalAndLog("store = setVersionTrans.objectStore('storeName')");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x')");
+
+ setVersionTrans.abort();
+}
+
+function checkMetadata()
+{
+ shouldBeEqualToString("db.version", "new version");
+ shouldBe("store.indexNames", "['indexName']");
+ shouldBe("store.indexNames.length", "1");
+ shouldBe("store.indexNames.contains('')", "false");
+ shouldBe("store.indexNames.contains('indexFail')", "false");
+ shouldBe("store.indexNames.contains('indexName')", "true");
+ shouldBeEqualToString("store.indexNames[0]", "indexName");
+ shouldBeNull("store.indexNames[1]");
+ shouldBeNull("store.indexNames[100]");
+ shouldBeNull("store.indexNames.item(1)");
+ shouldBeNull("store.indexNames.item(100)");
+ addData();
+}
+
+
+function addData()
+{
+ var transaction = evalAndLog("transaction = db.transaction()");
+ transaction.onabort = unexpectedAbortCallback;
+ window.store = evalAndLog("store = transaction.objectStore('storeName')");
+ result = evalAndLog("store.add({x: 'value'}, 'key')");
+ verifyResult(result);
+ result.onsuccess = addSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function addSuccess()
+{
+ debug("addSuccess():");
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result", "key");
+
+ result = evalAndLog("event.source.add({x: 'value'}, 'zzz')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = addAgainFailure;
+}
+
+function addAgainFailure()
+{
+ debug("addAgainFailure():");
+ verifyErrorEvent(event);
+ // FIXME: This error code needs to be specced.
+ shouldBe("event.code", "webkitIDBDatabaseException.UNKNOWN_ERR");
+
+ transaction = evalAndLog("db.transaction()");
+ transaction.onabort = unexpectedErrorCallback;
+ var store = evalAndLog("store = transaction.objectStore('storeName')");
+
+ result = evalAndLog("store.get('key')");
+ verifyResult(result);
+ result.onsuccess = getSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getSuccess()
+{
+ debug("getSuccess():");
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result.x", "value");
+ var store = evalAndLog("store = event.source");
+
+ result = evalAndLog("store.remove('key')");
+ verifyResult(result);
+ result.onsuccess = removeSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function removeSuccess()
+{
+ debug("removeSuccess():");
+ verifySuccessEvent(event);
+ shouldBeNull("event.result");
+ var store = evalAndLog("store = event.source");
+
+ try {
+ debug("Passing an invalid key into store.get().");
+ store.get([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into store.remove().");
+ store.remove([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into store.add().");
+ store.add(null, []);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ try {
+ debug("Passing an invalid key into store.put().");
+ store.put(null, []);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/objectstore-cursor-expected.txt b/LayoutTests/storage/indexeddb/objectstore-cursor-expected.txt
new file mode 100644
index 0000000..1493a80
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-cursor-expected.txt
@@ -0,0 +1,880 @@
+Test IndexedDB's objectStore.openCursor + the cursor it produces in depth.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('someDB', 'some description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('someObjectStore')
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+objectStore.add('', testData[nextToAdd])
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Scheduling tests...
+Running tests...
+
+Next test: lower open bound is 0; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 0; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 0; sorted ascending.
+PASS event.result is null
+
+Next test: upper bound is 0; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 0; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower bound is 0; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper open bound is 0; sorted descending.
+PASS event.result is null
+
+Next test: upper bound is 0; sorted descending.
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower open bound is 0; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower bound is 0; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower bound is 0; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 1; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 1; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 1; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 1; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower open bound is 1; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 1; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: upper open bound is 1; sorted descending.
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 1; sorted descending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 1; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 1; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower bound is 1; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: lower open bound is 2; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 2; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 2; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result is null
+
+Next test: upper bound is 2; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 2; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower bound is 2; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: upper open bound is 2; sorted descending.
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 2; sorted descending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower open bound is 2; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower bound is 2; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower bound is 2; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: lower open bound is 3; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 3; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 3; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result is null
+
+Next test: upper bound is 3; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower open bound is 3; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 3; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: upper open bound is 3; sorted descending.
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 3; sorted descending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 3; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 3; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower bound is 3; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: lower open bound is 4; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 4; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 4; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result is null
+
+Next test: upper bound is 4; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 4; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: upper open bound is 4; sorted descending.
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 4; sorted descending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 4; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 4; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower bound is 4; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: lower open bound is 5; sorted ascending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 5; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result is null
+
+Next test: upper bound is 5; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 5; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: upper open bound is 5; sorted descending.
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 5; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 5; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 5; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower bound is 5; upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower bound is 5; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: lower open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; sorted ascending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 6; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result is null
+
+Next test: upper bound is 6; sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: upper open bound is 6; sorted descending.
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 6; sorted ascending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 6; sorted ascending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: lower open bound is 6; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower open bound is 6; upper bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper open bound is 6; sorted descending.
+PASS event.result is null
+
+Next test: lower bound is 6; upper bound is 6; sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: null key path sorted ascending.
+PASS event.result.key is testData[0]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[6]
+PASS event.result is null
+
+Next test: null key path sorted descending.
+PASS event.result.key is testData[6]
+PASS event.result.key is testData[5]
+PASS event.result.key is testData[4]
+PASS event.result.key is testData[3]
+PASS event.result.key is testData[2]
+PASS event.result.key is testData[1]
+PASS event.result.key is testData[0]
+PASS event.result is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/objectstore-cursor.html b/LayoutTests/storage/indexeddb/objectstore-cursor.html
new file mode 100644
index 0000000..d0d786b
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-cursor.html
@@ -0,0 +1,243 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's objectStore.openCursor + the cursor it produces in depth.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+// In order of how it should be sorted by IndexedDB.
+window.testData = [
+ null,
+ 2,
+ 3,
+ 10,
+ // FIXME: Dates.
+ "A bigger string",
+ "The biggest",
+ "a low string"
+];
+
+function openDatabase()
+{
+ result = evalAndLog("webkitIndexedDB.open('someDB', 'some description')");
+ verifyResult(result);
+ result.onsuccess = setVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, startAddingData);
+}
+
+function startAddingData()
+{
+ window.objectStore = evalAndLog("db.createObjectStore('someObjectStore')");
+ window.nextToAdd = 0;
+ addData();
+}
+
+function addData()
+{
+ if (window.nextToAdd > 0)
+ verifySuccessEvent(event);
+
+ result = evalAndLog("objectStore.add('', testData[nextToAdd])");
+ verifyResult(result);
+ result.onsuccess = ++window.nextToAdd < testData.length ? addData : scheduleTests;
+}
+
+function scheduleTests()
+{
+ debug("Scheduling tests...");
+ window.scheduledTests = [];
+ for (var i = 0; i < testData.length; ++i) {
+ /* left bound, is open, right bound, is open, ascending */
+ scheduledTests.unshift([i, true, null, null, true]);
+ scheduledTests.unshift([i, false, null, null, true]);
+ scheduledTests.unshift([null, null, i, true, true]);
+ scheduledTests.unshift([null, null, i, false, true]);
+ scheduledTests.unshift([i, true, null, null, false]);
+ scheduledTests.unshift([i, false, null, null, false]);
+ scheduledTests.unshift([null, null, i, true, false]);
+ scheduledTests.unshift([null, null, i, false, false]);
+ for (var j = 6; j < testData.length; ++j) {
+ scheduledTests.unshift([i, true, j, true, true]);
+ scheduledTests.unshift([i, true, j, false, true]);
+ scheduledTests.unshift([i, false, j, true, true]);
+ scheduledTests.unshift([i, false, j, false, true]);
+ scheduledTests.unshift([i, true, j, true, false]);
+ scheduledTests.unshift([i, true, j, false, false]);
+ scheduledTests.unshift([i, false, j, true, false]);
+ scheduledTests.unshift([i, false, j, false, false]);
+ }
+ }
+
+ debug("Running tests...");
+ runNextTest();
+}
+
+function runNextTest()
+{
+ if (!scheduledTests.length) {
+ testNullKeyRange();
+ return;
+ }
+
+ var test = scheduledTests.pop();
+ window.lower = test[0];
+ window.lowerIsOpen = test[1];
+ window.upper = test[2];
+ window.upperIsOpen = test[3];
+ window.ascending = test[4];
+
+ str = "Next test: ";
+ if (lower !== null) {
+ str += "lower ";
+ if (lowerIsOpen)
+ str += "open ";
+ str += "bound is " + lower + "; ";
+ }
+ if (upper !== null) {
+ str += "upper ";
+ if (upperIsOpen)
+ str += "open ";
+ str += "bound is " + upper + "; ";
+ }
+ if (ascending)
+ str += "sorted ascending.";
+ else
+ str += "sorted descending.";
+
+ debug("");
+ debug(str);
+
+ if (ascending) {
+ if (lower !== null) {
+ if (!lowerIsOpen)
+ window.expectedIndex = lower;
+ else
+ window.expectedIndex = lower+1;
+ } else
+ window.expectedIndex = 0;
+ } else {
+ if (upper !== null) {
+ if (!upperIsOpen)
+ window.expectedIndex = upper;
+ else
+ window.expectedIndex = upper-1;
+ } else
+ window.expectedIndex = testData.length-1;
+ }
+ testWithinBounds();
+
+ var keyRange;
+ if (lower !== null && upper !== null)
+ keyRange = webkitIDBKeyRange.bound(testData[lower], testData[upper], lowerIsOpen, upperIsOpen);
+ else if (lower !== null)
+ keyRange = webkitIDBKeyRange.leftBound(testData[lower], lowerIsOpen);
+ else
+ keyRange = webkitIDBKeyRange.rightBound(testData[upper], upperIsOpen);
+
+ var request = objectStore.openCursor(keyRange, ascending ? webkitIDBCursor.NEXT : webkitIDBCursor.PREV);
+ request.onsuccess = cursorIteration;
+ request.onerror = unexpectedErrorCallback;
+}
+
+function testWithinBounds()
+{
+ if (expectedIndex < 0 || testData.length <= expectedIndex)
+ window.expectedIndex = null;
+ if (lower !== null && expectedIndex < lower)
+ window.expectedIndex = null;
+ if (upper !== null && upper < expectedIndex)
+ window.expectedIndex = null;
+ if (lower !== null && lowerIsOpen && expectedIndex <= lower)
+ window.expectedIndex = null;
+ if (upper !== null && upperIsOpen && upper <= expectedIndex)
+ window.expectedIndex = null;
+}
+
+function cursorIteration()
+{
+ if (expectedIndex === null) {
+ shouldBeNull("event.result");
+ runNextTest();
+ return;
+ }
+ if (event.result === null) {
+ testFailed("Event.result should not be null.");
+ runNextTest();
+ return;
+ }
+
+ shouldBe("event.result.key", "testData[" + expectedIndex + "]");
+ window.expectedIndex = ascending ? expectedIndex+1 : expectedIndex-1;
+ testWithinBounds();
+
+ event.result.continue();
+}
+
+window.nullKeyRangeStep = 0;
+function testNullKeyRange()
+{
+ window.lower = 0;
+ window.lowerIsOpen = false;
+ window.upper = testData.length-1;
+ window.upperIsOpen = false;
+
+ str = "Next test: null key path ";
+ if (window.nullKeyRangeStep == 0) {
+ str += "sorted ascending.";
+ window.ascending = true;
+ window.expectedIndex = lower;
+ window.nullKeyRangeStep = 1;
+ } else if (window.nullKeyRangeStep == 1) {
+ str += "sorted descending.";
+ window.ascending = false;
+ window.expectedIndex = upper;
+ window.nullKeyRangeStep = 2;
+ } else {
+ done();
+ return;
+ }
+
+ debug("");
+ debug(str);
+
+ var request = objectStore.openCursor(null, ascending ? webkitIDBCursor.NEXT : webkitIDBCursor.PREV);
+ request.onsuccess = cursorIteration;
+ request.onerror = unexpectedErrorCallback;
+}
+
+openDatabase(); // The first step.
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/objectstore-removeobjectstore-expected.txt b/LayoutTests/storage/indexeddb/objectstore-removeobjectstore-expected.txt
new file mode 100644
index 0000000..b6b20a2
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-removeobjectstore-expected.txt
@@ -0,0 +1,129 @@
+Test IndexedDB's webkitIDBObjectStore.removeObjectStore().
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+store = db.createObjectStore('storeName', null)
+store.createIndex('indexName', '')
+PASS store.indexNames.contains('indexName') is true
+store.add('value', 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db.transaction()
+store = transaction.objectStore('storeName')
+store.get('key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result is "value"
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+db.removeObjectStore('storeName')
+db.createObjectStore('storeName', null)
+db.transaction()
+store = transaction.objectStore('storeName')
+store.get('key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Error event fired:
+PASS 'result' in event is false
+PASS 'code' in event is true
+PASS 'message' in event is true
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.code is 2
+PASS event.source.indexNames.contains('indexName') is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/objectstore-removeobjectstore.html b/LayoutTests/storage/indexeddb/objectstore-removeobjectstore.html
new file mode 100644
index 0000000..c4418bc
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/objectstore-removeobjectstore.html
@@ -0,0 +1,126 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's webkitIDBObjectStore.removeObjectStore().");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = startSetVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function startSetVersion()
+{
+ verifySuccessEvent(event);
+ db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+
+ deleteAllObjectStores(db, createObjectStoreAndAddValue);
+}
+
+function createObjectStoreAndAddValue()
+{
+ store = evalAndLog("store = db.createObjectStore('storeName', null)");
+
+ window.index = evalAndLog("store.createIndex('indexName', '')");
+ shouldBeTrue("store.indexNames.contains('indexName')");
+
+ result = evalAndLog("store.add('value', 'key')");
+ verifyResult(result);
+ result.onsuccess = getValue;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function getValue()
+{
+ verifySuccessEvent(event);
+
+ transaction = evalAndLog("db.transaction()");
+ transaction.onabort = unexpectedErrorCallback;
+ var store = evalAndLog("store = transaction.objectStore('storeName')");
+
+ result = evalAndLog("store.get('key')");
+ verifyResult(result);
+ result.onsuccess = addIndex;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function addIndex()
+{
+ verifySuccessEvent(event);
+ shouldBeEqualToString("event.result", "value");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = removeObjectStore;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function removeObjectStore()
+{
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ evalAndLog("db.removeObjectStore('storeName')");
+ createObjectStoreAgain();
+}
+
+function createObjectStoreAgain()
+{
+ evalAndLog("db.createObjectStore('storeName', null)");
+ getValueAgain();
+}
+
+function getValueAgain()
+{
+ transaction = evalAndLog("db.transaction()");
+ transaction.onabort = unexpectedErrorCallback;
+ var store = evalAndLog("store = transaction.objectStore('storeName')");
+
+ result = evalAndLog("store.get('key')");
+ verifyResult(result);
+ result.onsuccess = unexpectedSuccessCallback;
+ result.onerror = verifyError;
+}
+
+function verifyError()
+{
+ verifyErrorEvent(event);
+ shouldBe("event.code", "2");
+ shouldBeFalse("event.source.indexNames.contains('indexName')");
+
+ done();
+}
+
+test();
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/open-cursor-expected.txt b/LayoutTests/storage/indexeddb/open-cursor-expected.txt
new file mode 100644
index 0000000..c3c1a8d
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/open-cursor-expected.txt
@@ -0,0 +1,100 @@
+Test IndexedDB's openCursor.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+objectStore = db.createObjectStore('test')
+objectStore.add('myValue', 'myKey')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Opening cursor
+event.source.openCursor(keyRange)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Cursor opened successfully.
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS event.result.direction is 0
+PASS event.result.key is 'myKey'
+PASS event.result.value is 'myValue'
+
+Passing an invalid key into .continue().
+PASS Caught exception: Error: TYPE_MISMATCH_ERR: DOM Exception 17
+
+Opening an empty cursor.
+objectStore.openCursor(keyRange)
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Empty cursor opened successfully.
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/open-cursor.html b/LayoutTests/storage/indexeddb/open-cursor.html
new file mode 100644
index 0000000..815c01b
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/open-cursor.html
@@ -0,0 +1,110 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB's openCursor.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function emptyCursorSuccess()
+{
+ debug("Empty cursor opened successfully.")
+ verifySuccessEvent(event);
+ // FIXME: check that we can iterate the cursor.
+ done();
+}
+
+function openEmptyCursor()
+{
+ debug("Opening an empty cursor.");
+ keyRange = webkitIDBKeyRange.leftBound("InexistentKey");
+ result = evalAndLog("objectStore.openCursor(keyRange)");
+ verifyResult(result);
+ result.onsuccess = emptyCursorSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cursorSuccess()
+{
+ debug("Cursor opened successfully.")
+ verifySuccessEvent(event);
+ // FIXME: check that we can iterate the cursor.
+ shouldBe("event.result.direction", "0");
+ shouldBe("event.result.key", "'myKey'");
+ shouldBe("event.result.value", "'myValue'");
+ debug("");
+ try {
+ debug("Passing an invalid key into .continue().");
+ event.result.continue([]);
+ testFailed("No exception thrown");
+ } catch (e) {
+ testPassed("Caught exception: " + e.toString());
+ }
+ debug("");
+ openEmptyCursor();
+}
+
+function openCursor()
+{
+ debug("Opening cursor");
+ keyRange = webkitIDBKeyRange.leftBound("myKey");
+ result = evalAndLog("event.source.openCursor(keyRange)");
+ verifyResult(result);
+ result.onsuccess = cursorSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function createAndPopulateObjectStore()
+{
+ var objectStore = evalAndLog("objectStore = db.createObjectStore('test')");
+ result = evalAndLog("objectStore.add('myValue', 'myKey')");
+ verifyResult(result);
+ result.onsuccess = openCursor;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createAndPopulateObjectStore);
+}
+
+function openSuccess()
+{
+ verifySuccessEvent(event);
+ var db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = setVersionSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/queued-commands-expected.txt b/LayoutTests/storage/indexeddb/queued-commands-expected.txt
new file mode 100644
index 0000000..e2e20af
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/queued-commands-expected.txt
@@ -0,0 +1,104 @@
+Verify that queuing up several commands works (and they all fire).
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('storeName', null)
+store.createIndex('indexName', 'x')
+store.add({x: 'value', y: 'zzz'}, 'key')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+store.add({x: 'value2', y: 'zzz2'}, 'key2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+store.put({x: 'valu2', y: 'zz2'}, 'ky2')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS 0 is 0
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS 1 is 1
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+PASS 2 is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/queued-commands.html b/LayoutTests/storage/indexeddb/queued-commands.html
new file mode 100644
index 0000000..7dc94c3
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/queued-commands.html
@@ -0,0 +1,87 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Verify that queuing up several commands works (and they all fire).");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = setVersion;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+ verifySuccessEvent(event);
+ db = evalAndLog("db = event.result");
+
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = deleteExisting;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ deleteAllObjectStores(db, createIndex);
+}
+
+function createIndex()
+{
+ window.store = evalAndLog("db.createObjectStore('storeName', null)");
+ window.indexObject = evalAndLog("store.createIndex('indexName', 'x')");
+
+ result = evalAndLog("store.add({x: 'value', y: 'zzz'}, 'key')");
+ verifyResult(result);
+ result.onsuccess = function() { verifyAdd(0); };
+ result.onerror = unexpectedErrorCallback;
+
+ result = evalAndLog("store.add({x: 'value2', y: 'zzz2'}, 'key2')");
+ verifyResult(result);
+ result.onsuccess = function() { verifyAdd(1); };
+ result.onerror = unexpectedErrorCallback;
+
+ result = evalAndLog("store.put({x: 'valu2', y: 'zz2'}, 'ky2')");
+ verifyResult(result);
+ result.onsuccess = function() { verifyAdd(2); };
+ result.onerror = unexpectedErrorCallback;
+
+ window.addCount = 0;
+}
+
+function verifyAdd(expected)
+{
+ verifySuccessEvent(event);
+ shouldBe("" + addCount++, "" + expected);
+
+ if (addCount == 3)
+ done();
+ if (addCount > 3)
+ testFailed("Unexpected call to verifyAdd!");
+}
+
+test();
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/resources/shared.js b/LayoutTests/storage/indexeddb/resources/shared.js
new file mode 100644
index 0000000..01ec606
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/resources/shared.js
@@ -0,0 +1,98 @@
+function done()
+{
+ isSuccessfullyParsed();
+ if (window.layoutTestController)
+ layoutTestController.notifyDone()
+}
+
+function verifyEventCommon(event)
+{
+ shouldBeTrue("'source' in event");
+ shouldBeTrue("event.source != null");
+ shouldBeTrue("'onsuccess' in event.target");
+ shouldBeTrue("'onerror' in event.target");
+ shouldBeTrue("'readyState' in event.target");
+ shouldBe("event.target.readyState", "event.target.DONE");
+ debug("");
+}
+
+function verifyErrorEvent(event)
+{
+ debug("Error event fired:");
+ shouldBeFalse("'result' in event");
+ shouldBeTrue("'code' in event");
+ shouldBeTrue("'message' in event");
+ verifyEventCommon(event);
+}
+
+function verifySuccessEvent(event)
+{
+ debug("Success event fired:");
+ shouldBeTrue("'result' in event");
+ shouldBeFalse("'code' in event");
+ shouldBeFalse("'message' in event");
+ verifyEventCommon(event);
+}
+
+function verifyAbortEvent(event)
+{
+ debug("Abort event fired:");
+ shouldBeEqualToString("event.type", "abort");
+}
+
+function verifyCompleteEvent(event)
+{
+ debug("Complete event fired:");
+ shouldBeEqualToString("event.type", "complete");
+}
+
+function verifyResult(result)
+{
+ shouldBeTrue("'onsuccess' in result");
+ shouldBeTrue("'onerror' in result");
+ shouldBeTrue("'readyState' in result");
+ debug("An event should fire shortly...");
+ debug("");
+}
+
+function unexpectedSuccessCallback()
+{
+ testFailed("Success function called unexpectedly.");
+ debug("");
+ verifySuccessEvent(event);
+ done();
+}
+
+function unexpectedErrorCallback()
+{
+ testFailed("Error function called unexpectedly: (" + event.code + ") " + event.message);
+ debug("");
+ verifyErrorEvent(event);
+ done();
+}
+
+function unexpectedAbortCallback()
+{
+ testFailed("Abort function called unexpectedly!");
+ debug("");
+ verifyAbortEvent(event);
+ done();
+}
+
+function unexpectedCompleteCallback()
+{
+ testFailed("oncomplete function called unexpectedly!");
+ debug("");
+ verifyCompleteEvent(event);
+ done();
+}
+
+// FIXME: remove the onfinished parameter.
+function deleteAllObjectStores(db, onfinished)
+{
+ while (db.objectStores.length)
+ db.removeObjectStore(db.objectStores.item(0));
+
+ debug("Deleted all object stores.");
+ onfinished();
+}
diff --git a/LayoutTests/storage/indexeddb/transaction-basics-expected.txt b/LayoutTests/storage/indexeddb/transaction-basics-expected.txt
new file mode 100644
index 0000000..b493946
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/transaction-basics-expected.txt
@@ -0,0 +1,186 @@
+Test IndexedDB transaction basics.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'webkitIndexedDB' in window is true
+PASS webkitIndexedDB == null is false
+webkitIndexedDB.open('name', 'description')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+openSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+db = event.result
+result = db.setVersion('version 1')
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+Deleted all object stores.
+PASS window.db.objectStores is []
+PASS window.db.objectStores.length is 0
+PASS window.db.objectStores.contains('storeFail') is false
+PASS window.db.objectStores is []
+PASS window.db.objectStores.length is 0
+PASS window.db.objectStores.contains('storeFail') is false
+result = startSetVersion('version fail')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+addRemoveIDBObjects():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+store = db.createObjectStore('storeFail', null)
+index = store.createIndex('indexFail', 'x', false)
+db.removeObjectStore('storeFail')
+store.removeIndex('indexFail')
+PASS window.db.objectStores is []
+PASS window.db.objectStores.length is 0
+PASS window.db.objectStores.contains('storeFail') is false
+result = startSetVersion('version fail')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+addRemoveAddIDBObjects():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+store = db.createObjectStore('storeFail', null)
+index = store.createIndex('indexFail', 'x', false)
+db.removeObjectStore('storeFail')
+store.removeIndex('indexFail')
+store = db.createObjectStore('storeFail', null)
+index = store.createIndex('indexFail', 'x', false)
+PASS window.db.objectStores is []
+PASS window.db.objectStores.length is 0
+PASS window.db.objectStores.contains('storeFail') is false
+result = startSetVersion('version fail')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+addIDBObjects():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+store = db.createObjectStore('storeFail', null)
+index = store.createIndex('indexFail', 'x', false)
+PASS db.objectStores is ['storeFail']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('storeFail') is true
+result = startSetVersion('version fail')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+removeIDBObjects():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+store = trans.objectStore('storeFail')
+store.removeIndex('indexFail')
+db.removeObjectStore('storeFail')
+PASS db.objectStores is ['storeFail']
+PASS db.objectStores.length is 1
+PASS db.objectStores.contains('storeFail') is true
+db.setVersion('new version')
+PASS 'onsuccess' in result is true
+PASS 'onerror' in result is true
+PASS 'readyState' in result is true
+An event should fire shortly...
+
+setVersionSuccess():
+Success event fired:
+PASS 'result' in event is true
+PASS 'code' in event is false
+PASS 'message' in event is false
+PASS 'source' in event is true
+PASS event.source != null is true
+PASS 'onsuccess' in event.target is true
+PASS 'onerror' in event.target is true
+PASS 'readyState' in event.target is true
+PASS event.target.readyState is event.target.DONE
+
+trans = event.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('storeName', null)
+db.transaction()
+store = transaction.objectStore('storeName')
+PASS store.name is "storeName"
+Abort event fired:
+PASS event.type is "abort"
+Complete event fired:
+PASS event.type is "complete"
+PASS oncomplete event had fired
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/transaction-basics.html b/LayoutTests/storage/indexeddb/transaction-basics.html
new file mode 100644
index 0000000..07fc685
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/transaction-basics.html
@@ -0,0 +1,254 @@
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../../fast/js/resources/js-test-post-function.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test IndexedDB transaction basics.");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ shouldBeTrue("'webkitIndexedDB' in window");
+ shouldBeFalse("webkitIndexedDB == null");
+
+ result = evalAndLog("webkitIndexedDB.open('name', 'description')");
+ verifyResult(result);
+ result.onsuccess = openSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+ debug("openSuccess():");
+ verifySuccessEvent(event);
+ window.db = evalAndLog("db = event.result");
+ result = evalAndLog("result = db.setVersion('version 1')");
+ result.onsuccess = cleanDatabase;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function cleanDatabase()
+{
+ verifySuccessEvent(event);
+ deleteAllObjectStores(db, checkMetadataEmpty);
+ commitAndContinue(testSetVersionAbort1);
+}
+
+function testSetVersionAbort1()
+{
+ checkMetadataEmpty();
+ result = evalAndLog("result = startSetVersion('version fail')");
+ result.onsuccess = addRemoveIDBObjects;
+}
+
+function addRemoveIDBObjects()
+{
+ debug("addRemoveIDBObjects():");
+ verifySuccessEvent(event);
+ var trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = testSetVersionAbort2;
+ trans.oncomplete = unexpectedCompleteCallback;
+
+ var store = evalAndLog("store = db.createObjectStore('storeFail', null)");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x', false)");
+
+ evalAndLog("db.removeObjectStore('storeFail')");
+ evalAndLog("store.removeIndex('indexFail')");
+
+ trans.abort();
+}
+
+function testSetVersionAbort2()
+{
+ checkMetadataEmpty();
+ result = evalAndLog("result = startSetVersion('version fail')");
+ result.onsuccess = addRemoveAddIDBObjects;
+}
+
+function addRemoveAddIDBObjects()
+{
+ debug("addRemoveAddIDBObjects():");
+ verifySuccessEvent(event);
+ var trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = testSetVersionAbort3;
+ trans.oncomplete = unexpectedCompleteCallback;
+
+ var store = evalAndLog("store = db.createObjectStore('storeFail', null)");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x', false)");
+
+ evalAndLog("db.removeObjectStore('storeFail')");
+ evalAndLog("store.removeIndex('indexFail')");
+
+ var store = evalAndLog("store = db.createObjectStore('storeFail', null)");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x', false)");
+
+ trans.abort();
+}
+
+function testSetVersionAbort3()
+{
+ checkMetadataEmpty();
+ result = evalAndLog("result = startSetVersion('version fail')");
+ result.onsuccess = addIDBObjects;
+}
+
+function addIDBObjects()
+{
+ debug("addIDBObjects():");
+ verifySuccessEvent(event);
+ var trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = testSetVersionAbort4;
+ trans.oncomplete = unexpectedCompleteCallback;
+
+ var store = evalAndLog("store = db.createObjectStore('storeFail', null)");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x', false)");
+
+ trans.abort();
+}
+
+function testSetVersionAbort4()
+{
+ checkMetadataEmpty();
+ result = evalAndLog("result = startSetVersion('version fail')");
+ result.onsuccess = addIDBObjectsAndCommit;
+}
+
+function addIDBObjects()
+{
+ debug("addIDBObjects():");
+ verifySuccessEvent(event);
+ var trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+
+ var store = evalAndLog("store = db.createObjectStore('storeFail', null)");
+ var index = evalAndLog("index = store.createIndex('indexFail', 'x', false)");
+
+ commitAndContinue(testSetVersionAbort5);
+}
+
+function commitAndContinue(continueFunction)
+{
+ window.setTimeout(continueFunction, 0);
+}
+
+function testSetVersionAbort5()
+{
+ checkMetadataExistingObjectStore();
+ result = evalAndLog("result = startSetVersion('version fail')");
+ result.onsuccess = removeIDBObjects;
+}
+
+function removeIDBObjects()
+{
+ debug("removeIDBObjects():");
+ verifySuccessEvent(event);
+ var trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = testSetVersionAbort6;
+ trans.oncomplete = unexpectedCompleteCallback;
+
+ var store = evalAndLog("store = trans.objectStore('storeFail')");
+ evalAndLog("store.removeIndex('indexFail')");
+ evalAndLog("db.removeObjectStore('storeFail')");
+
+ trans.abort();
+}
+
+function testSetVersionAbort6()
+{
+ checkMetadataExistingObjectStore();
+ setNewVersion();
+}
+
+function startSetVersion(versionName)
+{
+ result = db.setVersion(versionName);
+ verifyResult(result);
+ result.onerror = unexpectedErrorCallback;
+ return result;
+}
+
+function checkMetadataEmpty()
+{
+ shouldBe("window.db.objectStores", "[]");
+ shouldBe("window.db.objectStores.length", "0");
+ shouldBe("window.db.objectStores.contains('storeFail')", "false");
+}
+
+function checkMetadataExistingObjectStore()
+{
+ shouldBe("db.objectStores", "['storeFail']");
+ shouldBe("db.objectStores.length", "1");
+ shouldBe("db.objectStores.contains('storeFail')", "true");
+}
+
+function setNewVersion()
+{
+ result = evalAndLog("db.setVersion('new version')");
+ verifyResult(result);
+ result.onsuccess = setVersionSuccess;
+ result.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+ debug("setVersionSuccess():");
+ verifySuccessEvent(event);
+ window.trans = evalAndLog("trans = event.result");
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = completeCallback;
+ window.completeEventFired = false;
+
+ deleteAllObjectStores(db, createObjectStoreAndStartTransaction);
+}
+
+function createObjectStoreAndStartTransaction()
+{
+ evalAndLog("db.createObjectStore('storeName', null)");
+ transaction = evalAndLog("db.transaction()");
+ transaction.onabort = abortCallback;
+ var store = evalAndLog("store = transaction.objectStore('storeName')");
+ shouldBeEqualToString("store.name", "storeName");
+}
+
+function completeCallback()
+{
+ verifyCompleteEvent(event);
+ window.completeEventFired = true;
+}
+
+function abortCallback()
+{
+ verifyAbortEvent(event);
+ checkForCompleteEvent();
+}
+
+function checkForCompleteEvent()
+{
+ if (completeEventFired) {
+ testPassed("oncomplete event had fired");
+ done();
+ } else
+ setTimeout(checkForCompleteEvent, 1);
+}
+
+var successfullyParsed = true;
+
+test();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/indexeddb/tutorial-expected.txt b/LayoutTests/storage/indexeddb/tutorial-expected.txt
new file mode 100644
index 0000000..f9175b7
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/tutorial-expected.txt
@@ -0,0 +1,4 @@
+Please view source for more information on what this is doing and why...
+
+Everything worked!
+
diff --git a/LayoutTests/storage/indexeddb/tutorial.html b/LayoutTests/storage/indexeddb/tutorial.html
new file mode 100644
index 0000000..db9f2fa
--- /dev/null
+++ b/LayoutTests/storage/indexeddb/tutorial.html
@@ -0,0 +1,437 @@
+<html><title>IndexedDB Tutorial</title>
+<script>
+
+// This is a tutorial that highlights many of the features of IndexedDB along witha number of
+// caveats that currently exist in Chromium/WebKit but which will hopefully be improved upon
+// over time.
+//
+// The latest version of the spec can be found here:
+// http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html but note that there are quite a
+// few bugs currently opened against it and some major unresolved issues (like whether dynamic
+// transactions should be in for v1). Many of the bugs are filed here:
+// http://www.w3.org/Bugs/Public/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&component=Indexed+Database+API&longdesc_type=allwordssubstr&longdesc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&status_whiteboard_type=allwordssubstr&status_whiteboard=&keywords_type=allwords&keywords=&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailtype1=substring&email1=&emailtype2=substring&email2=&bug_id_type=anyexact&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&cmdtype=doit&order=Reuse+same+sort+as+last+time&known_name=IndexedDB&query_based_on=IndexedDB&field0-0-0=noop&type0-0-0=noop&value0-0-0=
+// Discussion happens on public-webapps@w3.org
+//
+// Although not user friendly, additional capabilities and example code can be found in the
+// tests for IndexedDB which are here:
+// http://trac.webkit.org/browser/trunk/LayoutTests/storage/indexeddb
+//
+// This document is currently maintained by Jeremy Orlow <jorlow@chromium.org>
+
+
+// This is not an ideal layout test since it doesn't verify things as thoroughly as it could,
+// but adding such content would make it much more cluttered and thus wouldn't serve its primary
+// goal of teaching people IndexedDB. That said, it does have a good amount of coverage and
+// serves as a living document describing what's expected to work and how within WebKit so it
+// seems well worth having checked in.
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+
+
+function setup()
+{
+ // As this API is still experimental, it's being shipped behind vendor specific prefixes.
+ if ('webkitIndexedDB' in window) {
+ indexedDB = webkitIndexedDB;
+ IDBCursor = webkitIDBCursor;
+ IDBKeyRange = webkitIDBKeyRange;
+ IDBTransaction = webkitIDBTransaction;
+ }
+
+ // This tutorial assumes that Mozilla and WebKit match each other which isn't true at the
+ // moment, but we can hope it'll become true over time.
+ if ('moz_indexedDB' in window) {
+ indexedDB = moz_indexedDB;
+ // Not implemented by them yet. I'm just guessing what they'll be.
+ IDBCursor = moz_IDBCursor;
+ IDBKeyRange = moz_IDBKeyRange;
+ IDBTransaction = moz_IDBTransaction;
+ }
+}
+
+function log(txt)
+{
+ document.getElementById("logger").innerHTML += txt + "<br>";
+}
+
+function logError(txt)
+{
+ log("<font color=red>" + txt + "</font>");
+}
+
+function start()
+{
+ setup();
+
+ // This is an example of one of the many asynchronous commands in IndexedDB's async interface.
+ // Each returns an IDBRequest object which has "success" and "error" event handlers. You can use
+ // "addEventListener" if you'd like, but I'm using the simpler = syntax. Only one or the other
+ // will fire. You're guaranteed that they won't fire until control is returned from JavaScript
+ // execution.
+ var request = indexedDB.open("myDB", "This is a description of the database."); // Chromium/WebKit doesn't yet require the description, but it will soon.
+ request.onsuccess = onOpen;
+ request.onerror = unexpectedError;
+}
+
+function unexpectedError()
+{
+ // If an asynchronous call results in an error, an "error" event will fire on the IDBRequest
+ // object that was returned and the event's code and message attributes will be populated with
+ // the correct values.
+ logError("Error " + event.code + ": " + event.message);
+
+ // Unfortunately, Chromium/WebKit do not implicitly abort a transaction when an error occurs
+ // within one of its async operations. In the future, when an error occurs and the event is
+ // not canceled, the transaction will be aborted.
+ if (currentTransaction)
+ currentTransaction.abort();
+}
+
+function onOpen()
+{
+ // If an asynchronous call results in success, a "success" event will fire on the IDBRequest
+ // object that was returned and the call's result will be placed in the event's "result"
+ // attribute. In some cases, the expected result will be null.
+ window.db = event.result;
+
+ // The IDBDatabase object has a "version" attribute. This can only be set by calling
+ // "setVersion" on the database and supplying a new version. This also starts a new
+ // transaction which is very special. There are many details and gotchas surrounding
+ // setVersion which we'll get into later.
+ if (db.version == "1.0") {
+ // We could skip setting up the object stores and indexes if this were a real application
+ // that wasn't going to change things without changing the version number. But since this
+ // is both a tutorial and a living document, we'll go on and set things up every time we run.
+ }
+ var request = db.setVersion("1.0");
+ request.onsuccess = onSetVersion;
+ request.onerror = unexpectedError;
+}
+
+function onSetVersion()
+{
+ // We are now in a setVersion transaction. Such a transaction is the only place where one
+ // can add or remove indexes and objectStores. The result (property of event) is an
+ // IDBTransaction object that has "complete", "abort", and "timeout" event handlers which tell
+ // us when the transaction has committed, aborted, or timed out.
+ window.currentTransaction = event.result;
+ currentTransaction.oncomplete = onSetVersionComplete;
+ currentTransaction.onabort = unexpectedAbort;
+
+ // Delete existing object stores.
+ while (db.objectStores.length)
+ db.removeObjectStore(db.objectStores[0]);
+
+ // Now that we have a blank slate, let's create an objectStore. An objectStore is simply an
+ // ordered mapping of keys to values. We can iterate through ranges of keys or do individual
+ // lookups. ObjectStores don't have any schema.
+ //
+ // Keys can be integers, strings, or null. (The spec also defines dates and there's talk of
+ // handling arrays, but these are not implemented yet in Chromium/WebKit.) Values can be
+ // anything supported by the structured clone algorithm
+ // (http://dev.w3.org/html5/spec/Overview.html#internal-structured-cloning-algorithm) which
+ // is a superset of what can be expressed in JSON. (Note that Chromium/WebKit does not fully
+ // implement the structured clone algorithm yet, but it definitely handles anything JSON
+ // serializable.)
+ //
+ // There are two types of objectStores: ones where the path is supplied manually every time a
+ // value is inserted and those with a "key path". A keyPath is essentially a JavaScript
+ // expression that is evaluated on every value to extract a key. For example, if you pass in
+ // the value of "{fname: 'john', lname: 'doe', address: {street: 'Buckingham Palace", number:
+ // 76}, siblings: ["Nancy", "Marcus"], id: 22}" and an objectStore has a keyPath of "id" then
+ // 22 will be the key for this value. In objectStores, each key must be unique.
+ //
+ // Note that the exact syntax allowed for keyPaths is not yet well specified, but
+ // Chromium/WebKit currently allows paths that are multiple levels deep within an object and
+ // allows that to be intermixed with array dereferences. So, for example, a key path of
+ // "address.number" or "siblings[0]" would be legal (provided every entry had an address with
+ // a number attribute and at least one sibling). You can even go wild and say
+ // "foo[0][2].bar[0].baz.test[1][2][3]". It's possible this will change in the future though.
+ //
+ // If you set autoIncrement (the third optional parameter), IndexedDB will generate a key
+ // for your entry automatically. And if you have a keyPath set, it'll set the value at
+ // the location of the keyPath _in the database_ (i.e. it will not modify the value you pass
+ // in to put/add). Unfortunately autoIncrement is not yet implemented in Chromium/WebKit.
+ //
+ // Let's now create an objectStore for people. We'll supply a key path in this case.
+ var objectStore = db.createObjectStore("people", "id");
+
+ // Notice that it returned synchronously. The rule of thumb is that any call that touches (in
+ // any way) keys or values is asynchronous and any other call (besides setVersion and open) are
+ // asynchronous.
+ //
+ // Now let's create some indexes. Indexes allow you to create other keys via key paths which
+ // will also point to a particular value in an objectStore. In this example, we'll create
+ // indexes for a persons first and last name. Indexes can optionally be specified to not be
+ // unique, which is good in the case of names. The first parameter is the name of the index.
+ // Second is the key path. The third specifies uniqueness.
+ var fname = objectStore.createIndex("fname", "fname", false);
+ var lname = objectStore.createIndex("lname", "lname", false);
+
+ // Note that if you wanted to delete these indexes, you can either call objectStore.removeIndex
+ // or simply delete the objectStores that own the indexes. (Note that removeObjectStore and
+ // removeIndex may be changed to deleteObjectStore and deleteIndex in the future.)
+ //
+ // If we wanted to, we could populate the objectStore with some data here or do anything else
+ // allowed in a normal (i.e. non-setVersion) transaction. This is useful so that data migrations
+ // can be atomic with changes to the objectStores/indexes.
+ //
+ // Because we haven't actually made any new asynchronous requests, this transaction will
+ // start committing as soon as we leave this function. This will cause oncomplete event handler
+ // for the transaction will fire shortly after. IndexedDB transactions commit whenever control is
+ // returned from JavaScript with no further work being queued up against the transaction. This
+ // means one cannot call setTimeout, do an XHR, or anything like that and expect my transaction
+ // to still be around when that completes.
+
+}
+
+function unexpectedAbort()
+{
+ logError("A transaction aborted unexpectedly!");
+}
+
+function onSetVersionComplete()
+{
+ // Lets create a new transaction and then not schedule any work on it to watch it abort itself.
+ // Transactions (besides those created with setVersion) are created synchronously. All three
+ // parameters are optional for transaction.
+ //
+ // The first specifies which object stores to lock. The spec specifies "dynamic transactions"
+ // which don't require this and which have finer grained locks, but no one yet supports this and
+ // it may even be dropped from the first version of the spec, so I don't suggest you rely on it.
+ // Chromium/WebKit also does not yet support anything finer grained than database level locking,
+ // so in this tutorial we'll just pass in the empty array which means "lock the world".
+ //
+ // The second parameter specifies the locking mode. The default is READ_ONLY (i.e. a shared lock).
+ // That's fine for this case, but later we'll ask for IDBTransaction.READ_WRITE. At the moment,
+ // Chromium/WebKit pretends every transaction is READ_WRITE, which is kind of bad. (Note that
+ // SNAPSHOT_READ will soon be removed from the spec.)
+ //
+ // The last parameter is the timeout length. At the moment, Chromium/WebKit defaults to 0 which
+ // means never, but it's possible we'll change this in the future, so set it if you really care.
+ window.currentTransaction = db.transaction([], IDBTransaction.READ_WRITE, 0);
+ currentTransaction.oncomplete = unexpectedComplete;
+ currentTransaction.onabort = onTransactionAborted;
+
+ // Verify that "people" is the only object store in existance. The objectStores attribute is
+ // a DOMStringList which is somewhat like an array.
+ var objectStoreList = db.objectStores;
+ if (objectStoreList.length != 1
+ || !objectStoreList.contains("people")
+ || objectStoreList.item(0) != "people"
+ || objectStoreList[0] != "people") {
+ logError("Something went wrong.");
+ }
+
+ // Let's grab a handle to the objectStore. This handle is tied to the transaction that creates
+ // it and thus becomes invalid once this transaction completes.
+ var objectStore = currentTransaction.objectStore("people");
+ if (!objectStore)
+ logError("Something went wrong.");
+
+ // If we try to grab an objectStore that doesn't exist, IndexedDB throws an exception.
+ try {
+ currentTransaction.objectStore("x");
+ logError("Something went wrong.");
+ } catch (e) {
+ // Note that the error messages in exceptions are mostly lies at the moment. The reason is
+ // that the spec re-uses exception codes for existing exceptions and there's no way we can
+ // disambiguate between the two. The best work-around at the moment is to look at
+ // http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#the-idbdatabaseexception-interface
+ // to figure out what the number corresponds to. We will try to resolve this soon in spec-land.
+ }
+
+ // Verify that fname and lname are the only indexes in existance.
+ if (objectStore.indexNames.length != 2)
+ logError("Something went wrong.");
+
+ // Note that no async actions were ever queued up agianst our transaction, so it'll abort once
+ // we leave this context.
+}
+
+function unexpectedComplete()
+{
+ logError("A transaction committed unexpectedly!");
+}
+
+function onTransactionAborted()
+{
+ // Now let's make a real transaction and a person to our objectStore.
+ window.currentTransaction = db.transaction(["people"], IDBTransaction.READ_WRITE, 0);
+ currentTransaction.onabort = unexpectedAbort;
+
+ var people = currentTransaction.objectStore("people");
+ var request = people.put({fname: 'John', lname: 'Doe', id: 1}); // If our objectStore didn't have a key path, the second parameter would have been the key.
+ request.onsuccess = onPutSuccess;
+ request.onerror = unexpectedError;
+
+ // While we're at it, why not add a few more? Multiple queued up async commands will be executed
+ // sequentially (though there is talk of prioritizing cursor.continue--see discussion below). Since
+ // we don't care about the individual commands' successes, we'll only bother with on error handlers.
+ //
+ // Remember that our implementation of unexpectedError should abort the "currentTransaction" in the
+ // case of an error. (Though no error should occur in this case.)
+ people.put({fname: 'Jane', lname: 'Doe', id: 2}).onerror = unexpectedError;
+ people.put({fname: 'Philip', lname: 'Fry', id: 3}).onerror = unexpectedError;
+
+ // Not shown here are the .remove method (which is soon to be renamed .delete) and .add (which is
+ // like .put except that it fires an onerror if the element already exists).
+}
+
+function onPutSuccess()
+{
+ // Result is the key used for the put.
+ if (event.result !== 1)
+ logError("Something went wrong.");
+
+ // We should be able to request the transaction via event.transaction from within any event handler
+ // (like this one) but this is not yet implemented in Chromium/WebKit. As a work-around, we use the
+ // global "currentTransaction" variable we set above.
+ currentTransaction.oncomplete = onPutTransactionComplete;
+}
+
+function onPutTransactionComplete()
+{
+ // OK, now let's query the people objectStore in a couple different ways. First up, let's try get.
+ // It simply takes in a key and returns a request whose result will be the value.
+ window.currentTransaction = db.transaction(["people"], IDBTransaction.READ_WRITE, 0);
+ currentTransaction.onabort = unexpectedAbort;
+
+ var people = currentTransaction.objectStore("people");
+ var request = people.get(1);
+ request.onsuccess = onGetSuccess;
+ request.onerror = unexpectedError;
+
+ // Note that multiple objectStore (or index) method calls will return different objects (that still
+ // refer to the same objectStore/index on disk).
+ people.someProperty = true;
+ if (currentTransaction.objectStore("people").someProperty)
+ logError("Something went wrong.");
+}
+
+function onGetSuccess()
+{
+ if (event.result.fname !== "John")
+ logError("Something went wrong.");
+
+ // Events have a .source attribute which is the object that fired the event. In this case, it's our
+ // "people" objectStore object.
+ var people = event.source;
+
+ // Now let's try opening a cursor from id 1 (exclusive/open) to id 3 (inclusive/closed). This means
+ // we'll get the objects for ids 2 and 3. You can also create cursors that are only right or only
+ // left bounded or ommit the bound in order to grab all objects. You can also specify a direction
+ // which can be IDBCursor.NEXT (default) for the cursor to move forward, NEXT_NO_DUPLICATE to only
+ // return unique entires (only applies to indexes with unique set to false), PREV to move backwards,
+ // and PREV_NO_DUPLICATE.
+ var keyRange = IDBKeyRange.bound(1, 3, true, false);
+ var request = people.openCursor(keyRange, IDBCursor.NEXT);
+ request.onsuccess = onObjectStoreCursor;
+ request.onerror = unexpectedError;
+}
+
+function onObjectStoreCursor()
+{
+ // The result of openCursor is an IDBCursor object or null if there are no (more--see below)
+ // records left.
+ var cursor = event.result;
+ if (cursor === null) {
+ cursorComplete(event.source); // The soruce is still an objectStore.
+ return;
+ }
+
+ // We could use these values if we wanted to.
+ var key = cursor.key;
+ var value = cursor.value;
+
+ // cursor.count is probably going to be removed.
+ // cursor.update and .remove are not yet implemented in Chromium/WebKit.
+
+ // cursor.continue will reuse the same request object as what openCursor returned. In the future,
+ // we MAY prioritize .continue() calls ahead of all other async operations queued up. This will
+ // introduce a level of non-determinism but should speed things up. Mozilla has already implemented
+ // this non-standard behavior, from what I've head.
+ event.result.continue();
+}
+
+function cursorComplete(objectStore)
+{
+ // While still in the same transaction, let's now do a lookup on the lname index.
+ var lname = objectStore.index("lname");
+
+ // Note that the spec has not been updated yet, but instead of get and getObject, we now
+ // have getKey and get. The former returns the objectStore's key that corresponds to the key
+ // in the index. get returns the objectStore's value that corresponds to the key in the
+ // index.
+ var request = lname.getKey("Doe");
+ request.onsuccess = onIndexGetSuccess;
+ request.onerror = unexpectedError;
+}
+
+function onIndexGetSuccess()
+{
+ // Because we did "getKey" the result is the objectStore's key.
+ if (event.result !== 1)
+ logError("Something went wrong.");
+
+ // Similarly, indexes have openCursor and openKeyCursor. We'll try a few of them with various
+ // different IDBKeyRanges just to demonstrate how to use them, but we won't bother to handle
+ // the onsuccess conditions.
+ var lname = event.source;
+ lname.openCursor(IDBKeyRange.leftBound("Doe", false), IDBCursor.NEXT_NO_DUPLICATE);
+ lname.openCursor(null, IDBCursor.PREV_NO_DUPLICATE);
+ lname.openCursor(IDBKeyRange.rightBound("ZZZZ"));
+ lname.openCursor(IDBKeyRange.only("Doe"), IDBCursor.PREV);
+ lname.openCursor();
+ lname.openKeyCursor();
+
+ // We should be able to request the transaction via event.transaction from within any event handler
+ // (like this one) but this is not yet implemented in Chromium/WebKit. As a work-around, we use the
+ // global "currentTransaction" variable we set above.
+ currentTransaction.oncomplete = onAllDone;
+}
+
+function onAllDone()
+{
+ log("Everything worked!");
+ layoutTestController.notifyDone();
+}
+
+// The way setVersion is supposed to work:
+// To keep things simple to begin with, objectStores and indexes can only be created in a setVersion
+// transaction and one can only run if no other connections are open to the database. This is designed
+// to save app developers from having an older verison of a web page that expects a certain set of
+// objectStores and indexes from breaking in odd ways when things get changed out from underneith it.
+// In the future, we'll probably add a more advanced mechanism, but this is it for now.
+// Because a setVersion transaction could stall out nearly forever until the user closes windows,
+// we've added a "blocked" event to the request object returned by setVersion. This will fire if the
+// setVersion transaction can't begin because other windows have an open connection. The app can then
+// either pop something up telling the user to close windows or it can tell the other windows to call
+// .close() on their database handle. .close() halts any new transactions from starting and waits for
+// the existing ones to finish. It then closes the connection and any indexedDB calls afterwards are
+// invalid (they'll probably throw, but this isn't specified yet). We may specify .close() to return
+// an IDBRequest object so that we can fire the onsuccess when the close completes.
+// Once inside a setVersion transaction, you can do anything you'd like. The one connection which
+// was allowed to stay open to complete the setVersion transaction will stay alive. Multiple
+// setVersion transactions can be queued up at once and will fire in the order queued (though
+// this obviously only works if they're queued in the same page).
+//
+// The current status of setVersion in Chromium/WebKit:
+// In Chromium/WebKit we currently don't enforce the "all connections must be closed before a
+// setVersion transaction starts" rule. We also don't implement database.close() or have a blocked
+// event on the request .setVersion() returns.
+//
+// The current status of workers:
+// Chromium/WebKit do not yet support workers using IndexedDB. Support for the async interface
+// will likely come before the sync interface. For now, a work-around is using postMessage to tell
+// the page what to do on the worker's behalf in an ad-hoc manner. Anything that can be serialized
+// to disk can be serialized for postMessage.
+
+</script>
+<body onload="start()">
+Please view source for more information on what this is doing and why...<br><br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/multiple-databases-garbage-collection.html b/LayoutTests/storage/multiple-databases-garbage-collection.html
index 45246a9..8451acf 100644
--- a/LayoutTests/storage/multiple-databases-garbage-collection.html
+++ b/LayoutTests/storage/multiple-databases-garbage-collection.html
@@ -1,77 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-function GC()
-{
- // Force GC.
- if (window.GCController)
- GCController.collect();
- else {
- for (var i = 0; i < 10000; ++i) {
- ({ });
- }
- }
-}
-
-// Variable for the database that will never be forgotten
-var persistentDB = 0;
-// Variable for the forgotten database
-var forgottenDB = 0;
-
-var completed = 0;
-function checkCompletion()
-{
- if (++completed == 2 && window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- persistentDB = openDatabase("MultipleDatabasesTest1", "1.0", "Test one out of a set of databases being destroyed (1)", 32768);
- forgottenDB = openDatabase("MultipleDatabasesTest2", "1.0", "Test one out of a set of databases being destroyed (2)", 32768);
-
- persistentDB.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS DataTest (randomData)", [], function(tx, result) {
- for (var i = 0; i < 25; ++i)
- tx.executeSql("INSERT INTO DataTest (randomData) VALUES (1)", []);
- });
- }, function(err) {
- log("Persistent Database Transaction Errored - " + err);
- checkCompletion();
- }, function() {
- log("Persistent Database Transaction Complete");
- checkCompletion();
- });
-
- forgottenDB.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS EmptyTable (unimportantData)", []);
- }, function(err) {
- log("Forgotten Database Transaction Errored - " + err);
- forgottenDB = 0;
- GC();
- checkCompletion();
- }, function() {
- log("Forgotten Database Transaction Complete");
- forgottenDB = 0;
- GC();
- checkCompletion();
- });
-}
-
-</script>
-<body onload="runTest();">
+<script src="resources/database-common.js"></script>
+<script src="multiple-databases-garbage-collection.js"></script>
+<body onload="setupAndRunTest();">
This test opens two databases, queues up a series of operations on both, then "forgets" about one of them.
After forcing GC, resources associated with that database should be freed gracefully.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/multiple-databases-garbage-collection.js b/LayoutTests/storage/multiple-databases-garbage-collection.js
new file mode 100644
index 0000000..9a2b27f
--- /dev/null
+++ b/LayoutTests/storage/multiple-databases-garbage-collection.js
@@ -0,0 +1,56 @@
+function GC()
+{
+ // Force GC.
+ if (window.GCController)
+ GCController.collect();
+ else {
+ for (var i = 0; i < 10000; ++i) {
+ ({ });
+ }
+ }
+}
+
+// Variable for the database that will never be forgotten
+var persistentDB = 0;
+// Variable for the forgotten database
+var forgottenDB = 0;
+
+var completed = 0;
+function checkCompletion()
+{
+ if (++completed == 2 && window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ persistentDB = openDatabaseWithSuffix("MultipleDatabasesTest1", "1.0", "Test one out of a set of databases being destroyed (1)", 32768);
+ forgottenDB = openDatabaseWithSuffix("MultipleDatabasesTest2", "1.0", "Test one out of a set of databases being destroyed (2)", 32768);
+
+ persistentDB.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS DataTest (randomData)", [], function(tx, result) {
+ for (var i = 0; i < 25; ++i)
+ tx.executeSql("INSERT INTO DataTest (randomData) VALUES (1)", []);
+ });
+ }, function(err) {
+ log("Persistent Database Transaction Errored - " + err);
+ checkCompletion();
+ }, function() {
+ log("Persistent Database Transaction Complete");
+ checkCompletion();
+ });
+
+ forgottenDB.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS EmptyTable (unimportantData)", []);
+ }, function(err) {
+ log("Forgotten Database Transaction Errored - " + err);
+ forgottenDB = 0;
+ GC();
+ checkCompletion();
+ }, function() {
+ log("Forgotten Database Transaction Complete");
+ forgottenDB = 0;
+ GC();
+ checkCompletion();
+ });
+}
diff --git a/LayoutTests/storage/multiple-transactions-on-different-handles.html b/LayoutTests/storage/multiple-transactions-on-different-handles.html
index bcdab82..0a18c32 100644
--- a/LayoutTests/storage/multiple-transactions-on-different-handles.html
+++ b/LayoutTests/storage/multiple-transactions-on-different-handles.html
@@ -1,96 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-var complete = 0;
-
-function checkCompletion()
-{
- // The test should end after two transactions
- if (++complete == 2 && window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-// Opens the database used in this test case
-function openTestDatabase()
-{
- return openDatabase("MultipleTransactionsOnDifferentHandlesTest",
- "1.0",
- "Test to make sure that queueing multiple transactions on different DB handles does not result in a deadlock.",
- 32768);
-}
-
-function statementSuccessCallback(dbName, statementType)
-{
- log(dbName + " " + statementType + " statement succeeded");
-}
-
-function statementErrorCallback(dbName, statementType, error)
-{
- log(dbName + " " + statementType + " statement failed: " + error.message);
-}
-
-// Runs a transaction on the given database
-function runTransaction(db, dbName, val)
-{
- db.transaction(function(tx) {
- // Execute a read-only statement
- tx.executeSql("SELECT COUNT(*) FROM Test;", [],
- function(result) { statementSuccessCallback(dbName, "read"); },
- function(tx, error) { statementErrorCallback(dbName, "read", error); });
-
- // Execute a write statement to make sure SQLite tries to acquire an exclusive lock on the DB file
- tx.executeSql("INSERT INTO Test VALUES (?);", [val],
- function(result) { statementSuccessCallback(dbName, "write"); },
- function(tx, error) { statementErrorCallback(dbName, "write", error); });
- }, function(error) {
- // Transaction failure callback
- log(dbName + " transaction failed: " + error.message);
- checkCompletion();
- }, function() {
- // Transaction success callback
- log(dbName + " transaction succeeded");
- checkCompletion();
- });
-}
-
-// We need to guarantee that the Test table exists before we run our test.
-// Therefore, the test code is in the successCallback of the transaction that creates the table.
-function runTest() {
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- try {
- var db = openTestDatabase();
- db.transaction(function(tx) {
- // Create the Test table if it does not exist
- tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo int);", [],
- function(result) {}, function(tx, error) {});
- }, function(error) {
- log("Creating the Test table failed: " + error.message);
- }, function() {
- // The Test table was created successfully
- var db1 = openTestDatabase();
- var db2 = openTestDatabase();
- if (db1 == db2)
- log("failure: db1 == db2");
- else {
- runTransaction(db1, "db1", 1);
- runTransaction(db2, "db2", 2);
- }
- });
- } catch(err) {}
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="multiple-transactions-on-different-handles.js"></script>
</head>
-<body onload="runTest();">
+<body onload="setupAndRunTest();">
This is a test to see if queueing up multiple transactions on different handles to the same database results in a deadlock.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/multiple-transactions-on-different-handles.js b/LayoutTests/storage/multiple-transactions-on-different-handles.js
new file mode 100644
index 0000000..9ab4b93
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions-on-different-handles.js
@@ -0,0 +1,78 @@
+var complete = 0;
+
+function checkCompletion()
+{
+ // The test should end after two transactions
+ if (++complete == 2 && window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+// Opens the database used in this test case
+function openTestDatabase()
+{
+ return openDatabaseWithSuffix("MultipleTransactionsOnDifferentHandlesTest",
+ "1.0",
+ "Test to make sure that queueing multiple transactions on different DB handles does not result in a deadlock.",
+ 32768);
+}
+
+function statementSuccessCallback(dbName, statementType)
+{
+ log(dbName + " " + statementType + " statement succeeded");
+}
+
+function statementErrorCallback(dbName, statementType, error)
+{
+ log(dbName + " " + statementType + " statement failed: " + error.message);
+}
+
+// Runs a transaction on the given database
+function runTransaction(db, dbName, val)
+{
+ db.transaction(function(tx) {
+ // Execute a read-only statement
+ tx.executeSql("SELECT COUNT(*) FROM Test;", [],
+ function(result) { statementSuccessCallback(dbName, "read"); },
+ function(tx, error) { statementErrorCallback(dbName, "read", error); });
+
+ // Execute a write statement to make sure SQLite tries to acquire an exclusive lock on the DB file
+ tx.executeSql("INSERT INTO Test VALUES (?);", [val],
+ function(result) { statementSuccessCallback(dbName, "write"); },
+ function(tx, error) { statementErrorCallback(dbName, "write", error); });
+ }, function(error) {
+ // Transaction failure callback
+ log(dbName + " transaction failed: " + error.message);
+ checkCompletion();
+ }, function() {
+ // Transaction success callback
+ log(dbName + " transaction succeeded");
+ checkCompletion();
+ });
+}
+
+// We need to guarantee that the Test table exists before we run our test.
+// Therefore, the test code is in the successCallback of the transaction that creates the table.
+function runTest() {
+ try {
+ var db = openTestDatabase();
+ db.transaction(function(tx) {
+ // Create the Test table if it does not exist
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo int);", [],
+ function(result) {}, function(tx, error) {});
+ }, function(error) {
+ log("Creating the Test table failed: " + error.message);
+ }, function() {
+ // The Test table was created successfully
+ var db1 = openTestDatabase();
+ var db2 = openTestDatabase();
+ if (db1 == db2)
+ log("failure: db1 == db2");
+ else {
+ runTransaction(db1, "db1", 1);
+ runTransaction(db2, "db2", 2);
+ }
+ });
+ } catch(err) {}
+}
+
+
diff --git a/LayoutTests/storage/multiple-transactions.html b/LayoutTests/storage/multiple-transactions.html
index 56656ca..3597440 100644
--- a/LayoutTests/storage/multiple-transactions.html
+++ b/LayoutTests/storage/multiple-transactions.html
@@ -1,53 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-var complete = 0;
-
-function checkCompletion()
-{
- if (++complete == 2 && window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- var db = openDatabase("MultipleTransactionsTest", "1.0", "Test to make sure multiple transactions can be queued at once for an HTML5 database", 32768);
-
- db.transaction(function(tx) {
- log("Transaction 1 Started");
- }, function(err) {
- log("Transaction 1 Errored - " + err);
- checkCompletion();
- }, function() {
- log("Transaction 1 Succeeded");
- checkCompletion();
- });
-
- db.transaction(function(tx) {
- log("Transaction 2 Started");
- }, function(err) {
- log("Transaction 2 Errored - " + err);
- checkCompletion();
- }, function() {
- log("Transaction 2 Succeeded");
- checkCompletion();
- });
-}
-
-</script>
+<script src="resources/database-common.js"></script>
+<script src="multiple-transactions.js"></script>
<head>
-<body onload="runTest();">
+<body onload="setupAndRunTest();">
This is a test to see if the database API allows multiple transactions to be queued on the same database at once:<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/multiple-transactions.js b/LayoutTests/storage/multiple-transactions.js
new file mode 100644
index 0000000..85cdabc
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions.js
@@ -0,0 +1,32 @@
+var complete = 0;
+
+function checkCompletion()
+{
+ if (++complete == 2 && window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ var db = openDatabaseWithSuffix("MultipleTransactionsTest", "1.0", "Test to make sure multiple transactions can be queued at once for an HTML5 database", 32768);
+
+ db.transaction(function(tx) {
+ log("Transaction 1 Started");
+ }, function(err) {
+ log("Transaction 1 Errored - " + err);
+ checkCompletion();
+ }, function() {
+ log("Transaction 1 Succeeded");
+ checkCompletion();
+ });
+
+ db.transaction(function(tx) {
+ log("Transaction 2 Started");
+ }, function(err) {
+ log("Transaction 2 Errored - " + err);
+ checkCompletion();
+ }, function() {
+ log("Transaction 2 Succeeded");
+ checkCompletion();
+ });
+}
diff --git a/LayoutTests/storage/null-callbacks-expected.txt b/LayoutTests/storage/null-callbacks-expected.txt
new file mode 100644
index 0000000..8311675
--- /dev/null
+++ b/LayoutTests/storage/null-callbacks-expected.txt
@@ -0,0 +1,2 @@
+This test checks that 'null' can be used wherever we expect an optional callback.
+PASS
diff --git a/LayoutTests/storage/null-callbacks.html b/LayoutTests/storage/null-callbacks.html
new file mode 100644
index 0000000..4ca0e1c
--- /dev/null
+++ b/LayoutTests/storage/null-callbacks.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+<script>
+function finishTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ try {
+ var db = openDatabase("NullCallbacks", "1.0", "Test for null callbacks.", 1);
+ db.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo INT)", null);
+ tx.executeSql("INSERT INTO Test VALUES (?)", [1], null, null);
+ tx.executeSql("INSERT INTO Test VALUES (?)", [2], null);
+ tx.executeSql("INSERT INTO Test VALUES (3)", null, null, null);
+ tx.executeSql("INSERT INTO Test VALUES (?)", [4], null,
+ function(tx, error) {});
+ }, null, null);
+
+ db.transaction(function(tx) {
+ tx.executeSql("INSERT INTO Test VALUES (?)", [5]);
+ }, null, function() { finishTest(); });
+ } catch(err) {
+ document.getElementById("console").innerHTML = "FAIL";
+ finishTest();
+ }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+This test checks that 'null' can be used wherever we expect an optional callback.
+<pre id="console">PASS</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/open-database-creation-callback-expected.txt b/LayoutTests/storage/open-database-creation-callback-expected.txt
new file mode 100644
index 0000000..d86ab72
--- /dev/null
+++ b/LayoutTests/storage/open-database-creation-callback-expected.txt
@@ -0,0 +1,2 @@
+This test tests openDatabase()'s creation callback.
+
diff --git a/LayoutTests/storage/open-database-creation-callback-isolated-world-expected.txt b/LayoutTests/storage/open-database-creation-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..fa96a67
--- /dev/null
+++ b/LayoutTests/storage/open-database-creation-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the openDatabase() creation callback is called in the right world.
+
diff --git a/LayoutTests/storage/open-database-creation-callback-isolated-world.html b/LayoutTests/storage/open-database-creation-callback-isolated-world.html
new file mode 100644
index 0000000..98a4664
--- /dev/null
+++ b/LayoutTests/storage/open-database-creation-callback-isolated-world.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the openDatabase() creation callback is called in the right world.
+<div id="console"></div>
+<script>
+var creationCallbacksExecuted = 0;
+function done()
+{
+ if ((++creationCallbacksExecuted == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function creationCallback1(db)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function creationCallback2(db)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function creationCallback1(db)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('OpenDatabaseCreationCallbackIsolatedWorld', '1.0', '', 1, creationCallback1);");
+
+ var db2 = openDatabase('OpenDatabaseCreationCallbackIsolatedWorld2', '1.0', '', 1, creationCallback2);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/open-database-creation-callback.html b/LayoutTests/storage/open-database-creation-callback.html
new file mode 100644
index 0000000..081645c
--- /dev/null
+++ b/LayoutTests/storage/open-database-creation-callback.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+<script>
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.setDatabaseQuota(32768);
+ layoutTestController.dumpDatabaseCallbacks();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ var transactionsRun = 0;
+
+ // Open a new database with a creation callback, and make sure the creation callback is queued
+ var creationCallbackCalled1 = false;
+ var db1Name = "OpenDatabaseCreationCallback1" + (new Date()).getTime();
+ var db1 = openDatabase(db1Name, "1.0", "", 1,
+ function(db) {
+ creationCallbackCalled1 = true;
+ if (db.version != "") {
+ log("Creation callback was called with a database with version " +
+ db.version + "; empty string expected.");
+ finishTest();
+ }
+ });
+
+ // Putting this code inside a transaction on 'db1' makes sure that it is executed after
+ // the creation callback is.
+ db1.transaction(function(tx) {
+ if (!creationCallbackCalled1) {
+ log("Creation callback for db1 was not called.");
+ finishTest();
+ }
+ if (++transactionsRun == 2)
+ finishTest();
+ });
+
+ // Try to open another handle to the same database.
+ // Since the version of this database is "" (empty string), openDatabase() should return
+ // a null handle and throw a INVALID_STATE_ERR exception.
+ var db1Fail = null;
+ try {
+ db1Fail = openDatabase(db1Name, "1.0", "", 1);
+ log("This statement should not have been executed; an INVALID_STATE_ERR exception should've been thrown.");
+ finishTest();
+ } catch(err) {
+ if (db1Fail) {
+ log("db1Fail should have been null.");
+ finishTest();
+ }
+ }
+
+ // Open a handle to another database, first without a creation callback, then with one.
+ // Make sure the creation callback is not called.
+ var creationCallbackCalled2 = false;
+ var db2 = openDatabase("OpenDatabaseCreationCallback2", "1.0", "", 1);
+ db2 = openDatabase("OpenDatabaseCreationCallback2", "1.0", "", 1,
+ function(db) { creationCallbackCalled2 = true; });
+ db2.transaction(function(tx) {
+ if (creationCallbackCalled2) {
+ log("Creation callback for db2 should not have been called.");
+ finishTest();
+ }
+ if (++transactionsRun == 2)
+ finishTest();
+ });
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+This test tests openDatabase()'s creation callback.
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/open-database-over-quota-expected.txt b/LayoutTests/storage/open-database-over-quota-expected.txt
new file mode 100644
index 0000000..d0c15e4
--- /dev/null
+++ b/LayoutTests/storage/open-database-over-quota-expected.txt
@@ -0,0 +1,2 @@
+This tests that calling openDatabase with a size over 5MB doesn't assert on debug builds.
+PASS
diff --git a/LayoutTests/storage/open-database-over-quota.html b/LayoutTests/storage/open-database-over-quota.html
new file mode 100644
index 0000000..2a9264d
--- /dev/null
+++ b/LayoutTests/storage/open-database-over-quota.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script>
+function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.clearAllDatabases();
+ }
+
+ try {
+ var db = openDatabase('OverQuotaOpen', '', 'Test for bug 36473: missing lock in call to doneCreatingDatabase', 10000000);
+ } catch (err) {
+ document.getElementById('result').innerHTML = 'PASS'
+ }
+}
+</script>
+</head>
+<body onload="runTest()">
+<div>This tests that calling openDatabase with a size over 5MB doesn't assert on debug builds.
+<div id="result">
+FAIL: We shouldn't have been able to open the database.
+</div>
+</body>
+</html>
diff --git a/LayoutTests/storage/open-database-while-transaction-in-progress.html b/LayoutTests/storage/open-database-while-transaction-in-progress.html
index 691f6fe..707a1a4 100644
--- a/LayoutTests/storage/open-database-while-transaction-in-progress.html
+++ b/LayoutTests/storage/open-database-while-transaction-in-progress.html
@@ -1,71 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-var complete = 0;
-
-function checkCompletion()
-{
- // The test should end after two transactions
- if (++complete == 1 && window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-// Opens the database used in this test case
-function openTestDatabase()
-{
- return openDatabase("OpenDatabaseWhileTransactionInProgressTest",
- "1.0",
- "Test to make sure that calling openDatabase() while a transaction is in progress on a different handle to the same database does not result in a deadlock.",
- 2100000); // 2MB + epsilon
-}
-
-// See https://bugs.webkit.org/show_bug.cgi?id=28207
-// In order to trigger this bug, the transaction must acquire an exclusive
-// lock on the DB file before trying to obtain a second handle to the same DB.
-// The only way to force SQLite to obtain an exclusive lock is to change more
-// than cache_size * page_size bytes in the database. The default value for
-// cache_size is 2000 pages, and the default page_size is 1024 bytes. So the
-// size of the blob must be at least 2MB.
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- try {
- var db1 = openTestDatabase();
- db1.transaction(function(tx) {
- // Create the Test table if it does not exist
- tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo BLOB);");
- tx.executeSql("INSERT INTO Test VALUES (ZEROBLOB(2097152));", [],
- function(result) {
- var db2 = openTestDatabase();
- log("openDatabase() succeeded.");
- },
- function(tx, error) {
- log("Executing statement failed: " + error.message);
- });
-
- // Clean up the DB to allow for repeated runs of this test
- // without needing to increase the default allowed quota (5MB)
- tx.executeSql("DELETE FROM Test;");
- }, function(error) {
- log("Transaction failed: " + error.message);
- }, function() {
- checkCompletion();
- });
- } catch(err) {}
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="open-database-while-transaction-in-progress.js"></script>
</head>
-<body onload="runTest();">
+<body onload="setupAndRunTest();">
This is a test to see if opening a database while a transaction is running on a different handle to the same database results in a deadlock.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/open-database-while-transaction-in-progress.js b/LayoutTests/storage/open-database-while-transaction-in-progress.js
new file mode 100644
index 0000000..7a1a0c0
--- /dev/null
+++ b/LayoutTests/storage/open-database-while-transaction-in-progress.js
@@ -0,0 +1,41 @@
+// Opens the database used in this test case
+function openTestDatabase()
+{
+ return openDatabaseWithSuffix("OpenDatabaseWhileTransactionInProgressTest",
+ "1.0",
+ "Test to make sure that calling openDatabase() while a transaction is in progress on a different handle to the same database does not result in a deadlock.",
+ 2100000); // 2MB + epsilon
+}
+
+// See https://bugs.webkit.org/show_bug.cgi?id=28207
+// In order to trigger this bug, the transaction must acquire an exclusive
+// lock on the DB file before trying to obtain a second handle to the same DB.
+// The only way to force SQLite to obtain an exclusive lock is to change more
+// than cache_size * page_size bytes in the database. The default value for
+// cache_size is 2000 pages, and the default page_size is 1024 bytes. So the
+// size of the blob must be at least 2MB.
+function runTest()
+{
+ var db1 = openTestDatabase();
+ db1.transaction(function(tx) {
+ // Create the Test table if it does not exist
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo BLOB);");
+ tx.executeSql("INSERT INTO Test VALUES (ZEROBLOB(2097152));", [],
+ function(result) {
+ var db2 = openTestDatabase();
+ log("openDatabase() succeeded.");
+ },
+ function(tx, error) {
+ log("Executing statement failed: " + error.message);
+ });
+
+ // Clean up the DB to allow for repeated runs of this test
+ // without needing to increase the default allowed quota (5MB)
+ tx.executeSql("DELETE FROM Test;");
+ }, function(error) {
+ log("Transaction failed: " + error.message);
+ }, function() {
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+ });
+}
diff --git a/LayoutTests/storage/private-browsing-readonly.html b/LayoutTests/storage/private-browsing-readonly.html
index 70a209a..972b123 100644
--- a/LayoutTests/storage/private-browsing-readonly.html
+++ b/LayoutTests/storage/private-browsing-readonly.html
@@ -46,6 +46,7 @@ function privateBrowsingErrorFunction(tx, error)
{
++completed;
writeMessageToLog("Private browsing statement " + completed + " completed with an error\n" + error.message);
+ return false;
}
function runSetup(transaction)
diff --git a/LayoutTests/storage/quota-tracking-expected.txt b/LayoutTests/storage/quota-tracking-expected.txt
index 62a2c6b..6d860f0 100644
--- a/LayoutTests/storage/quota-tracking-expected.txt
+++ b/LayoutTests/storage/quota-tracking-expected.txt
@@ -1,6 +1,6 @@
UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{file, , 0} database:QuotaManagementDatabase2
This test checks to make sure that quotas are enforced per-origin instead of per-database, as they were prior to http://trac.webkit.org/projects/webkit/changeset/29983.
-The test clears all databases, sets the quota for the origin to 32k, then tries to insert 17k of data into two databases. If things go as planned, the UI Delegate will be informed of the exceeded quota and should increase the quota for this origin. Inserting 17k of data the third time should succeed again.
+The test clears all databases, sets the quota for the origin to 40k, then tries to insert 17k of data into two databases. If things go as planned, the second insert should fail, the UI Delegate should be informed of the exceeded quota and should increase the quota for this origin. Inserting 17k of data the third time should succeed again.
Adding a table
Inserting some data
Done adding data
diff --git a/LayoutTests/storage/quota-tracking.html b/LayoutTests/storage/quota-tracking.html
index fb2cc9e..6e52523 100644
--- a/LayoutTests/storage/quota-tracking.html
+++ b/LayoutTests/storage/quota-tracking.html
@@ -1,9 +1,9 @@
<html>
<head>
<script>
-var database1;
-var database2;
-var database3;
+var database1 = null;
+var database2 = null;
+var database3 = null;
function log(message)
{
@@ -39,13 +39,15 @@ function checkCompletion(db)
{
log("Done adding data");
- db.complete = true;
- if (database1.complete && database2.complete && database3.complete)
+ if (database3) {
finishTest();
- else if (database2.complete)
+ } else if (database2) {
+ database3 = openDatabase("QuotaManagementDatabase3", "1.0", "Test for quota management <rdar://5628468>", 1);
testDatabase(database3);
- else
+ } else {
+ database2 = openDatabase("QuotaManagementDatabase2", "1.0", "Test for quota management <rdar://5628468>", 1);
testDatabase(database2);
+ }
}
function addData(db)
@@ -89,18 +91,12 @@ function runTest()
if (window.layoutTestController) {
layoutTestController.clearAllDatabases();
layoutTestController.dumpDatabaseCallbacks();
- layoutTestController.setDatabaseQuota(32768);
+ layoutTestController.setDatabaseQuota(40960);
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
database1 = openDatabase("QuotaManagementDatabase1", "1.0", "Test for quota management <rdar://5628468>", 1);
- database2 = openDatabase("QuotaManagementDatabase2", "1.0", "Test for quota management <rdar://5628468>", 1);
- database3 = openDatabase("QuotaManagementDatabase3", "1.0", "Test for quota management <rdar://5628468>", 1);
- database1.complete = false;
- database2.complete = false;
- database3.complete = false;
-
testDatabase(database1);
}
@@ -109,7 +105,7 @@ function runTest()
<body onload="runTest()">
This test checks to make sure that quotas are enforced per-origin instead of per-database, as they were prior to http://trac.webkit.org/projects/webkit/changeset/29983.<br>
-The test clears all databases, sets the quota for the origin to 32k, then tries to insert 17k of data into two databases. If things go as planned, the UI Delegate will be informed of the exceeded quota and should increase the quota for this origin. Inserting 17k of data the third time should succeed again.
+The test clears all databases, sets the quota for the origin to 40k, then tries to insert 17k of data into two databases. If things go as planned, the second insert should fail, the UI Delegate should be informed of the exceeded quota and should increase the quota for this origin. Inserting 17k of data the third time should succeed again.
<pre id="console">
</pre>
</body>
diff --git a/LayoutTests/storage/read-and-write-transactions-dont-run-together.html b/LayoutTests/storage/read-and-write-transactions-dont-run-together.html
index a7565b5..11da356 100644
--- a/LayoutTests/storage/read-and-write-transactions-dont-run-together.html
+++ b/LayoutTests/storage/read-and-write-transactions-dont-run-together.html
@@ -1,103 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-function terminateTest()
-{
- if (window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function openTestDatabase()
-{
- return openDatabase("ReadAndWriteTransactionsDontRunTogetherTest",
- "1.0",
- "Test to make sure that read and write transactions on different DB handles to the same DB don't run at the same time.",
- 32768);
-}
-
-var readTransactionsInProgress = 0;
-var writeTransactionsInProgress = 0;
-var totalTransactions = 0;
-var finishedTransactions = 0;
-
-function runTransaction(db, readOnly)
-{
- var transactionFunction = (readOnly ? db.readTransaction : db.transaction);
- transactionFunction.call(db, function(tx) {
- if (readOnly) {
- if (writeTransactionsInProgress != 0) {
- log("Read transaction starting while write transaction in progress.");
- terminateTest();
- }
- readTransactionsInProgress++;
- } else {
- if ((readTransactionsInProgress != 0) || (writeTransactionsInProgress != 0)) {
- log("Write transaction starting while another transaction in progress.");
- terminateTest();
- }
- writeTransactionsInProgress++;
- }
- tx.executeSql("SELECT * FROM Test;");
- }, function(error) {
- log((readOnly ? "Read" : "Write") + " transaction failed: " + error.message);
- terminateTest();
- }, function() {
- finishedTransactions++;
- if (readOnly)
- readTransactionsInProgress--;
- else
- writeTransactionsInProgress--;
- log("Transaction successful.");
- if ((finishedTransactions == totalTransactions) && (readTransactionsInProgress == 0) && (writeTransactionsInProgress == 0))
- terminateTest();
- });
-}
-
-function runReadAndWriteTransactions(db1, db2, db3)
-{
- totalTransactions = 10;
- finishedTransactions = 0;
- runTransaction(db1, true);
- runTransaction(db2, true);
- runTransaction(db1, false);
- runTransaction(db1, true);
- runTransaction(db2, true);
- runTransaction(db3, true);
- runTransaction(db1, false);
- runTransaction(db2, false);
- runTransaction(db1, true);
- runTransaction(db3, true);
-}
-
-function runTest() {
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- try {
- var db1 = openTestDatabase();
- var db2 = openTestDatabase();
- var db3 = openTestDatabase();
- db1.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo int);");
- }, function(error) {
- log("Cannot create the Test table: " + error.message);
- terminateTest();
- }, function() {
- runReadAndWriteTransactions(db1, db2, db3);
- });
- } catch(err) {}
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="read-and-write-transactions-dont-run-together.js"></script>
</head>
-<body onload="runTest();">
+<body onload="setupAndRunTest();">
This test tests that read and write transactions on different handles to the same database don't run together.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/read-and-write-transactions-dont-run-together.js b/LayoutTests/storage/read-and-write-transactions-dont-run-together.js
new file mode 100644
index 0000000..5b817ba
--- /dev/null
+++ b/LayoutTests/storage/read-and-write-transactions-dont-run-together.js
@@ -0,0 +1,81 @@
+function terminateTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function openTestDatabase()
+{
+ return openDatabaseWithSuffix("ReadAndWriteTransactionsDontRunTogetherTest",
+ "1.0",
+ "Test to make sure that read and write transactions on different DB handles to the same DB don't run at the same time.",
+ 32768);
+}
+
+var readTransactionsInProgress = 0;
+var writeTransactionsInProgress = 0;
+var totalTransactions = 0;
+var finishedTransactions = 0;
+
+function runTransaction(db, readOnly)
+{
+ var transactionFunction = (readOnly ? db.readTransaction : db.transaction);
+ transactionFunction.call(db, function(tx) {
+ if (readOnly) {
+ if (writeTransactionsInProgress != 0) {
+ log("Read transaction starting while write transaction in progress.");
+ terminateTest();
+ }
+ readTransactionsInProgress++;
+ } else {
+ if ((readTransactionsInProgress != 0) || (writeTransactionsInProgress != 0)) {
+ log("Write transaction starting while another transaction in progress.");
+ terminateTest();
+ }
+ writeTransactionsInProgress++;
+ }
+ tx.executeSql("SELECT * FROM Test;");
+ }, function(error) {
+ log((readOnly ? "Read" : "Write") + " transaction failed: " + error.message);
+ terminateTest();
+ }, function() {
+ finishedTransactions++;
+ if (readOnly)
+ readTransactionsInProgress--;
+ else
+ writeTransactionsInProgress--;
+ log("Transaction successful.");
+ if ((finishedTransactions == totalTransactions) && (readTransactionsInProgress == 0) && (writeTransactionsInProgress == 0))
+ terminateTest();
+ });
+}
+
+function runReadAndWriteTransactions(db1, db2, db3)
+{
+ totalTransactions = 10;
+ finishedTransactions = 0;
+ runTransaction(db1, true);
+ runTransaction(db2, true);
+ runTransaction(db1, false);
+ runTransaction(db1, true);
+ runTransaction(db2, true);
+ runTransaction(db3, true);
+ runTransaction(db1, false);
+ runTransaction(db2, false);
+ runTransaction(db1, true);
+ runTransaction(db3, true);
+}
+
+function runTest() {
+ var db1 = openTestDatabase();
+ var db2 = openTestDatabase();
+ var db3 = openTestDatabase();
+ db1.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Test (Foo int);");
+ }, function(error) {
+ log("Cannot create the Test table: " + error.message);
+ terminateTest();
+ }, function() {
+ runReadAndWriteTransactions(db1, db2, db3);
+ });
+}
diff --git a/LayoutTests/storage/resources/database-common.js b/LayoutTests/storage/resources/database-common.js
new file mode 100644
index 0000000..d1eb410
--- /dev/null
+++ b/LayoutTests/storage/resources/database-common.js
@@ -0,0 +1,29 @@
+var DB_TEST_SUFFIX = "_dom";
+
+function openDatabaseWithSuffix(name, version, description, size, callback)
+{
+ if (arguments.length > 4) {
+ return openDatabase(name + DB_TEST_SUFFIX, version, description, size, callback);
+ } else {
+ return openDatabase(name + DB_TEST_SUFFIX, version, description, size);
+ }
+}
+
+function log(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+function setLocationHash(hash) {
+ location.hash = hash;
+}
+
+function setupAndRunTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+ document.getElementById("console").innerText = "";
+ runTest();
+}
diff --git a/LayoutTests/storage/resources/database-lock-after-reload-2.html b/LayoutTests/storage/resources/database-lock-after-reload-2.html
index d73a0df..6e0f09c 100644
--- a/LayoutTests/storage/resources/database-lock-after-reload-2.html
+++ b/LayoutTests/storage/resources/database-lock-after-reload-2.html
@@ -1,8 +1,6 @@
<html>
<head>
<script>
-var database;
-
function log(message)
{
document.getElementById("console").innerHTML += message + "<br>";
@@ -15,7 +13,7 @@ function finishTest()
layoutTestController.notifyDone();
}
-function errorFunction(tx, error)
+function errorFunction(error)
{
log("Test failed - " + error.message);
finishTest();
@@ -23,13 +21,11 @@ function errorFunction(tx, error)
function addData(db)
{
- db.transaction(function(tx) {
+ db.transaction(function(tx) {
log("Inserting some data");
-
- tx.executeSql("INSERT INTO DataTest (testData) VALUES (?)", ["A"], function(tx, result) { }, errorFunction);
- }, function(){}, function() {
- finishTest();
- });
+ tx.executeSql("INSERT INTO DataTest (testData) VALUES (?)", ["A"],
+ function(tx, result) { }, function(tx, error) { errorFunction(error); });
+ }, function() { }, function() { finishTest(); });
}
function runTest()
@@ -38,9 +34,9 @@ function runTest()
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
-
+
try {
- database = openDatabase("DatabaseLockTest", "1.0", "Test for database locking", 5242880);
+ var database = openDatabase("DatabaseLockTest", "1.0", "Test for database locking", 5242880);
addData(database);
} catch(e) {
log("Error - could not open database");
diff --git a/LayoutTests/storage/sql-error-codes-expected.txt b/LayoutTests/storage/sql-error-codes-expected.txt
new file mode 100644
index 0000000..1a9caa7
--- /dev/null
+++ b/LayoutTests/storage/sql-error-codes-expected.txt
@@ -0,0 +1,11 @@
+CONSOLE MESSAGE: line 0: Exception thrown in transaction callback.
+CONSOLE MESSAGE: line 0: Cannot call toString() on this object.
+This test tests the error codes reported in exceptional situations.
+PASS: expected and got error code UNKNOWN_ERR
+PASS: expected and got error code UNKNOWN_ERR
+PASS: expected and got error code SYNTAX_ERR
+PASS: expected and got error code SYNTAX_ERR
+PASS: expected and got error code UNKNOWN_ERR
+PASS: expected and got error code QUOTA_ERR
+PASS: expected and got error code VERSION_ERR
+
diff --git a/LayoutTests/storage/sql-error-codes.html b/LayoutTests/storage/sql-error-codes.html
new file mode 100644
index 0000000..dc70c6c
--- /dev/null
+++ b/LayoutTests/storage/sql-error-codes.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script src="resources/database-common.js"></script>
+<script src="sql-error-codes.js"></script>
+</head>
+<body onload="setupAndRunTest();">
+This test tests the error codes reported in exceptional situations.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/storage/sql-error-codes.js b/LayoutTests/storage/sql-error-codes.js
new file mode 100644
index 0000000..2d2c97c
--- /dev/null
+++ b/LayoutTests/storage/sql-error-codes.js
@@ -0,0 +1,114 @@
+function finishTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+var TOTAL_TESTS = 7;
+var testsRun = 0;
+function transactionErrorCallback(error, expectedErrorCodeName)
+{
+ if (error.code == error[expectedErrorCodeName]) {
+ log("PASS: expected and got error code " + expectedErrorCodeName);
+ if (++testsRun == TOTAL_TESTS)
+ finishTest();
+ } else {
+ log("FAIL: expected error code " + expectedErrorCodeName + " (" + error[expectedErrorCodeName] + "); got " + error.code);
+ finishTest();
+ }
+}
+
+function transactionSuccessCallback()
+{
+ log("FAIL: a transaction has completed successfully.");
+ finishTest();
+}
+
+function testTransaction(db, transactionCallback, expectedErrorCodeName)
+{
+ db.transaction(transactionCallback,
+ function(error) {
+ transactionErrorCallback(error, expectedErrorCodeName);
+ }, transactionSuccessCallback);
+}
+
+function testTransactionThrowsException(db)
+{
+ testTransaction(db, function(tx) { throw "Exception thrown in transaction callback."; }, "UNKNOWN_ERR");
+}
+
+function testTransactionFailureBecauseOfStatementFailure(db)
+{
+ testTransaction(db,
+ function(tx) {
+ tx.executeSql("BAD STATEMENT", [], null, function(tx, error) { return true; });
+ }, "UNKNOWN_ERR");
+}
+
+function testInvalidStatement(db)
+{
+ testTransaction(db, function(tx) { tx.executeSql("BAD STATEMENT"); }, "SYNTAX_ERR");
+}
+
+function testIncorrectNumberOfBindParameters(db)
+{
+ testTransaction(db,
+ function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS BadBindNumberTest (Foo INT, Bar INT)");
+ tx.executeSql("INSERT INTO BadBindNumberTest VALUES (?, ?)", [1]);
+ }, "SYNTAX_ERR");
+}
+
+function testBindParameterOfWrongType(db)
+{
+ var badString = { };
+ badString.toString = function() { throw "Cannot call toString() on this object." };
+
+ testTransaction(db, function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS BadBindTypeTest (Foo TEXT)");
+ tx.executeSql("INSERT INTO BadBindTypeTest VALUES (?)", [badString]);
+ }, "UNKNOWN_ERR");
+}
+
+function testQuotaExceeded(db)
+{
+ testTransaction(db,
+ function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS QuotaTest (Foo BLOB)");
+ tx.executeSql("INSERT INTO QuotaTest VALUES (ZEROBLOB(10 * 1024 * 1024))");
+ }, "QUOTA_ERR");
+}
+
+function testVersionMismatch(db)
+{
+ // Use another DB handle to change the version. However, in order to make sure that the DB version is not
+ // changed before the transactions in the other tests have run, we need to do it in a transaction on 'db'.
+ db.transaction(function(tx) {
+ var db2 = openDatabaseWithSuffix("SQLErrorCodesTest", "1.0", "Tests the error codes.", 1);
+ db2.changeVersion("1.0", "2.0", function(tx) { },
+ function(error) {
+ log("FAIL: could not change the DB version.");
+ finishTest();
+ }, function() { });
+ });
+
+ testTransaction(db,
+ function(tx) {
+ tx.executeSql("THIS STATEMENT SHOULD NEVER GET EXECUTED");
+ }, "VERSION_ERR");
+}
+
+function runTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.clearAllDatabases();
+
+ var db = openDatabaseWithSuffix("SQLErrorCodesTest", "1.0", "Tests the error codes.", 1);
+ testTransactionThrowsException(db);
+ testTransactionFailureBecauseOfStatementFailure(db);
+ testInvalidStatement(db);
+ testIncorrectNumberOfBindParameters(db);
+ testBindParameterOfWrongType(db);
+ testQuotaExceeded(db);
+ testVersionMismatch(db);
+}
diff --git a/LayoutTests/storage/statement-error-callback-expected.txt b/LayoutTests/storage/statement-error-callback-expected.txt
index ca117a8..6b65683 100644
--- a/LayoutTests/storage/statement-error-callback-expected.txt
+++ b/LayoutTests/storage/statement-error-callback-expected.txt
@@ -1,6 +1,14 @@
-CONSOLE MESSAGE: line 0: Exception in Statement error callback
-This test confirms that if the statement error callback returns true or throws an exception we do not execute any further statements in that transaction and instead execute the transaction error callback immediately.
+CONSOLE MESSAGE: line 0: Exception in statement error callback
+This test confirms that a transaction is immediately rolled back if and only if a statement's error callback throws an exception, returns true, or doesn't return any value.
PASS - the transaction error callback was invoked.
PASS - the transaction error callback was invoked.
+PASS - the transaction error callback was invoked.
+PASS - the transaction error callback was invoked.
+PASS - the transaction error callback was invoked.
+PASS - the transaction error callback was invoked.
+PASS - the transaction success callback was invoked.
+PASS - the transaction success callback was invoked.
+PASS - the transaction success callback was invoked.
+PASS - the transaction success callback was invoked.
Test Complete
diff --git a/LayoutTests/storage/statement-error-callback-isolated-world-expected.txt b/LayoutTests/storage/statement-error-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..b376896
--- /dev/null
+++ b/LayoutTests/storage/statement-error-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the statement error callback is called in the right world.
+
diff --git a/LayoutTests/storage/statement-error-callback-isolated-world.html b/LayoutTests/storage/statement-error-callback-isolated-world.html
new file mode 100644
index 0000000..196e42b
--- /dev/null
+++ b/LayoutTests/storage/statement-error-callback-isolated-world.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the statement error callback is called in the right world.
+<div id="console"></div>
+<script>
+var statementErrorCallbacksInvoked = 0;
+function done()
+{
+ if ((++statementErrorCallbacksInvoked == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function statementErrorCallback1(tx, error)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function statementErrorCallback2(tx, error)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function statementErrorCallback1(tx, error)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('StatementErrorCallbackIsolatedWorld1', '1.0', '', 1);\n" +
+ "db1.transaction(function(tx) {\n" +
+ " tx.executeSql('BAD STATEMENT', [], function(tx, data) {}, statementErrorCallback1);\n" +
+ "});");
+
+ var db2 = openDatabase('StatementErrorCallbackIsolatedWorld2', '1.0', '', 1);
+ db2.transaction(function(tx) {
+ tx.executeSql('BAD STATEMENT', [], function(tx, data) {}, statementErrorCallback2);
+ });
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/statement-error-callback.html b/LayoutTests/storage/statement-error-callback.html
index 3675548..6db836d 100644
--- a/LayoutTests/storage/statement-error-callback.html
+++ b/LayoutTests/storage/statement-error-callback.html
@@ -15,20 +15,42 @@ function finishTest()
}
var txCallbackCount = 0;
-var NUMBER_OF_TRANSACTIONS = 2;
+var NUMBER_OF_TRANSACTIONS = 10;
+var database;
-function transactionErrorFunction(error)
+function runTransaction(expectedToFail, statementErrorCallback)
{
- log("PASS - the transaction error callback was invoked.");
- if (++txCallbackCount == NUMBER_OF_TRANSACTIONS)
- finishTest();
-}
-
-function transactionSuccessFunction(message)
-{
- log("FAIL - the transaction success callback should not be invoked.");
- if (++txCallbackCount == NUMBER_OF_TRANSACTIONS)
- finishTest();
+ database.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS TestTable (RandomData TEXT)");
+ tx.executeSql("INSERT INTO TestTable VALUES (?)", ['test']);
+ tx.executeSql("THIS STATEMENT WILL FAIL", [],
+ function(tx, data) {
+ log("FAIL - this statement should have failed");
+ finishTest();
+ }, statementErrorCallback);
+ tx.executeSql("INSERT INTO TestTable VALUES (?)", ['test1'],
+ function(error) {
+ if (expectedToFail)
+ log("FAIL - This statement should not have been executed");
+ }, function() {
+ if (expectedToFail)
+ log("FAIL - This statement should not have been executed");
+ });
+ }, function(error) {
+ if (expectedToFail)
+ log("PASS - the transaction error callback was invoked.");
+ else
+ log("FAIL - the transaction error callback should not have been invoked.");
+ if (++txCallbackCount == NUMBER_OF_TRANSACTIONS)
+ finishTest();
+ }, function() {
+ if (expectedToFail)
+ log("FAIL - the transaction success callback should not have been invoked.");
+ else
+ log("PASS - the transaction success callback was invoked.");
+ if (++txCallbackCount == NUMBER_OF_TRANSACTIONS)
+ finishTest();
+ });
}
function runTest()
@@ -38,29 +60,26 @@ function runTest()
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
-
- var database = openDatabase("bug-28872", "1.0", "statement error callback test", 1024);
-
- database.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS StatementErrorCallbackTest (randomData)");
- tx.executeSql("INSERT INTO StatementErrorCallbackTest (randomData) VALUES (?)", ['test']);
- tx.executeSql("THIS STATEMENT WILL FAIL", [], function(message) { log("FAIL - this statement should have failed"); finishTest(); }, function(error) { return true; });
- tx.executeSql("INSERT INTO StatementErrorCallbackTest (randomData) VALUES (?)", ['test1'], function(message) { log("FAIL - This statement should not have been executed"); }, function(message) { log("FAIL - This statement should not have been executed"); });
- }, transactionErrorFunction, transactionSuccessFunction);
- database.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS StatementErrorCallbackTest (randomData)");
- tx.executeSql("INSERT INTO StatementErrorCallbackTest (randomData) VALUES (?)", ['test']);
- tx.executeSql("THIS STATEMENT WILL FAIL", [], function(message) { log("FAIL - this statement should have failed"); finishTest(); }, function(error) { throw "Exception in Statement error callback"; return false; });
- tx.executeSql("INSERT INTO StatementErrorCallbackTest (randomData) VALUES (?)", ['test1'], function(message) { log("FAIL - This statement should not have been executed"); }, function(message) { log("FAIL - This statement should not have been executed"); });
- }, transactionErrorFunction, transactionSuccessFunction);
+ database = openDatabase("StatementErrorCallbackTest", "1.0", "statement error callback test", 1024);
+
+ runTransaction(true, function(error) { return true; });
+ runTransaction(true, function(error) { throw "Exception in statement error callback"; return false; });
+ runTransaction(true, function(error) { return "some string"; });
+ runTransaction(true, function(error) { return 1234; });
+ runTransaction(true, function(error) { return {a: 2, b: "abc"}; });
+ runTransaction(true, function(error) { return "false"; });
+ runTransaction(false, function(error) {});
+ runTransaction(false, function(error) { return false; });
+ runTransaction(false, function(error) { return 0; });
+ runTransaction(false, function(error) { return null; });
}
</script>
</head>
<body onload="runTest()">
-This test confirms that if the statement error callback returns true or throws an exception we do not execute any further statements in that transaction and instead execute the transaction error callback immediately.
+This test confirms that a transaction is immediately rolled back if and only if a statement's error callback throws an exception, returns true, or doesn't return any value.
<pre id="console">
</pre>
</body>
diff --git a/LayoutTests/storage/statement-success-callback-isolated-world-expected.txt b/LayoutTests/storage/statement-success-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..2ef027d
--- /dev/null
+++ b/LayoutTests/storage/statement-success-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the statement success callback is called in the right world.
+
diff --git a/LayoutTests/storage/statement-success-callback-isolated-world.html b/LayoutTests/storage/statement-success-callback-isolated-world.html
new file mode 100644
index 0000000..4fac754
--- /dev/null
+++ b/LayoutTests/storage/statement-success-callback-isolated-world.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the statement success callback is called in the right world.
+<div id="console"></div>
+<script>
+var statementSuccessCallbacksInvoked = 0;
+function done()
+{
+ if ((++statementSuccessCallbacksInvoked == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function statementSuccessCallback1(tx, data)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function statementSuccessCallback2(tx, data)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function statementSuccessCallback1(tx, data)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('StatementSuccessCallbackIsolatedWorld1', '1.0', '', 1);\n" +
+ "db1.transaction(function(tx) {\n" +
+ " tx.executeSql('CREATE TABLE IF NOT EXISTS Test (Foo INT)', [], statementSuccessCallback1);\n" +
+ "});");
+
+ var db2 = openDatabase('StatementSuccessCallbackIsolatedWorld2', '1.0', '', 1);
+ db2.transaction(function(tx) {
+ tx.executeSql('CREATE TABLE IF NOT EXISTS Test (Foo INT)', [], statementSuccessCallback2);
+ });
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/test-authorizer.html b/LayoutTests/storage/test-authorizer.html
index 57785a6..8baf10a 100644
--- a/LayoutTests/storage/test-authorizer.html
+++ b/LayoutTests/storage/test-authorizer.html
@@ -1,175 +1,12 @@
<html>
<head>
-<script>
-
-function log(message)
-{
- document.body.innerHTML += message + "<br>";
-}
-
-function terminateTest()
-{
- if (window.layoutTestController)
- layoutTestController.notifyDone();
-}
-
-function logAndTerminateTest(message, error)
-{
- log(message + ": " + error.message);
- terminateTest();
-}
-
-function cleanup(db)
-{
- db.transaction(function(tx) {
- tx.executeSql("DROP TABLE IF EXISTS Test;");
- tx.executeSql("DROP INDEX IF EXISTS TestIndex;");
- tx.executeSql("DROP VIEW IF EXISTS TestView;");
- tx.executeSql("DROP TRIGGER IF EXISTS TestTrigger;");
- }, function(error) { logAndTerminateTest("Cleanup failed", error); });
-}
-
-function statementSuccessCallback(statementType)
-{
- log(statementType + " statement succeeded.");
-}
-
-function statementErrorCallback(statementType, error)
-{
- log(statementType + " statement failed: " + error.message);
- return false;
-}
-
-function executeStatement(tx, statement, operation)
-{
- tx.executeSql(statement, [],
- function(result) { statementSuccessCallback(operation); },
- function(tx, error) { return statementErrorCallback(operation, error); });
-}
-
-function createTableCallback(tx)
-{
- executeStatement(tx, "CREATE TABLE Test (Foo int);", "SQLITE_CREATE_TABLE");
-}
-
-function createStatementsCallback(tx)
-{
- executeStatement(tx, "CREATE INDEX TestIndex ON Test (Foo);", "SQLITE_CREATE_INDEX");
-
- // Even though the following query should trigger a SQLITE_CREATE_TEMP_INDEX operation
- // (according to http://www.sqlite.org/tempfiles.html), it doesn't, and I'm not aware
- // of any other way to trigger this operation. So we'll skip it for now.
- //executeStatement(tx, "SELECT * FROM Test WHERE Foo IN (1, 2, 3);", "SQLITE_CREATE_TEMP_INDEX");
-
- executeStatement(tx, "CREATE TEMP TABLE TestTempTable (Foo int);", "SQLITE_CREATE_TEMP_TABLE");
- executeStatement(tx, "CREATE TEMP TRIGGER TestTempTrigger INSERT ON Test BEGIN SELECT COUNT(*) FROM Test; END;", "SQLITE_CREATE_TEMP_TRIGGER");
- executeStatement(tx, "CREATE TEMP VIEW TestTempView AS SELECT COUNT(*) FROM Test;", "SQLITE_CREATE_TEMP_VIEW");
- executeStatement(tx, "CREATE TRIGGER TestTrigger INSERT ON Test BEGIN SELECT COUNT(*) FROM Test; END;", "SQLITE_CREATE_TRIGGER");
- executeStatement(tx, "CREATE VIEW TestView AS SELECT COUNT(*) FROM Test;", "SQLITE_CREATE_VIEW");
- executeStatement(tx, "CREATE VIRTUAL TABLE TestVirtualTable USING MissingModule;", "SQLITE_CREATE_VTABLE");
-}
-
-function otherStatementsCallback(tx)
-{
- executeStatement(tx, "SELECT COUNT(*) FROM Test;", "SQLITE_READ");
- executeStatement(tx, "SELECT COUNT(*) FROM Test;", "SQLITE_SELECT");
- executeStatement(tx, "DELETE FROM Test;", "SQLITE_DELETE");
- executeStatement(tx, "INSERT INTO Test VALUES (1);", "SQLITE_INSERT");
- executeStatement(tx, "UPDATE Test SET Foo = 2 WHERE Foo = 1;", "SQLITE_UPDATE");
- executeStatement(tx, "PRAGMA cache_size;", "SQLITE_PRAGMA");
-
- executeStatement(tx, "ALTER TABLE Test RENAME TO TestTable;", "SQLITE_ALTER_TABLE");
- // Rename the table back to its original name
- executeStatement(tx, "ALTER TABLE TestTable RENAME To Test;", "SQLITE_ALTER_TABLE");
-
- executeStatement(tx, "BEGIN TRANSACTION;", "SQLITE_TRANSACTION");
- executeStatement(tx, "ATTACH main AS TestMain;", "SQLITE_ATTACH");
- executeStatement(tx, "DETACH TestMain;", "SQLITE_DETACH");
- executeStatement(tx, "REINDEX;", "SQLITE_REINDEX");
- executeStatement(tx, "ANALYZE;", "SQLITE_ANALYZE");
-
- // SQLITE_FUNCTION: allowed write mode
- // There is no SQL/Javascript API to add user-defined functions to SQLite,
- // so we cannot test this operation
-}
-
-function dropStatementsCallback(tx)
-{
- executeStatement(tx, "DROP INDEX TestIndex;", "SQLITE_DROP_INDEX");
-
- // SQLITE_DROP_TEMP_INDEX: allowed in write mode
- // Not sure how to test this: temp indexes are automatically dropped when
- // the database is closed, but HTML5 doesn't specify a closeDatabase() call.
-
- executeStatement(tx, "DROP TABLE TestTempTable;", "SQLITE_DROP_TEMP_TABLE");
- executeStatement(tx, "DROP TRIGGER TestTempTrigger;", "SQLITE_DROP_TEMP_TRIGGER");
- executeStatement(tx, "DROP VIEW TestTempView;", "SQLITE_DROP_TEMP_VIEW");
- executeStatement(tx, "DROP TRIGGER TestTrigger;", "SQLITE_DROP_TRIGGER");
- executeStatement(tx, "DROP VIEW TestView;", "SQLITE_DROP_VIEW");
-
- // SQLITE_DROP_VTABLE: allowed in write mode
- // Not sure how to test this: we cannot create a virtual table because we do not
- // have SQL/Javascript APIs to register a module that implements a virtual table.
- // Therefore, trying to drop a virtual table will always fail (no such table)
- // before even getting to the authorizer.
-
- executeStatement(tx, "DROP TABLE Test;", "SQLITE_DROP_TABLE");
-}
-
-function testReadWriteMode(db)
-{
- db.transaction(function(tx) {
- createTableCallback(tx);
- createStatementsCallback(tx);
- otherStatementsCallback(tx);
- dropStatementsCallback(tx);
- },
- function(error) { logAndTerminateTest("Write transaction failed", error); },
- function() { log("Write transaction succeeded."); });
-}
-
-function testReadOnlyMode(db)
-{
- // Test the 'CREATE TABLE' operation; it should be disallowed
- db.readTransaction(createTableCallback,
- function(error) { logAndTerminateTest("Read 'CREATE TABLE' transaction failed", error); });
-
- // In order to test all other 'CREATE' operations, we must create the table first
- db.transaction(createTableCallback,
- function(error) { logAndTerminateTest("Write 'CREATE TABLE' transaction failed", error); });
- db.readTransaction(createStatementsCallback,
- function(error) { logAndTerminateTest("Read 'CREATE' transaction failed", error); });
-
- // In order to test the 'DROP' and 'other' operations, we need to first create the respective entities
- db.transaction(createStatementsCallback,
- function(error) { logAndTerminateTest("Write 'CREATE' transaction failed", error); });
- db.readTransaction(otherStatementsCallback,
- function(error) { logAndTerminateTest("Read 'other' transaction failed", error); });
-
- // Hack: insert an empty write transaction to guaratee that these transactions are executed sequentially
- db.transaction(function(tx) { });
- db.readTransaction(dropStatementsCallback,
- function(error) { logAndTerminateTest("Read 'DROP' transaction failed", error); },
- function() { log("Read transactions succeeded."); terminateTest(); });
-}
-
-function runTest()
-{
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
- layoutTestController.waitUntilDone();
- }
-
- try {
- var db = openDatabase("AuthorizerTest", "1.0", "Tests the database authorizer.", 32768);
- cleanup(db);
- testReadWriteMode(db);
- testReadOnlyMode(db);
- } catch(err) {}
-}
-</script>
+<script src="resources/database-common.js"></script>
+<script src="test-authorizer.js"></script>
</head>
-<body onload="runTest();">
+<body onload="setupAndRunTest();">
This test tests the database authorizer.<br>
+<pre id="console">
+FAILURE: test didn't run.
+</pre>
</body>
</html>
diff --git a/LayoutTests/storage/test-authorizer.js b/LayoutTests/storage/test-authorizer.js
new file mode 100644
index 0000000..4a08c89
--- /dev/null
+++ b/LayoutTests/storage/test-authorizer.js
@@ -0,0 +1,153 @@
+function terminateTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function logAndTerminateTest(message, error)
+{
+ log(message + ": " + error.message);
+ terminateTest();
+}
+
+function cleanup(db)
+{
+ db.transaction(function(tx) {
+ tx.executeSql("DROP TABLE IF EXISTS Test;");
+ tx.executeSql("DROP INDEX IF EXISTS TestIndex;");
+ tx.executeSql("DROP VIEW IF EXISTS TestView;");
+ tx.executeSql("DROP TRIGGER IF EXISTS TestTrigger;");
+ }, function(error) { logAndTerminateTest("Cleanup failed", error); });
+}
+
+function statementSuccessCallback(statementType)
+{
+ log(statementType + " statement succeeded.");
+}
+
+function statementErrorCallback(statementType, error)
+{
+ log(statementType + " statement failed: " + error.message);
+ return false;
+}
+
+function executeStatement(tx, statement, operation)
+{
+ tx.executeSql(statement, [],
+ function(result) { statementSuccessCallback(operation); },
+ function(tx, error) { return statementErrorCallback(operation, error); });
+}
+
+function createTableCallback(tx)
+{
+ executeStatement(tx, "CREATE TABLE Test (Foo int);", "SQLITE_CREATE_TABLE");
+}
+
+function createStatementsCallback(tx)
+{
+ executeStatement(tx, "CREATE INDEX TestIndex ON Test (Foo);", "SQLITE_CREATE_INDEX");
+
+ // Even though the following query should trigger a SQLITE_CREATE_TEMP_INDEX operation
+ // (according to http://www.sqlite.org/tempfiles.html), it doesn't, and I'm not aware
+ // of any other way to trigger this operation. So we'll skip it for now.
+ //executeStatement(tx, "SELECT * FROM Test WHERE Foo IN (1, 2, 3);", "SQLITE_CREATE_TEMP_INDEX");
+
+ executeStatement(tx, "CREATE TEMP TABLE TestTempTable (Foo int);", "SQLITE_CREATE_TEMP_TABLE");
+ executeStatement(tx, "CREATE TEMP TRIGGER TestTempTrigger INSERT ON Test BEGIN SELECT COUNT(*) FROM Test; END;", "SQLITE_CREATE_TEMP_TRIGGER");
+ executeStatement(tx, "CREATE TEMP VIEW TestTempView AS SELECT COUNT(*) FROM Test;", "SQLITE_CREATE_TEMP_VIEW");
+ executeStatement(tx, "CREATE TRIGGER TestTrigger INSERT ON Test BEGIN SELECT COUNT(*) FROM Test; END;", "SQLITE_CREATE_TRIGGER");
+ executeStatement(tx, "CREATE VIEW TestView AS SELECT COUNT(*) FROM Test;", "SQLITE_CREATE_VIEW");
+ executeStatement(tx, "CREATE VIRTUAL TABLE TestVirtualTable USING MissingModule;", "SQLITE_CREATE_VTABLE");
+}
+
+function otherStatementsCallback(tx)
+{
+ executeStatement(tx, "SELECT COUNT(*) FROM Test;", "SQLITE_READ");
+ executeStatement(tx, "SELECT COUNT(*) FROM Test;", "SQLITE_SELECT");
+ executeStatement(tx, "DELETE FROM Test;", "SQLITE_DELETE");
+ executeStatement(tx, "INSERT INTO Test VALUES (1);", "SQLITE_INSERT");
+ executeStatement(tx, "UPDATE Test SET Foo = 2 WHERE Foo = 1;", "SQLITE_UPDATE");
+ executeStatement(tx, "PRAGMA cache_size;", "SQLITE_PRAGMA");
+
+ executeStatement(tx, "ALTER TABLE Test RENAME TO TestTable;", "SQLITE_ALTER_TABLE");
+ // Rename the table back to its original name
+ executeStatement(tx, "ALTER TABLE TestTable RENAME To Test;", "SQLITE_ALTER_TABLE");
+
+ executeStatement(tx, "BEGIN TRANSACTION;", "SQLITE_TRANSACTION");
+ executeStatement(tx, "ATTACH main AS TestMain;", "SQLITE_ATTACH");
+ executeStatement(tx, "DETACH TestMain;", "SQLITE_DETACH");
+ executeStatement(tx, "REINDEX;", "SQLITE_REINDEX");
+ executeStatement(tx, "ANALYZE;", "SQLITE_ANALYZE");
+
+ // SQLITE_FUNCTION: allowed write mode
+ // There is no SQL/Javascript API to add user-defined functions to SQLite,
+ // so we cannot test this operation
+}
+
+function dropStatementsCallback(tx)
+{
+ executeStatement(tx, "DROP INDEX TestIndex;", "SQLITE_DROP_INDEX");
+
+ // SQLITE_DROP_TEMP_INDEX: allowed in write mode
+ // Not sure how to test this: temp indexes are automatically dropped when
+ // the database is closed, but HTML5 doesn't specify a closeDatabase() call.
+
+ executeStatement(tx, "DROP TABLE TestTempTable;", "SQLITE_DROP_TEMP_TABLE");
+ executeStatement(tx, "DROP TRIGGER TestTempTrigger;", "SQLITE_DROP_TEMP_TRIGGER");
+ executeStatement(tx, "DROP VIEW TestTempView;", "SQLITE_DROP_TEMP_VIEW");
+ executeStatement(tx, "DROP TRIGGER TestTrigger;", "SQLITE_DROP_TRIGGER");
+ executeStatement(tx, "DROP VIEW TestView;", "SQLITE_DROP_VIEW");
+
+ // SQLITE_DROP_VTABLE: allowed in write mode
+ // Not sure how to test this: we cannot create a virtual table because we do not
+ // have SQL/Javascript APIs to register a module that implements a virtual table.
+ // Therefore, trying to drop a virtual table will always fail (no such table)
+ // before even getting to the authorizer.
+
+ executeStatement(tx, "DROP TABLE Test;", "SQLITE_DROP_TABLE");
+}
+
+function testReadWriteMode(db)
+{
+ db.transaction(function(tx) {
+ createTableCallback(tx);
+ createStatementsCallback(tx);
+ otherStatementsCallback(tx);
+ dropStatementsCallback(tx);
+ },
+ function(error) { logAndTerminateTest("Write transaction failed", error); },
+ function() { log("Write transaction succeeded."); });
+}
+
+function testReadOnlyMode(db)
+{
+ // Test the 'CREATE TABLE' operation; it should be disallowed
+ db.readTransaction(createTableCallback,
+ function(error) { logAndTerminateTest("Read 'CREATE TABLE' transaction failed", error); });
+
+ // In order to test all other 'CREATE' operations, we must create the table first
+ db.transaction(createTableCallback,
+ function(error) { logAndTerminateTest("Write 'CREATE TABLE' transaction failed", error); });
+ db.readTransaction(createStatementsCallback,
+ function(error) { logAndTerminateTest("Read 'CREATE' transaction failed", error); });
+
+ // In order to test the 'DROP' and 'other' operations, we need to first create the respective entities
+ db.transaction(createStatementsCallback,
+ function(error) { logAndTerminateTest("Write 'CREATE' transaction failed", error); });
+ db.readTransaction(otherStatementsCallback,
+ function(error) { logAndTerminateTest("Read 'other' transaction failed", error); });
+
+ // Hack: insert an empty write transaction to guaratee that these transactions are executed sequentially
+ db.transaction(function(tx) { });
+ db.readTransaction(dropStatementsCallback,
+ function(error) { logAndTerminateTest("Read 'DROP' transaction failed", error); },
+ function() { log("Read transactions succeeded."); terminateTest(); });
+}
+
+function runTest()
+{
+ var db = openDatabaseWithSuffix("AuthorizerTest", "1.0", "Tests the database authorizer.", 32768);
+ cleanup(db);
+ testReadWriteMode(db);
+ testReadOnlyMode(db);
+}
diff --git a/LayoutTests/storage/transaction-callback-isolated-world-expected.txt b/LayoutTests/storage/transaction-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..27f474f
--- /dev/null
+++ b/LayoutTests/storage/transaction-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the transaction callback is called in the right world.
+
diff --git a/LayoutTests/storage/transaction-callback-isolated-world.html b/LayoutTests/storage/transaction-callback-isolated-world.html
new file mode 100644
index 0000000..6825d70
--- /dev/null
+++ b/LayoutTests/storage/transaction-callback-isolated-world.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the transaction callback is called in the right world.
+<div id="console"></div>
+<script>
+var transactionCallbacksInvoked = 0;
+function done()
+{
+ if ((++transactionCallbacksInvoked == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function transactionCallback1(tx)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function transactionCallback2(tx)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function transactionCallback1(tx)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('TransactionCallbackIsolatedWorld1', '1.0', '', 1);\n" +
+ "db1.transaction(transactionCallback1);");
+
+ var db2 = openDatabase('TransactionCallbackIsolatedWorld2', '1.0', '', 1);
+ db2.transaction(transactionCallback2);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/transaction-error-callback-isolated-world-expected.txt b/LayoutTests/storage/transaction-error-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..da15396
--- /dev/null
+++ b/LayoutTests/storage/transaction-error-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the transaction error callback is called in the right world.
+
diff --git a/LayoutTests/storage/transaction-error-callback-isolated-world.html b/LayoutTests/storage/transaction-error-callback-isolated-world.html
new file mode 100644
index 0000000..521894d
--- /dev/null
+++ b/LayoutTests/storage/transaction-error-callback-isolated-world.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the transaction error callback is called in the right world.
+<div id="console"></div>
+<script>
+var transactionErrorCallbacksInvoked = 0;
+function done()
+{
+ if ((++transactionErrorCallbacksInvoked == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function transactionErrorCallback1(tx)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function transactionErrorCallback2(tx)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function transactionErrorCallback1(tx)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('TransactionErrorCallbackIsolatedWorld1', '1.0', '', 1);\n" +
+ "db1.transaction(function(tx) {\n" +
+ " tx.executeSql('BAD STATEMENT', [], function(tx, data) {}, function(tx, error) { return true; });\n" +
+ "}, transactionErrorCallback1);");
+
+ var db2 = openDatabase('TransactionErrorCallbackIsolatedWorld2', '1.0', '', 1);
+ db2.transaction(function(tx) {
+ tx.executeSql('BAD STATEMENT', [], function(tx, data) {}, function(tx, error) { return true; });
+ }, transactionErrorCallback2);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/storage/transaction-success-callback-isolated-world-expected.txt b/LayoutTests/storage/transaction-success-callback-isolated-world-expected.txt
new file mode 100644
index 0000000..e427f4a
--- /dev/null
+++ b/LayoutTests/storage/transaction-success-callback-isolated-world-expected.txt
@@ -0,0 +1,4 @@
+ALERT: undefined
+ALERT: PASS: document.body.bar visible in a callback created in this world.
+This test tests that the transaction success callback is called in the right world.
+
diff --git a/LayoutTests/storage/transaction-success-callback-isolated-world.html b/LayoutTests/storage/transaction-success-callback-isolated-world.html
new file mode 100644
index 0000000..ca2df94
--- /dev/null
+++ b/LayoutTests/storage/transaction-success-callback-isolated-world.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+This test tests that the transaction success callback is called in the right world.
+<div id="console"></div>
+<script>
+var transactionSuccessCallbacksInvoked = 0;
+function done()
+{
+ if ((++transactionSuccessCallbacksInvoked == 2) && (window.layoutTestController))
+ layoutTestController.notifyDone();
+}
+
+function transactionSuccessCallback1(tx)
+{
+ alert("FAIL: Visible in isolated world.");
+ done();
+}
+
+function transactionSuccessCallback2(tx)
+{
+ alert(document.body.bar);
+ done();
+}
+
+document.body.foo = "FAIL: document.body.foo visible in isolated world.";
+document.body.bar = "PASS: document.body.bar visible in a callback created in this world.";
+
+if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ layoutTestController.evaluateScriptInIsolatedWorld(
+ 0,
+ "function transactionSuccessCallback1(tx)\n" +
+ "{\n" +
+ " alert(document.body.foo);\n" +
+ " window.location='javascript:done()';\n" +
+ "}\n" +
+ "var db1 = openDatabase('TransactionSuccessCallbackIsolatedWorld1', '1.0', '', 1);\n" +
+ "db1.transaction(function(tx) { }, null, transactionSuccessCallback1);");
+
+ var db2 = openDatabase('TransactionSuccessCallbackIsolatedWorld2', '1.0', '', 1);
+ db2.transaction(function(tx) { }, null, transactionSuccessCallback2);
+}
+</script>
+</body>
+</html>