summaryrefslogtreecommitdiffstats
path: root/LayoutTests/storage
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2010-02-25 10:58:37 +0000
committerSteve Block <steveblock@google.com>2010-02-25 11:17:52 +0000
commitcfb0617749a64f2e177386b030d46007b8c4b179 (patch)
tree033b0b4e1b1eaa96831b52fc1ec6675132f33035 /LayoutTests/storage
parent4175d59b46f96005f0c64978b1a94e3fe60f1e8e (diff)
downloadexternal_webkit-cfb0617749a64f2e177386b030d46007b8c4b179.zip
external_webkit-cfb0617749a64f2e177386b030d46007b8c4b179.tar.gz
external_webkit-cfb0617749a64f2e177386b030d46007b8c4b179.tar.bz2
Adds layout tests for HTML5 features
The following layout tests should all pass on Android, as they are for recently implemented HTML5 features ... - fast/dom/Geolocation - storage - http/tests/appcache This change adds these tests to the Android tree, at the current WebKit revision r54731. This is so that we can easily keep track of which tests should always be green, and so that we can add Android-specific test results. We also add the following paths ... - fast/js/resources - used by the Geolocation tests - http/conf - used by the Appcache tests Tests that are currently failing are added to the DumpRenderTree skipped list temporarily, to keep all tests green. Change-Id: Id96c05e3746ed64e4e4c40c99567b8def688f90a
Diffstat (limited to 'LayoutTests/storage')
-rw-r--r--LayoutTests/storage/change-version-expected.txt4
-rw-r--r--LayoutTests/storage/change-version-handle-reuse-expected.txt6
-rw-r--r--LayoutTests/storage/change-version-handle-reuse.html71
-rw-r--r--LayoutTests/storage/change-version.html90
-rw-r--r--LayoutTests/storage/close-during-stress-test-expected.txt5
-rw-r--r--LayoutTests/storage/close-during-stress-test.html19
-rw-r--r--LayoutTests/storage/database-lock-after-reload-expected.txt3
-rw-r--r--LayoutTests/storage/database-lock-after-reload.html69
-rw-r--r--LayoutTests/storage/domstorage/clear-expected.txt35
-rw-r--r--LayoutTests/storage/domstorage/clear.html12
-rw-r--r--LayoutTests/storage/domstorage/complex-keys-expected.txt237
-rw-r--r--LayoutTests/storage/domstorage/complex-keys.html12
-rw-r--r--LayoutTests/storage/domstorage/complex-values-expected.txt207
-rw-r--r--LayoutTests/storage/domstorage/complex-values.html12
-rw-r--r--LayoutTests/storage/domstorage/events/basic-body-attribute-expected.txt102
-rw-r--r--LayoutTests/storage/domstorage/events/basic-body-attribute.html13
-rw-r--r--LayoutTests/storage/domstorage/events/basic-expected.txt98
-rw-r--r--LayoutTests/storage/domstorage/events/basic-setattribute-expected.txt102
-rw-r--r--LayoutTests/storage/domstorage/events/basic-setattribute.html13
-rw-r--r--LayoutTests/storage/domstorage/events/basic.html13
-rw-r--r--LayoutTests/storage/domstorage/events/case-sensitive-expected.txt38
-rw-r--r--LayoutTests/storage/domstorage/events/case-sensitive.html13
-rw-r--r--LayoutTests/storage/domstorage/events/documentURI-expected.txt40
-rw-r--r--LayoutTests/storage/domstorage/events/documentURI.html13
-rw-r--r--LayoutTests/storage/domstorage/events/resources/body-event-handler.html8
-rw-r--r--LayoutTests/storage/domstorage/events/resources/eventTestHarness.js51
-rw-r--r--LayoutTests/storage/domstorage/events/resources/setattribute-event-handler.html11
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/TEMPLATE.html13
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/basic-body-attribute.js98
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/basic-setattribute.js98
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/basic.js97
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/case-sensitive.js50
-rw-r--r--LayoutTests/storage/domstorage/events/script-tests/documentURI.js52
-rw-r--r--LayoutTests/storage/domstorage/localstorage/delete-removal-expected.txt13
-rw-r--r--LayoutTests/storage/domstorage/localstorage/delete-removal.html51
-rw-r--r--LayoutTests/storage/domstorage/localstorage/enumerate-storage-expected.txt9
-rw-r--r--LayoutTests/storage/domstorage/localstorage/enumerate-storage.html47
-rw-r--r--LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key-expected.txt8
-rw-r--r--LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key.html47
-rw-r--r--LayoutTests/storage/domstorage/localstorage/index-get-and-set-expected.txt32
-rw-r--r--LayoutTests/storage/domstorage/localstorage/index-get-and-set.html79
-rw-r--r--LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage-expected.txt7
-rw-r--r--LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage.html50
-rw-r--r--LayoutTests/storage/domstorage/localstorage/resources/clearLocalStorage.js12
-rw-r--r--LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html24
-rw-r--r--LayoutTests/storage/domstorage/localstorage/resources/window-open-second.html36
-rw-r--r--LayoutTests/storage/domstorage/localstorage/simple-usage-expected.txt13
-rw-r--r--LayoutTests/storage/domstorage/localstorage/simple-usage.html49
-rw-r--r--LayoutTests/storage/domstorage/localstorage/string-conversion-expected.txt15
-rw-r--r--LayoutTests/storage/domstorage/localstorage/string-conversion.html54
-rw-r--r--LayoutTests/storage/domstorage/localstorage/window-open-expected.txt8
-rw-r--r--LayoutTests/storage/domstorage/localstorage/window-open.html39
-rw-r--r--LayoutTests/storage/domstorage/quota-expected.txt32
-rw-r--r--LayoutTests/storage/domstorage/quota.html12
-rw-r--r--LayoutTests/storage/domstorage/remove-item-expected.txt65
-rw-r--r--LayoutTests/storage/domstorage/remove-item.html12
-rw-r--r--LayoutTests/storage/domstorage/script-tests/TEMPLATE.html12
-rw-r--r--LayoutTests/storage/domstorage/script-tests/clear.js34
-rw-r--r--LayoutTests/storage/domstorage/script-tests/complex-keys.js152
-rw-r--r--LayoutTests/storage/domstorage/script-tests/complex-values.js79
-rw-r--r--LayoutTests/storage/domstorage/script-tests/quota.js87
-rw-r--r--LayoutTests/storage/domstorage/script-tests/remove-item.js50
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/delete-removal-expected.txt13
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/delete-removal.html51
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/enumerate-storage-expected.txt9
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/enumerate-storage.html47
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key-expected.txt9
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key.html47
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/index-get-and-set-expected.txt32
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/index-get-and-set.html79
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage-expected.txt7
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage.html50
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/resources/clearSessionStorage.js12
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html24
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/resources/window-open-second.html36
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/simple-usage-expected.txt13
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/simple-usage.html49
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/string-conversion-expected.txt15
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/string-conversion.html54
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/window-open-expected.txt8
-rw-r--r--LayoutTests/storage/domstorage/sessionstorage/window-open.html39
-rw-r--r--LayoutTests/storage/domstorage/window-attributes-exist-expected.txt21
-rw-r--r--LayoutTests/storage/domstorage/window-attributes-exist.html59
-rw-r--r--LayoutTests/storage/empty-statement-expected.txt2
-rw-r--r--LayoutTests/storage/empty-statement.html36
-rw-r--r--LayoutTests/storage/execute-sql-args-expected.txt29
-rw-r--r--LayoutTests/storage/execute-sql-args.html109
-rw-r--r--LayoutTests/storage/hash-change-with-xhr-expected.txt3
-rw-r--r--LayoutTests/storage/hash-change-with-xhr.html140
-rw-r--r--LayoutTests/storage/multiple-databases-garbage-collection-expected.txt4
-rw-r--r--LayoutTests/storage/multiple-databases-garbage-collection.html77
-rw-r--r--LayoutTests/storage/multiple-transactions-expected.txt6
-rw-r--r--LayoutTests/storage/multiple-transactions-on-different-handles-expected.txt8
-rw-r--r--LayoutTests/storage/multiple-transactions-on-different-handles.html96
-rw-r--r--LayoutTests/storage/multiple-transactions.html53
-rw-r--r--LayoutTests/storage/open-database-empty-version-expected.txt2
-rw-r--r--LayoutTests/storage/open-database-empty-version.html22
-rw-r--r--LayoutTests/storage/open-database-set-empty-version-expected.txt2
-rw-r--r--LayoutTests/storage/open-database-set-empty-version.html26
-rw-r--r--LayoutTests/storage/open-database-while-transaction-in-progress-expected.txt3
-rw-r--r--LayoutTests/storage/open-database-while-transaction-in-progress.html71
-rw-r--r--LayoutTests/storage/private-browsing-readonly-expected.txt13
-rw-r--r--LayoutTests/storage/private-browsing-readonly.html100
-rw-r--r--LayoutTests/storage/quota-tracking-expected.txt15
-rw-r--r--LayoutTests/storage/quota-tracking.html117
-rw-r--r--LayoutTests/storage/read-and-write-transactions-dont-run-together-expected.txt12
-rw-r--r--LayoutTests/storage/read-and-write-transactions-dont-run-together.html103
-rw-r--r--LayoutTests/storage/read-transactions-running-concurrently-expected.txt3
-rw-r--r--LayoutTests/storage/read-transactions-running-concurrently.html67
-rw-r--r--LayoutTests/storage/resources/database-lock-after-reload-2.html59
-rw-r--r--LayoutTests/storage/resources/stress-frame.html51
-rw-r--r--LayoutTests/storage/sql-data-types-expected.txt7
-rw-r--r--LayoutTests/storage/sql-data-types.html10
-rw-r--r--LayoutTests/storage/sql-data-types.js87
-rw-r--r--LayoutTests/storage/statement-error-callback-expected.txt6
-rw-r--r--LayoutTests/storage/statement-error-callback.html68
-rw-r--r--LayoutTests/storage/success-callback-expected.txt5
-rw-r--r--LayoutTests/storage/success-callback.html54
-rw-r--r--LayoutTests/storage/test-authorizer-expected.txt68
-rw-r--r--LayoutTests/storage/test-authorizer.html175
-rw-r--r--LayoutTests/storage/transaction-callback-exception-crash-expected.txt2
-rw-r--r--LayoutTests/storage/transaction-callback-exception-crash.html20
-rw-r--r--LayoutTests/storage/transaction-error-callback-expected.txt9
-rw-r--r--LayoutTests/storage/transaction-error-callback.html105
124 files changed, 5352 insertions, 0 deletions
diff --git a/LayoutTests/storage/change-version-expected.txt b/LayoutTests/storage/change-version-expected.txt
new file mode 100644
index 0000000..c939e36
--- /dev/null
+++ b/LayoutTests/storage/change-version-expected.txt
@@ -0,0 +1,4 @@
+This test verifies that the JS database.changeVersion() function works as expected.
+Finished tests with version 3; expected version: 3
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/change-version-handle-reuse-expected.txt b/LayoutTests/storage/change-version-handle-reuse-expected.txt
new file mode 100644
index 0000000..805c9fb
--- /dev/null
+++ b/LayoutTests/storage/change-version-handle-reuse-expected.txt
@@ -0,0 +1,6 @@
+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.
+changeVersion: transaction callback
+changeVersion: success callback
+transaction: statement error callback: no such table: FooBar
+TEST COMPLETE.
+
diff --git a/LayoutTests/storage/change-version-handle-reuse.html b/LayoutTests/storage/change-version-handle-reuse.html
new file mode 100644
index 0000000..de3a5f4
--- /dev/null
+++ b/LayoutTests/storage/change-version-handle-reuse.html
@@ -0,0 +1,71 @@
+<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>
+</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">
+FAILURE: test didn't run.
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/storage/change-version.html b/LayoutTests/storage/change-version.html
new file mode 100644
index 0000000..d81c6b5
--- /dev/null
+++ b/LayoutTests/storage/change-version.html
@@ -0,0 +1,90 @@
+<html>
+<head>
+<title>Test database.changeVersion</title>
+<script>
+var db;
+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.");
+ window.location.href = window.location + '?2';
+}
+
+function changeVersionError(error)
+{
+ log("Error: " + error.message);
+ finishTest();
+}
+
+function finishTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+ log("TEST COMPLETE");
+}
+
+function log(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+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);
+ finishTest();
+ } else
+ testPart1();
+}
+
+function testPart1() {
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ 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 +")");
+
+ // 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);
+ });
+ }
+}
+</script>
+</head>
+<body onload="runTest();">
+This test verifies that the JS database.changeVersion() function works as expected.
+<pre id="console"></pre>
+</body>
+</html>
diff --git a/LayoutTests/storage/close-during-stress-test-expected.txt b/LayoutTests/storage/close-during-stress-test-expected.txt
new file mode 100644
index 0000000..2aad90c
--- /dev/null
+++ b/LayoutTests/storage/close-during-stress-test-expected.txt
@@ -0,0 +1,5 @@
+Should not crash or cause an assertion failure.
+
+A JavaScript failure on the console is expected, however, as the global object is cleared when closing a frame. It actually helps to cause database activity by throwing an exception from a callback.
+
+
diff --git a/LayoutTests/storage/close-during-stress-test.html b/LayoutTests/storage/close-during-stress-test.html
new file mode 100644
index 0000000..2183e2f
--- /dev/null
+++ b/LayoutTests/storage/close-during-stress-test.html
@@ -0,0 +1,19 @@
+<body>
+<p>Should not crash or cause an assertion failure.</p>
+<p>A JavaScript failure on the console is expected, however, as the global object is cleared when closing a frame.
+It actually helps to cause database activity by throwing an exception from a callback.</p>
+<iframe src="resources/stress-frame.html" onload="startTest()"></iframe>
+<script>
+if (window.layoutTestController) {
+ layoutTestController.waitUntilDone();
+ layoutTestController.dumpAsText();
+}
+
+function startTest()
+{
+ setTimeout("document.getElementsByTagName('iframe')[0].src = 'about:blank'", 100);
+ if (window.layoutTestController)
+ setTimeout("layoutTestController.notifyDone()", 500);
+}
+</script>
+</body>
diff --git a/LayoutTests/storage/database-lock-after-reload-expected.txt b/LayoutTests/storage/database-lock-after-reload-expected.txt
new file mode 100644
index 0000000..2729563
--- /dev/null
+++ b/LayoutTests/storage/database-lock-after-reload-expected.txt
@@ -0,0 +1,3 @@
+Inserting some data
+Test part 2 Complete
+
diff --git a/LayoutTests/storage/database-lock-after-reload.html b/LayoutTests/storage/database-lock-after-reload.html
new file mode 100644
index 0000000..ad34d5b
--- /dev/null
+++ b/LayoutTests/storage/database-lock-after-reload.html
@@ -0,0 +1,69 @@
+<html>
+<head>
+<script>
+var database;
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ log("Test part 1 Complete");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function errorFunction(tx, error)
+{
+ log("Test failed - " + error.message);
+ finishTest();
+}
+
+function addData(db)
+{
+ 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();
+ });
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ 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);
+ });
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/domstorage/clear-expected.txt b/LayoutTests/storage/domstorage/clear-expected.txt
new file mode 100644
index 0000000..30dcae1
--- /dev/null
+++ b/LayoutTests/storage/domstorage/clear-expected.txt
@@ -0,0 +1,35 @@
+Test basic dom storage .clear() functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+storage['FOO'] = 'MyFOO'
+storage['BAR'] = 'MyBar'
+PASS storage.length is 2
+PASS storage['FOO'] is "MyFOO"
+PASS storage['BAR'] is "MyBar"
+storage.clear()
+PASS storage.length is 0
+PASS storage['FOO'] is undefined
+PASS storage['BAR'] is undefined
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+storage['FOO'] = 'MyFOO'
+storage['BAR'] = 'MyBar'
+PASS storage.length is 2
+PASS storage['FOO'] is "MyFOO"
+PASS storage['BAR'] is "MyBar"
+storage.clear()
+PASS storage.length is 0
+PASS storage['FOO'] is undefined
+PASS storage['BAR'] is undefined
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/domstorage/clear.html b/LayoutTests/storage/domstorage/clear.html
new file mode 100644
index 0000000..8f23701
--- /dev/null
+++ b/LayoutTests/storage/domstorage/clear.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/clear.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/complex-keys-expected.txt b/LayoutTests/storage/domstorage/complex-keys-expected.txt
new file mode 100644
index 0000000..4ff0d29
--- /dev/null
+++ b/LayoutTests/storage/domstorage/complex-keys-expected.txt
@@ -0,0 +1,237 @@
+Test dom storage with many different types of keys (as opposed to values)
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS storage.getItem('FOO') is null
+storage.setItem('FOO', 'BAR')
+PASS storage.length is 1
+PASS storage.getItem('FOO') is "BAR"
+PASS storage.getItem('foo') is null
+PASS storage.foo is undefined.
+PASS storage['foo'] is undefined.
+storage.foo = 'x'
+PASS storage.foo is "x"
+PASS storage['foo'] is "x"
+PASS storage.getItem('foo') is "x"
+storage['foo'] = 'y'
+PASS storage.foo is "y"
+PASS storage['foo'] is "y"
+PASS storage.getItem('foo') is "y"
+storage.setItem('foo', 'z')
+PASS storage.foo is "z"
+PASS storage['foo'] is "z"
+PASS storage.getItem('foo') is "z"
+PASS storage.length is 2
+
+Testing a null key
+storage.setItem(null, 'asdf')
+PASS storage.getItem('null') is "asdf"
+PASS storage.getItem(null) is "asdf"
+PASS storage['null'] is "asdf"
+PASS storage[null] is "asdf"
+PASS storage.length is 3
+storage[null] = 1
+PASS storage.getItem(null) is "1"
+storage['null'] = 2
+PASS storage.getItem(null) is "2"
+storage.setItem('null', 3)
+PASS storage.getItem(null) is "3"
+PASS storage.length is 3
+
+Testing an undefined key
+storage[undefined] = 'xyz'
+PASS storage.getItem('undefined') is "xyz"
+PASS storage.getItem(undefined) is "xyz"
+PASS storage['undefined'] is "xyz"
+PASS storage[undefined] is "xyz"
+PASS storage.length is 4
+storage['undefined'] = 4
+PASS storage.getItem(undefined) is "4"
+storage.setItem(undefined, 5)
+PASS storage.getItem(undefined) is "5"
+storage.setItem('undefined', 6)
+PASS storage.getItem(undefined) is "6"
+PASS storage.length is 4
+
+Testing a numeric key
+storage['2'] = 'ppp'
+PASS storage.getItem('2') is "ppp"
+PASS storage.getItem(2) is "ppp"
+PASS storage['2'] is "ppp"
+PASS storage[2] is "ppp"
+PASS storage.length is 5
+storage[2] = 7
+PASS storage.getItem(2) is "7"
+storage.setItem(2, 8)
+PASS storage.getItem(2) is "8"
+storage.setItem('2', 9)
+PASS storage.getItem(2) is "9"
+PASS storage.length is 5
+
+Setting a non-ascii string to foo
+storage[k] = 'hello'
+PASS storage.getItem(k) is "hello"
+PASS storage[k] is "hello"
+PASS storage.length is 6
+
+Testing case differences
+storage.foo1 = 'lower1'
+storage.FOO1 = 'UPPER1'
+storage['foo2'] = 'lower2'
+storage['FOO2'] = 'UPPER2'
+storage.setItem('foo3', 'lower3')
+storage.setItem('FOO3', 'UPPER3')
+PASS storage.foo1 is "lower1"
+PASS storage.FOO1 is "UPPER1"
+PASS storage['foo2'] is "lower2"
+PASS storage['FOO2'] is "UPPER2"
+PASS storage.getItem('foo3') is "lower3"
+PASS storage.getItem('FOO3') is "UPPER3"
+PASS storage.length is 12
+
+Testing overriding length
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.length = 0
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage['length'] = 0
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.setItem('length', 0)
+PASS storage.length is 13
+PASS storage['length'] is 13
+PASS storage.getItem('length') is "0"
+storage.removeItem('length')
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.setItem('length', 0)
+PASS storage.length is 13
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS storage.getItem('FOO') is null
+storage.setItem('FOO', 'BAR')
+PASS storage.length is 1
+PASS storage.getItem('FOO') is "BAR"
+PASS storage.getItem('foo') is null
+PASS storage.foo is undefined.
+PASS storage['foo'] is undefined.
+storage.foo = 'x'
+PASS storage.foo is "x"
+PASS storage['foo'] is "x"
+PASS storage.getItem('foo') is "x"
+storage['foo'] = 'y'
+PASS storage.foo is "y"
+PASS storage['foo'] is "y"
+PASS storage.getItem('foo') is "y"
+storage.setItem('foo', 'z')
+PASS storage.foo is "z"
+PASS storage['foo'] is "z"
+PASS storage.getItem('foo') is "z"
+PASS storage.length is 2
+
+Testing a null key
+storage.setItem(null, 'asdf')
+PASS storage.getItem('null') is "asdf"
+PASS storage.getItem(null) is "asdf"
+PASS storage['null'] is "asdf"
+PASS storage[null] is "asdf"
+PASS storage.length is 3
+storage[null] = 1
+PASS storage.getItem(null) is "1"
+storage['null'] = 2
+PASS storage.getItem(null) is "2"
+storage.setItem('null', 3)
+PASS storage.getItem(null) is "3"
+PASS storage.length is 3
+
+Testing an undefined key
+storage[undefined] = 'xyz'
+PASS storage.getItem('undefined') is "xyz"
+PASS storage.getItem(undefined) is "xyz"
+PASS storage['undefined'] is "xyz"
+PASS storage[undefined] is "xyz"
+PASS storage.length is 4
+storage['undefined'] = 4
+PASS storage.getItem(undefined) is "4"
+storage.setItem(undefined, 5)
+PASS storage.getItem(undefined) is "5"
+storage.setItem('undefined', 6)
+PASS storage.getItem(undefined) is "6"
+PASS storage.length is 4
+
+Testing a numeric key
+storage['2'] = 'ppp'
+PASS storage.getItem('2') is "ppp"
+PASS storage.getItem(2) is "ppp"
+PASS storage['2'] is "ppp"
+PASS storage[2] is "ppp"
+PASS storage.length is 5
+storage[2] = 7
+PASS storage.getItem(2) is "7"
+storage.setItem(2, 8)
+PASS storage.getItem(2) is "8"
+storage.setItem('2', 9)
+PASS storage.getItem(2) is "9"
+PASS storage.length is 5
+
+Setting a non-ascii string to foo
+storage[k] = 'hello'
+PASS storage.getItem(k) is "hello"
+PASS storage[k] is "hello"
+PASS storage.length is 6
+
+Testing case differences
+storage.foo1 = 'lower1'
+storage.FOO1 = 'UPPER1'
+storage['foo2'] = 'lower2'
+storage['FOO2'] = 'UPPER2'
+storage.setItem('foo3', 'lower3')
+storage.setItem('FOO3', 'UPPER3')
+PASS storage.foo1 is "lower1"
+PASS storage.FOO1 is "UPPER1"
+PASS storage['foo2'] is "lower2"
+PASS storage['FOO2'] is "UPPER2"
+PASS storage.getItem('foo3') is "lower3"
+PASS storage.getItem('FOO3') is "UPPER3"
+PASS storage.length is 12
+
+Testing overriding length
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.length = 0
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage['length'] = 0
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.setItem('length', 0)
+PASS storage.length is 13
+PASS storage['length'] is 13
+PASS storage.getItem('length') is "0"
+storage.removeItem('length')
+PASS storage.length is 12
+PASS storage['length'] is 12
+PASS storage.getItem('length') is null
+storage.setItem('length', 0)
+PASS storage.length is 13
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/domstorage/complex-keys.html b/LayoutTests/storage/domstorage/complex-keys.html
new file mode 100644
index 0000000..40af774
--- /dev/null
+++ b/LayoutTests/storage/domstorage/complex-keys.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/complex-keys.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/complex-values-expected.txt b/LayoutTests/storage/domstorage/complex-values-expected.txt
new file mode 100644
index 0000000..69d7644
--- /dev/null
+++ b/LayoutTests/storage/domstorage/complex-values-expected.txt
@@ -0,0 +1,207 @@
+Test some corner case DOM Storage values.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS typeof storage['foo'] is "undefined"
+PASS storage['foo'] is undefined.
+PASS typeof storage.foo is "undefined"
+PASS storage.foo is undefined.
+PASS typeof storage.getItem('foo') is "object"
+PASS storage.getItem('foo') is null
+
+storage.foo1 = null
+PASS typeof storage['foo1'] is "string"
+PASS storage['foo1'] is "null"
+PASS typeof storage.foo1 is "string"
+PASS storage.foo1 is "null"
+PASS typeof storage.getItem('foo1') is "string"
+PASS storage.getItem('foo1') is "null"
+storage['foo2'] = null
+PASS typeof storage['foo2'] is "string"
+PASS storage['foo2'] is "null"
+PASS typeof storage.foo2 is "string"
+PASS storage.foo2 is "null"
+PASS typeof storage.getItem('foo2') is "string"
+PASS storage.getItem('foo2') is "null"
+storage.setItem('foo3', null)
+PASS typeof storage['foo3'] is "string"
+PASS storage['foo3'] is "null"
+PASS typeof storage.foo3 is "string"
+PASS storage.foo3 is "null"
+PASS typeof storage.getItem('foo3') is "string"
+PASS storage.getItem('foo3') is "null"
+
+storage.foo4 = undefined
+PASS typeof storage['foo4'] is "string"
+PASS storage['foo4'] is "undefined"
+PASS typeof storage.foo4 is "string"
+PASS storage.foo4 is "undefined"
+PASS typeof storage.getItem('foo4') is "string"
+PASS storage.getItem('foo4') is "undefined"
+storage['foo5'] = undefined
+PASS typeof storage['foo5'] is "string"
+PASS storage['foo5'] is "undefined"
+PASS typeof storage.foo5 is "string"
+PASS storage.foo5 is "undefined"
+PASS typeof storage.getItem('foo5') is "string"
+PASS storage.getItem('foo5') is "undefined"
+storage.setItem('foo6', undefined)
+PASS typeof storage['foo6'] is "string"
+PASS storage['foo6'] is "undefined"
+PASS typeof storage.foo6 is "string"
+PASS storage.foo6 is "undefined"
+PASS typeof storage.getItem('foo6') is "string"
+PASS storage.getItem('foo6') is "undefined"
+
+storage.foo7 = 2
+PASS typeof storage['foo7'] is "string"
+PASS storage['foo7'] is "2"
+PASS typeof storage.foo7 is "string"
+PASS storage.foo7 is "2"
+PASS typeof storage.getItem('foo7') is "string"
+PASS storage.getItem('foo7') is "2"
+storage['foo8'] = 2
+PASS typeof storage['foo8'] is "string"
+PASS storage['foo8'] is "2"
+PASS typeof storage.foo8 is "string"
+PASS storage.foo8 is "2"
+PASS typeof storage.getItem('foo8') is "string"
+PASS storage.getItem('foo8') is "2"
+storage.setItem('foo9', 2)
+PASS typeof storage['foo9'] is "string"
+PASS storage['foo9'] is "2"
+PASS typeof storage.foo9 is "string"
+PASS storage.foo9 is "2"
+PASS typeof storage.getItem('foo9') is "string"
+PASS storage.getItem('foo9') is "2"
+
+storage.foo10 = k
+PASS typeof storage['foo10'] is "string"
+PASS storage['foo10'] is "ÿ찡hello"
+PASS typeof storage.foo10 is "string"
+PASS storage.foo10 is "ÿ찡hello"
+PASS typeof storage.getItem('foo10') is "string"
+PASS storage.getItem('foo10') is "ÿ찡hello"
+storage['foo11'] = k
+PASS typeof storage['foo11'] is "string"
+PASS storage['foo11'] is "ÿ찡hello"
+PASS typeof storage.foo11 is "string"
+PASS storage.foo11 is "ÿ찡hello"
+PASS typeof storage.getItem('foo11') is "string"
+PASS storage.getItem('foo11') is "ÿ찡hello"
+storage.setItem('foo12', k)
+PASS typeof storage['foo12'] is "string"
+PASS storage['foo12'] is "ÿ찡hello"
+PASS typeof storage.foo12 is "string"
+PASS storage.foo12 is "ÿ찡hello"
+PASS typeof storage.getItem('foo12') is "string"
+PASS storage.getItem('foo12') is "ÿ찡hello"
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS typeof storage['foo'] is "undefined"
+PASS storage['foo'] is undefined.
+PASS typeof storage.foo is "undefined"
+PASS storage.foo is undefined.
+PASS typeof storage.getItem('foo') is "object"
+PASS storage.getItem('foo') is null
+
+storage.foo1 = null
+PASS typeof storage['foo1'] is "string"
+PASS storage['foo1'] is "null"
+PASS typeof storage.foo1 is "string"
+PASS storage.foo1 is "null"
+PASS typeof storage.getItem('foo1') is "string"
+PASS storage.getItem('foo1') is "null"
+storage['foo2'] = null
+PASS typeof storage['foo2'] is "string"
+PASS storage['foo2'] is "null"
+PASS typeof storage.foo2 is "string"
+PASS storage.foo2 is "null"
+PASS typeof storage.getItem('foo2') is "string"
+PASS storage.getItem('foo2') is "null"
+storage.setItem('foo3', null)
+PASS typeof storage['foo3'] is "string"
+PASS storage['foo3'] is "null"
+PASS typeof storage.foo3 is "string"
+PASS storage.foo3 is "null"
+PASS typeof storage.getItem('foo3') is "string"
+PASS storage.getItem('foo3') is "null"
+
+storage.foo4 = undefined
+PASS typeof storage['foo4'] is "string"
+PASS storage['foo4'] is "undefined"
+PASS typeof storage.foo4 is "string"
+PASS storage.foo4 is "undefined"
+PASS typeof storage.getItem('foo4') is "string"
+PASS storage.getItem('foo4') is "undefined"
+storage['foo5'] = undefined
+PASS typeof storage['foo5'] is "string"
+PASS storage['foo5'] is "undefined"
+PASS typeof storage.foo5 is "string"
+PASS storage.foo5 is "undefined"
+PASS typeof storage.getItem('foo5') is "string"
+PASS storage.getItem('foo5') is "undefined"
+storage.setItem('foo6', undefined)
+PASS typeof storage['foo6'] is "string"
+PASS storage['foo6'] is "undefined"
+PASS typeof storage.foo6 is "string"
+PASS storage.foo6 is "undefined"
+PASS typeof storage.getItem('foo6') is "string"
+PASS storage.getItem('foo6') is "undefined"
+
+storage.foo7 = 2
+PASS typeof storage['foo7'] is "string"
+PASS storage['foo7'] is "2"
+PASS typeof storage.foo7 is "string"
+PASS storage.foo7 is "2"
+PASS typeof storage.getItem('foo7') is "string"
+PASS storage.getItem('foo7') is "2"
+storage['foo8'] = 2
+PASS typeof storage['foo8'] is "string"
+PASS storage['foo8'] is "2"
+PASS typeof storage.foo8 is "string"
+PASS storage.foo8 is "2"
+PASS typeof storage.getItem('foo8') is "string"
+PASS storage.getItem('foo8') is "2"
+storage.setItem('foo9', 2)
+PASS typeof storage['foo9'] is "string"
+PASS storage['foo9'] is "2"
+PASS typeof storage.foo9 is "string"
+PASS storage.foo9 is "2"
+PASS typeof storage.getItem('foo9') is "string"
+PASS storage.getItem('foo9') is "2"
+
+storage.foo10 = k
+PASS typeof storage['foo10'] is "string"
+PASS storage['foo10'] is "ÿ찡hello"
+PASS typeof storage.foo10 is "string"
+PASS storage.foo10 is "ÿ찡hello"
+PASS typeof storage.getItem('foo10') is "string"
+PASS storage.getItem('foo10') is "ÿ찡hello"
+storage['foo11'] = k
+PASS typeof storage['foo11'] is "string"
+PASS storage['foo11'] is "ÿ찡hello"
+PASS typeof storage.foo11 is "string"
+PASS storage.foo11 is "ÿ찡hello"
+PASS typeof storage.getItem('foo11') is "string"
+PASS storage.getItem('foo11') is "ÿ찡hello"
+storage.setItem('foo12', k)
+PASS typeof storage['foo12'] is "string"
+PASS storage['foo12'] is "ÿ찡hello"
+PASS typeof storage.foo12 is "string"
+PASS storage.foo12 is "ÿ찡hello"
+PASS typeof storage.getItem('foo12') is "string"
+PASS storage.getItem('foo12') is "ÿ찡hello"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/domstorage/complex-values.html b/LayoutTests/storage/domstorage/complex-values.html
new file mode 100644
index 0000000..00ad8d7
--- /dev/null
+++ b/LayoutTests/storage/domstorage/complex-values.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/complex-values.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/basic-body-attribute-expected.txt b/LayoutTests/storage/domstorage/events/basic-body-attribute-expected.txt
new file mode 100644
index 0000000..273c95b
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic-body-attribute-expected.txt
@@ -0,0 +1,102 @@
+This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener specified as an attribute on the body.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+iframe.onload = step1
+iframe.src = 'resources/body-event-handler.html'
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+iframe.onload = step1
+iframe.src = 'resources/body-event-handler.html'
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/storage/domstorage/events/basic-body-attribute.html b/LayoutTests/storage/domstorage/events/basic-body-attribute.html
new file mode 100644
index 0000000..c2778ff
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic-body-attribute.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="script-tests/basic-body-attribute.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/basic-expected.txt b/LayoutTests/storage/domstorage/events/basic-expected.txt
new file mode 100644
index 0000000..bccdb5c
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic-expected.txt
@@ -0,0 +1,98 @@
+This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener set via window.onstorage.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/storage/domstorage/events/basic-setattribute-expected.txt b/LayoutTests/storage/domstorage/events/basic-setattribute-expected.txt
new file mode 100644
index 0000000..99c7f18
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic-setattribute-expected.txt
@@ -0,0 +1,102 @@
+This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener attached via setattribute.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+iframe.onload = step1
+iframe.src = 'resources/setattribute-event-handler.html'
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+iframe.onload = step1
+iframe.src = 'resources/setattribute-event-handler.html'
+Reset storage event list
+storageEventList = new Array()
+storage.setItem('FOO', 'BAR')
+PASS storageEventList.length is 1
+PASS storageEventList[0].key is "FOO"
+PASS storageEventList[0].oldValue is null
+PASS storageEventList[0].newValue is "BAR"
+storage.setItem('FU', 'BAR')
+storage.setItem('a', '1')
+storage.setItem('b', '2')
+storage.setItem('b', '3')
+PASS storageEventList.length is 5
+PASS storageEventList[1].key is "FU"
+PASS storageEventList[1].oldValue is null
+PASS storageEventList[1].newValue is "BAR"
+PASS storageEventList[2].key is "a"
+PASS storageEventList[2].oldValue is null
+PASS storageEventList[2].newValue is "1"
+PASS storageEventList[3].key is "b"
+PASS storageEventList[3].oldValue is null
+PASS storageEventList[3].newValue is "2"
+PASS storageEventList[4].key is "b"
+PASS storageEventList[4].oldValue is "2"
+PASS storageEventList[4].newValue is "3"
+storage.removeItem('FOO')
+PASS storageEventList.length is 6
+PASS storageEventList[5].key is "FOO"
+PASS storageEventList[5].oldValue is "BAR"
+PASS storageEventList[5].newValue is null
+storage.removeItem('FU')
+PASS storageEventList.length is 7
+PASS storageEventList[6].key is "FU"
+PASS storageEventList[6].oldValue is "BAR"
+PASS storageEventList[6].newValue is null
+storage.clear()
+PASS storageEventList.length is 8
+PASS storageEventList[7].key is null
+PASS storageEventList[7].oldValue is null
+PASS storageEventList[7].newValue is null
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/storage/domstorage/events/basic-setattribute.html b/LayoutTests/storage/domstorage/events/basic-setattribute.html
new file mode 100644
index 0000000..2b95568
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic-setattribute.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="script-tests/basic-setattribute.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/basic.html b/LayoutTests/storage/domstorage/events/basic.html
new file mode 100644
index 0000000..4c52d07
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/basic.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="script-tests/basic.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/case-sensitive-expected.txt b/LayoutTests/storage/domstorage/events/case-sensitive-expected.txt
new file mode 100644
index 0000000..6cdf19f
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/case-sensitive-expected.txt
@@ -0,0 +1,38 @@
+Verify that storage events fire even when only the case of the value changes.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+
+Verify storage events are case sensitive
+storage.foo = 'test'
+Reset storage event list
+storageEventList = new Array()
+storage.foo = 'test'
+PASS storageEventList.length is 0
+storage.foo = 'TEST'
+PASS storageEventList.length is 1
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+
+Verify storage events are case sensitive
+storage.foo = 'test'
+Reset storage event list
+storageEventList = new Array()
+storage.foo = 'test'
+PASS storageEventList.length is 0
+storage.foo = 'TEST'
+PASS storageEventList.length is 1
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/storage/domstorage/events/case-sensitive.html b/LayoutTests/storage/domstorage/events/case-sensitive.html
new file mode 100644
index 0000000..c8da433
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/case-sensitive.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="script-tests/case-sensitive.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/documentURI-expected.txt b/LayoutTests/storage/domstorage/events/documentURI-expected.txt
new file mode 100644
index 0000000..5952523
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/documentURI-expected.txt
@@ -0,0 +1,40 @@
+Test that changing documentURI has no effects on the uri passed into storage events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+Reset storage event list
+storageEventList = new Array()
+storage.foo = '123'
+PASS storageEventList.length is 1
+Saving URI
+document.documentURI = 'abc'
+PASS document.documentURI is "abc"
+storage.foo = 'xyz'
+PASS storageEventList.length is 2
+PASS true is true
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+Reset storage event list
+storageEventList = new Array()
+storage.foo = '123'
+PASS storageEventList.length is 1
+Saving URI
+document.documentURI = 'abc'
+PASS document.documentURI is "abc"
+storage.foo = 'xyz'
+PASS storageEventList.length is 2
+PASS true is true
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/storage/domstorage/events/documentURI.html b/LayoutTests/storage/domstorage/events/documentURI.html
new file mode 100644
index 0000000..7721e87
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/documentURI.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="script-tests/documentURI.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/resources/body-event-handler.html b/LayoutTests/storage/domstorage/events/resources/body-event-handler.html
new file mode 100644
index 0000000..a1218fe
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/resources/body-event-handler.html
@@ -0,0 +1,8 @@
+<html><head><script>
+
+function handleStorageEvent(e) {
+ window.parent.storageEventList.push(e);
+}
+
+</script></head><body onstorage="handleStorageEvent(window.event);">
+</body></html>
diff --git a/LayoutTests/storage/domstorage/events/resources/eventTestHarness.js b/LayoutTests/storage/domstorage/events/resources/eventTestHarness.js
new file mode 100644
index 0000000..8f15c31
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/resources/eventTestHarness.js
@@ -0,0 +1,51 @@
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+
+iframe = document.createElement("IFRAME");
+iframe.src = "about:blank";
+document.body.appendChild(iframe);
+iframe.contentWindow.document.body.innerText = "Nothing to see here.";
+
+storageEventList = new Array();
+iframe.contentWindow.onstorage = function (e) {
+ window.parent.storageEventList.push(e);
+}
+
+function runAfterStorageEvents(callback) {
+ var currentCount = storageEventList.length;
+ function onTimeout() {
+ if (currentCount != storageEventList.length)
+ runAfterStorageEvents(callback);
+ else
+ callback();
+ }
+ setTimeout(onTimeout, 0);
+}
+
+function testStorages(testCallback)
+{
+ // When we're done testing LocalStorage, this is run.
+ function allDone()
+ {
+ debug("");
+ debug("");
+ window.successfullyParsed = true;
+ isSuccessfullyParsed();
+ debug("");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone()
+ }
+
+ // When we're done testing with SessionStorage, this is run.
+ function runLocalStorage()
+ {
+ debug("");
+ debug("");
+ testCallback("localStorage", allDone);
+ }
+
+ // First run the test with SessionStorage.
+ testCallback("sessionStorage", runLocalStorage);
+}
diff --git a/LayoutTests/storage/domstorage/events/resources/setattribute-event-handler.html b/LayoutTests/storage/domstorage/events/resources/setattribute-event-handler.html
new file mode 100644
index 0000000..a764c53
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/resources/setattribute-event-handler.html
@@ -0,0 +1,11 @@
+<html><head></head><body>
+<script>
+
+function handleStorageEvent(e) {
+ window.parent.storageEventList.push(e);
+}
+
+document.body.setAttribute("onstorage", "handleStorageEvent(window.event);");
+
+</script>
+</body></html>
diff --git a/LayoutTests/storage/domstorage/events/script-tests/TEMPLATE.html b/LayoutTests/storage/domstorage/events/script-tests/TEMPLATE.html
new file mode 100644
index 0000000..d9af438
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/TEMPLATE.html
@@ -0,0 +1,13 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/eventTestHarness.js"></script>
+<script src="YOUR_JS_FILE_HERE"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/events/script-tests/basic-body-attribute.js b/LayoutTests/storage/domstorage/events/script-tests/basic-body-attribute.js
new file mode 100644
index 0000000..7054069
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/basic-body-attribute.js
@@ -0,0 +1,98 @@
+description("This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener specified as an attribute on the body.");
+
+function test(storageString, callback)
+{
+ window.completionCallback = callback;
+ window.storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ evalAndLog("iframe.onload = step1");
+ evalAndLog("iframe.src = 'resources/body-event-handler.html'");
+}
+
+function step1()
+{
+ debug("Reset storage event list");
+ evalAndLog("storageEventList = new Array()");
+ evalAndLog("storage.setItem('FOO', 'BAR')");
+
+ runAfterStorageEvents(step2);
+}
+
+function step2()
+{
+ shouldBe("storageEventList.length", "1");
+ shouldBeEqualToString("storageEventList[0].key", "FOO");
+ shouldBeNull("storageEventList[0].oldValue");
+ shouldBeEqualToString("storageEventList[0].newValue", "BAR");
+ evalAndLog("storage.setItem('FU', 'BAR')");
+ evalAndLog("storage.setItem('a', '1')");
+ evalAndLog("storage.setItem('b', '2')");
+ evalAndLog("storage.setItem('b', '3')");
+
+ runAfterStorageEvents(step3);
+}
+
+function step3()
+{
+ shouldBe("storageEventList.length", "5");
+ shouldBeEqualToString("storageEventList[1].key", "FU");
+ shouldBeNull("storageEventList[1].oldValue");
+ shouldBeEqualToString("storageEventList[1].newValue", "BAR");
+ shouldBeEqualToString("storageEventList[2].key", "a");
+ shouldBeNull("storageEventList[2].oldValue");
+ shouldBeEqualToString("storageEventList[2].newValue", "1");
+ shouldBeEqualToString("storageEventList[3].key", "b");
+ shouldBeNull("storageEventList[3].oldValue");
+ shouldBeEqualToString("storageEventList[3].newValue", "2");
+ shouldBeEqualToString("storageEventList[4].key", "b");
+ shouldBeEqualToString("storageEventList[4].oldValue", "2");
+ shouldBeEqualToString("storageEventList[4].newValue", "3");
+ evalAndLog("storage.removeItem('FOO')");
+
+ runAfterStorageEvents(step4);
+}
+
+function step4()
+{
+ shouldBe("storageEventList.length", "6");
+ shouldBeEqualToString("storageEventList[5].key", "FOO");
+ shouldBeEqualToString("storageEventList[5].oldValue", "BAR");
+ shouldBeNull("storageEventList[5].newValue");
+ evalAndLog("storage.removeItem('FU')");
+
+ runAfterStorageEvents(step5);
+}
+
+function step5()
+{
+ shouldBe("storageEventList.length", "7");
+ shouldBeEqualToString("storageEventList[6].key", "FU");
+ shouldBeEqualToString("storageEventList[6].oldValue", "BAR");
+ shouldBeNull("storageEventList[6].newValue");
+ evalAndLog("storage.clear()");
+
+ runAfterStorageEvents(step6);
+}
+
+function step6()
+{
+ shouldBe("storageEventList.length", "8");
+ shouldBeNull("storageEventList[7].key");
+ shouldBeNull("storageEventList[7].oldValue");
+ shouldBeNull("storageEventList[7].newValue");
+
+ completionCallback();
+}
+
+testStorages(test);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/storage/domstorage/events/script-tests/basic-setattribute.js b/LayoutTests/storage/domstorage/events/script-tests/basic-setattribute.js
new file mode 100644
index 0000000..0da34a6
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/basic-setattribute.js
@@ -0,0 +1,98 @@
+description("This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener attached via setattribute.");
+
+function test(storageString, callback)
+{
+ window.completionCallback = callback;
+ window.storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ evalAndLog("iframe.onload = step1");
+ evalAndLog("iframe.src = 'resources/setattribute-event-handler.html'");
+}
+
+function step1()
+{
+ debug("Reset storage event list");
+ evalAndLog("storageEventList = new Array()");
+ evalAndLog("storage.setItem('FOO', 'BAR')");
+
+ runAfterStorageEvents(step2);
+}
+
+function step2()
+{
+ shouldBe("storageEventList.length", "1");
+ shouldBeEqualToString("storageEventList[0].key", "FOO");
+ shouldBeNull("storageEventList[0].oldValue");
+ shouldBeEqualToString("storageEventList[0].newValue", "BAR");
+ evalAndLog("storage.setItem('FU', 'BAR')");
+ evalAndLog("storage.setItem('a', '1')");
+ evalAndLog("storage.setItem('b', '2')");
+ evalAndLog("storage.setItem('b', '3')");
+
+ runAfterStorageEvents(step3);
+}
+
+function step3()
+{
+ shouldBe("storageEventList.length", "5");
+ shouldBeEqualToString("storageEventList[1].key", "FU");
+ shouldBeNull("storageEventList[1].oldValue");
+ shouldBeEqualToString("storageEventList[1].newValue", "BAR");
+ shouldBeEqualToString("storageEventList[2].key", "a");
+ shouldBeNull("storageEventList[2].oldValue");
+ shouldBeEqualToString("storageEventList[2].newValue", "1");
+ shouldBeEqualToString("storageEventList[3].key", "b");
+ shouldBeNull("storageEventList[3].oldValue");
+ shouldBeEqualToString("storageEventList[3].newValue", "2");
+ shouldBeEqualToString("storageEventList[4].key", "b");
+ shouldBeEqualToString("storageEventList[4].oldValue", "2");
+ shouldBeEqualToString("storageEventList[4].newValue", "3");
+ evalAndLog("storage.removeItem('FOO')");
+
+ runAfterStorageEvents(step4);
+}
+
+function step4()
+{
+ shouldBe("storageEventList.length", "6");
+ shouldBeEqualToString("storageEventList[5].key", "FOO");
+ shouldBeEqualToString("storageEventList[5].oldValue", "BAR");
+ shouldBeNull("storageEventList[5].newValue");
+ evalAndLog("storage.removeItem('FU')");
+
+ runAfterStorageEvents(step5);
+}
+
+function step5()
+{
+ shouldBe("storageEventList.length", "7");
+ shouldBeEqualToString("storageEventList[6].key", "FU");
+ shouldBeEqualToString("storageEventList[6].oldValue", "BAR");
+ shouldBeNull("storageEventList[6].newValue");
+ evalAndLog("storage.clear()");
+
+ runAfterStorageEvents(step6);
+}
+
+function step6()
+{
+ shouldBe("storageEventList.length", "8");
+ shouldBeNull("storageEventList[7].key");
+ shouldBeNull("storageEventList[7].oldValue");
+ shouldBeNull("storageEventList[7].newValue");
+
+ completionCallback();
+}
+
+testStorages(test);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/storage/domstorage/events/script-tests/basic.js b/LayoutTests/storage/domstorage/events/script-tests/basic.js
new file mode 100644
index 0000000..06591b1
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/basic.js
@@ -0,0 +1,97 @@
+description("This is a test to make sure DOM Storage mutations fire StorageEvents that are caught by the event listener set via window.onstorage.");
+
+function test(storageString, callback)
+{
+ window.completionCallback = callback;
+ window.storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ runAfterStorageEvents(step1);
+}
+
+function step1()
+{
+ debug("Reset storage event list");
+ evalAndLog("storageEventList = new Array()");
+ evalAndLog("storage.setItem('FOO', 'BAR')");
+
+ runAfterStorageEvents(step2);
+}
+
+function step2()
+{
+ shouldBe("storageEventList.length", "1");
+ shouldBeEqualToString("storageEventList[0].key", "FOO");
+ shouldBeNull("storageEventList[0].oldValue");
+ shouldBeEqualToString("storageEventList[0].newValue", "BAR");
+ evalAndLog("storage.setItem('FU', 'BAR')");
+ evalAndLog("storage.setItem('a', '1')");
+ evalAndLog("storage.setItem('b', '2')");
+ evalAndLog("storage.setItem('b', '3')");
+
+ runAfterStorageEvents(step3);
+}
+
+function step3()
+{
+ shouldBe("storageEventList.length", "5");
+ shouldBeEqualToString("storageEventList[1].key", "FU");
+ shouldBeNull("storageEventList[1].oldValue");
+ shouldBeEqualToString("storageEventList[1].newValue", "BAR");
+ shouldBeEqualToString("storageEventList[2].key", "a");
+ shouldBeNull("storageEventList[2].oldValue");
+ shouldBeEqualToString("storageEventList[2].newValue", "1");
+ shouldBeEqualToString("storageEventList[3].key", "b");
+ shouldBeNull("storageEventList[3].oldValue");
+ shouldBeEqualToString("storageEventList[3].newValue", "2");
+ shouldBeEqualToString("storageEventList[4].key", "b");
+ shouldBeEqualToString("storageEventList[4].oldValue", "2");
+ shouldBeEqualToString("storageEventList[4].newValue", "3");
+ evalAndLog("storage.removeItem('FOO')");
+
+ runAfterStorageEvents(step4);
+}
+
+function step4()
+{
+ shouldBe("storageEventList.length", "6");
+ shouldBeEqualToString("storageEventList[5].key", "FOO");
+ shouldBeEqualToString("storageEventList[5].oldValue", "BAR");
+ shouldBeNull("storageEventList[5].newValue");
+ evalAndLog("storage.removeItem('FU')");
+
+ runAfterStorageEvents(step5);
+}
+
+function step5()
+{
+ shouldBe("storageEventList.length", "7");
+ shouldBeEqualToString("storageEventList[6].key", "FU");
+ shouldBeEqualToString("storageEventList[6].oldValue", "BAR");
+ shouldBeNull("storageEventList[6].newValue");
+ evalAndLog("storage.clear()");
+
+ runAfterStorageEvents(step6);
+}
+
+function step6()
+{
+ shouldBe("storageEventList.length", "8");
+ shouldBeNull("storageEventList[7].key");
+ shouldBeNull("storageEventList[7].oldValue");
+ shouldBeNull("storageEventList[7].newValue");
+
+ completionCallback();
+}
+
+testStorages(test);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/storage/domstorage/events/script-tests/case-sensitive.js b/LayoutTests/storage/domstorage/events/script-tests/case-sensitive.js
new file mode 100644
index 0000000..b5038a8
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/case-sensitive.js
@@ -0,0 +1,50 @@
+description("Verify that storage events fire even when only the case of the value changes.");
+
+function test(storageString, callback)
+{
+ window.completionCallback = callback;
+ window.storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("");
+ debug("Verify storage events are case sensitive");
+ evalAndLog("storage.foo = 'test'");
+
+ runAfterStorageEvents(step1);
+}
+
+function step1()
+{
+ debug("Reset storage event list");
+ evalAndLog("storageEventList = new Array()");
+ evalAndLog("storage.foo = 'test'");
+
+ runAfterStorageEvents(step2);
+}
+
+function step2()
+{
+ shouldBe("storageEventList.length", "0");
+ evalAndLog("storage.foo = 'TEST'");
+
+ runAfterStorageEvents(step3);
+}
+
+function step3()
+{
+ shouldBe("storageEventList.length", "1");
+
+ completionCallback();
+}
+
+testStorages(test);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/storage/domstorage/events/script-tests/documentURI.js b/LayoutTests/storage/domstorage/events/script-tests/documentURI.js
new file mode 100644
index 0000000..4b4f4cb
--- /dev/null
+++ b/LayoutTests/storage/domstorage/events/script-tests/documentURI.js
@@ -0,0 +1,52 @@
+description("Test that changing documentURI has no effects on the uri passed into storage events.");
+
+function test(storageString, callback)
+{
+ window.completionCallback = callback;
+ window.storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ runAfterStorageEvents(step1);
+}
+
+function step1()
+{
+ debug("Reset storage event list");
+ evalAndLog("storageEventList = new Array()");
+ evalAndLog("storage.foo = '123'");
+
+ runAfterStorageEvents(step2);
+}
+
+function step2()
+{
+ shouldBe("storageEventList.length", "1");
+ debug("Saving URI");
+ window.lastURI = storageEventList[0].uri;
+
+ evalAndLog("document.documentURI = 'abc'");
+ shouldBeEqualToString("document.documentURI", "abc");
+ evalAndLog("storage.foo = 'xyz'");
+
+ runAfterStorageEvents(step3);
+}
+
+function step3()
+{
+ shouldBe("storageEventList.length", "2");
+ shouldBeTrue(String(window.lastURI == storageEventList[1].uri));
+
+ completionCallback();
+}
+
+testStorages(test);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/storage/domstorage/localstorage/delete-removal-expected.txt b/LayoutTests/storage/domstorage/localstorage/delete-removal-expected.txt
new file mode 100644
index 0000000..b50b617
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/delete-removal-expected.txt
@@ -0,0 +1,13 @@
+This test makes sure that using the syntax `delete localStorage.keyName` works as an alias to `localStorage.removeItem(keyName).`
+foo (before anything) is: undefined
+foo (after a named property settter set) is: bar
+foo (after a delete) is: undefined
+foo (after an indexed setter set) is: bar
+foo (after deleting FOO (not foo)) is: bar
+foo (after a delete) is: undefined
+foo (after calling setItem) is: bar
+foo (after a delete) is: undefined
+foo (after a redundant delete) is: undefined
+foo (after an implicit settter set) is: bar
+foo (after an indexed delete) is: undefined
+
diff --git a/LayoutTests/storage/domstorage/localstorage/delete-removal.html b/LayoutTests/storage/domstorage/localstorage/delete-removal.html
new file mode 100644
index 0000000..85d1278
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/delete-removal.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById('logger').appendChild(document.createTextNode(a));
+ document.getElementById('logger').appendChild(document.createElement("br"));
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ log("foo (before anything) is: " + localStorage.foo);
+ localStorage.foo = "bar";
+ log("foo (after a named property settter set) is: " + localStorage.foo);
+ delete localStorage.foo;
+ log("foo (after a delete) is: " + localStorage.foo);
+ localStorage["foo"] = "bar";
+ log("foo (after an indexed setter set) is: " + localStorage.foo);
+ delete localStorage.FOO;
+ log("foo (after deleting FOO (not foo)) is: " + localStorage.foo);
+ delete localStorage.foo;
+ log("foo (after a delete) is: " + localStorage.foo);
+ localStorage.setItem("foo", "bar");
+ log("foo (after calling setItem) is: " + localStorage.foo);
+ delete localStorage.foo;
+ log("foo (after a delete) is: " + localStorage.foo);
+ delete localStorage.foo;
+ log("foo (after a redundant delete) is: " + localStorage.foo);
+ localStorage.foo = "bar";
+ log("foo (after an implicit settter set) is: " + localStorage.foo);
+ delete localStorage["foo"];
+ log("foo (after an indexed delete) is: " + localStorage.foo);
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test makes sure that using the syntax `delete localStorage.keyName` works as an alias to `localStorage.removeItem(keyName).`<br><hr>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/enumerate-storage-expected.txt b/LayoutTests/storage/domstorage/localstorage/enumerate-storage-expected.txt
new file mode 100644
index 0000000..a9feac5
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/enumerate-storage-expected.txt
@@ -0,0 +1,9 @@
+This test checks to see that you can enumerate a Storage object and get only the keys as a result. The built-in properties of the Storage object should be ignored. The test operates on the localStorage object.
+alpha
+bar
+batman
+foo
+fu
+prototypeTestKey
+zeta
+
diff --git a/LayoutTests/storage/domstorage/localstorage/enumerate-storage.html b/LayoutTests/storage/domstorage/localstorage/enumerate-storage.html
new file mode 100644
index 0000000..b45a419
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/enumerate-storage.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function startTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ Storage.prototype.prototypeTestKey = "prototypeTestValue";
+ localStorage.foo = "bar";
+ localStorage.fu = "baz";
+ localStorage.batman = "bin suparman";
+ localStorage.bar = "foo";
+ localStorage.alpha = "beta";
+ localStorage.zeta = "gamma";
+
+ // Enumerate localStorage, appending each key onto an array
+ var enumeratedArray = new Array();
+ for (var n in localStorage)
+ enumeratedArray.push(n);
+
+ // Sort the array, since the storage order isn't guaranteed
+ enumeratedArray.sort();
+
+ for (var n in enumeratedArray)
+ log(enumeratedArray[n]);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+This test checks to see that you can enumerate a Storage object and get only the keys as a result. The built-in properties of the Storage object should be ignored. The test operates on the localStorage object.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key-expected.txt b/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key-expected.txt
new file mode 100644
index 0000000..7b61676
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key-expected.txt
@@ -0,0 +1,8 @@
+This test attempts to enumerate all the keys in localStorage with .length + .key(). The built-in properties of the Storage object should be ignored. The test operates on the localStorage object.
+alpha
+bar
+batman
+foo
+fu
+zeta
+
diff --git a/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key.html b/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key.html
new file mode 100644
index 0000000..aa77dcd
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/enumerate-with-length-and-key.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function startTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ Storage.prototype.prototypeTestKey = "prototypeTestValue";
+ localStorage.foo = "bar";
+ localStorage.fu = "baz";
+ localStorage.batman = "bin suparman";
+ localStorage.bar = "foo";
+ localStorage.alpha = "beta";
+ localStorage.zeta = "gamma";
+
+ // Enumerate localStorage, appending each key onto an array
+ var enumeratedArray = new Array();
+ for (var i=0; i<localStorage.length; ++i)
+ enumeratedArray.push(localStorage.key(i));
+
+ // Sort the array, since the storage order isn't guaranteed
+ enumeratedArray.sort();
+
+ for (var n in enumeratedArray)
+ log(enumeratedArray[n]);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+This test attempts to enumerate all the keys in localStorage with .length + .key(). The built-in properties of the Storage object should be ignored. The test operates on the localStorage object.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/index-get-and-set-expected.txt b/LayoutTests/storage/domstorage/localstorage/index-get-and-set-expected.txt
new file mode 100644
index 0000000..ad58023
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/index-get-and-set-expected.txt
@@ -0,0 +1,32 @@
+This is a test to make sure you can get and set values in localStorage by index.
+Setting FOO using the index setter.
+Reading FOO:
+BAR
+BAR
+BAR
+
+Setting FOO again, using setItem.
+Reading FOO:
+BAZ
+BAZ
+BAZ
+
+Setting FOO again, using the index setter.
+Reading FOO:
+BAT
+BAT
+BAT
+
+Setting FOO again, using property-slot syntax
+Reading FOO:
+BATMAN
+BATMAN
+BATMAN
+
+Removing FOO, then trying to read it
+Reading FOO:
+undefined
+undefined
+null
+
+
diff --git a/LayoutTests/storage/domstorage/localstorage/index-get-and-set.html b/LayoutTests/storage/domstorage/localstorage/index-get-and-set.html
new file mode 100644
index 0000000..1026f51
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/index-get-and-set.html
@@ -0,0 +1,79 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function finish()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone()
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ finish();
+ return;
+ }
+
+ log("Setting FOO using the index setter.");
+ localStorage["FOO"] = "BAR";
+ log("Reading FOO:");
+ log(localStorage.FOO);
+ log(localStorage["FOO"]);
+ log(localStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using setItem.");
+ localStorage.setItem("FOO", "BAZ");
+ log("Reading FOO:");
+ log(localStorage.FOO);
+ log(localStorage["FOO"]);
+ log(localStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using the index setter.");
+ localStorage["FOO"] = "BAT";
+ log("Reading FOO:");
+ log(localStorage.FOO);
+ log(localStorage["FOO"]);
+ log(localStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using property-slot syntax");
+ localStorage.FOO = "BATMAN";
+ log("Reading FOO:");
+ log(localStorage.FOO);
+ log(localStorage["FOO"]);
+ log(localStorage.getItem("FOO"));
+ log("");
+
+ log("Removing FOO, then trying to read it");
+ localStorage.removeItem("FOO");
+ log("Reading FOO:");
+ log(localStorage.FOO);
+ log(localStorage["FOO"]);
+ log(localStorage.getItem("FOO"));
+ log("");
+
+ finish();
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a test to make sure you can get and set values in localStorage by index.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage-expected.txt b/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage-expected.txt
new file mode 100644
index 0000000..6de4296
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage-expected.txt
@@ -0,0 +1,7 @@
+This is a test to make sure that when private browsing is on any attempt to change the localStorage area fail.
+Initial value of testItem is: InitialValue
+Caught exception trying to change item: Error: QUOTA_EXCEEDED_ERR: DOM Exception 22
+After change attempt, testItem is: InitialValue
+After remove attempt, testItem is: InitialValue
+After clear attempt, testItem is: InitialValue
+
diff --git a/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage.html b/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage.html
new file mode 100644
index 0000000..9e46bec
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/private-browsing-affects-storage.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+ if (!window.layoutTestController) {
+ log("Test only designed to be run under DumpRenderTree");
+ return;
+ }
+
+ localStorage.setItem("testItem", "InitialValue");
+ layoutTestController.setPrivateBrowsingEnabled(true);
+ log("Initial value of testItem is: " + localStorage.getItem("testItem"));
+
+ try {
+ localStorage.setItem("testItem", "ChangedValue");
+ } catch(e) {
+ log("Caught exception trying to change item: " + e);
+ }
+
+ log("After change attempt, testItem is: " + localStorage.getItem("testItem"));
+
+ localStorage.removeItem("testItem");
+ log("After remove attempt, testItem is: " + localStorage.getItem("testItem"));
+
+ localStorage.clear();
+ log("After clear attempt, testItem is: " + localStorage.getItem("testItem"));
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a test to make sure that when private browsing is on any attempt to change the localStorage area fail.
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/resources/clearLocalStorage.js b/LayoutTests/storage/domstorage/localstorage/resources/clearLocalStorage.js
new file mode 100644
index 0000000..418d000
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/resources/clearLocalStorage.js
@@ -0,0 +1,12 @@
+function clearLocalStorage()
+{
+ var keys = new Array();
+ for (key in localStorage)
+ keys.push(key);
+
+ for (key in keys)
+ localStorage.removeItem(keys[key]);
+}
+
+if (window.localStorage)
+ clearLocalStorage();
diff --git a/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html b/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html
new file mode 100644
index 0000000..2ab8ba1
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/resources/iframe-events-second.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script>
+function handleStorageEvent(e)
+{
+ parent.log("Subframe received storage event:");
+ 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("Storage Area - " + ((e.storageArea == window.localStorage) ? "This window's window.localStorage" : "Another window's window.localStorage"));
+ parent.log("");
+
+ if (e.key != "Subframe") {
+ parent.log("Subframe about to change localStorage...");
+ localStorage.setItem("Subframe", "SET");
+ }
+}
+</script>
+</head>
+<body onload="window.addEventListener('storage', handleStorageEvent, false);">
+This is the subframe which exists to make sure that both frames of a same security origin receive the event for that origin's localStorage object mutating
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/resources/window-open-second.html b/LayoutTests/storage/domstorage/localstorage/resources/window-open-second.html
new file mode 100644
index 0000000..f944d71
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/resources/window-open-second.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script>
+
+var secondWindowLog = "Logging from second window:<br>";
+
+function log(a)
+{
+ secondWindowLog += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ log("Value for FOO is " + window.localStorage.getItem("FOO"));
+ window.localStorage.setItem("FOO", "BAR-NEWWINDOW");
+ log("Value for FOO after changing my own copy is " + window.localStorage.getItem("FOO"));
+
+ log("Value for FOO in my opening window is " + window.opener.localStorage.getItem("FOO"));
+
+ window.opener.log(secondWindowLog);
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a new window to make sure the localStorage object for an origin is shared between multiple windows.<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/simple-usage-expected.txt b/LayoutTests/storage/domstorage/localstorage/simple-usage-expected.txt
new file mode 100644
index 0000000..8a318c8
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/simple-usage-expected.txt
@@ -0,0 +1,13 @@
+This test tries simple operations on localStorage
+Length is 0
+Value for FOO is null
+Length is 1
+Value for FOO is BAR
+Key for index 0 is FOO
+Key for index 1 is null
+Key for index -1 is null
+Length is 1
+Value for FOO is BAZ
+Length is 0
+Value for FOO is null
+
diff --git a/LayoutTests/storage/domstorage/localstorage/simple-usage.html b/LayoutTests/storage/domstorage/localstorage/simple-usage.html
new file mode 100644
index 0000000..37592d0
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/simple-usage.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ log("Length is " + localStorage.length);
+ log("Value for FOO is " + localStorage.getItem("FOO"));
+
+ localStorage.setItem("FOO", "BAR");
+
+ log("Length is " + localStorage.length);
+ log("Value for FOO is " + localStorage.getItem("FOO"));
+ log("Key for index 0 is " + localStorage.key(0));
+ log("Key for index 1 is " + localStorage.key(1));
+ log("Key for index -1 is " + localStorage.key(-1));
+
+ localStorage.setItem("FOO", "BAZ");
+
+ log("Length is " + localStorage.length);
+ log("Value for FOO is " + localStorage.getItem("FOO"));
+
+ localStorage.removeItem("FOO");
+
+ log("Length is " + localStorage.length);
+ log("Value for FOO is " + localStorage.getItem("FOO"));
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test tries simple operations on localStorage<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/string-conversion-expected.txt b/LayoutTests/storage/domstorage/localstorage/string-conversion-expected.txt
new file mode 100644
index 0000000..81ca1fa
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/string-conversion-expected.txt
@@ -0,0 +1,15 @@
+This test case verifies that local storage only stores strings.
+Length is 0
+Testing implicit setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+Testing explicit setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+Testing index setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+
diff --git a/LayoutTests/storage/domstorage/localstorage/string-conversion.html b/LayoutTests/storage/domstorage/localstorage/string-conversion.html
new file mode 100644
index 0000000..aa8bdfd
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/string-conversion.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ log("Length is " + localStorage.length);
+
+ log("Testing implicit setters");
+ localStorage.a = null;
+ log("Type/value for null is " + typeof localStorage.a + "/" + localStorage.a);
+ localStorage.b = 0;
+ log("Type/value for 0 is " + typeof localStorage.b + "/" + localStorage.b);
+ localStorage.c = function(){};
+ log("Type/value for function(){} is " + typeof localStorage.c + "/" + localStorage.c);
+
+ log("Testing explicit setters");
+ localStorage.setItem('d', null);
+ log("Type/value for null is " + typeof localStorage.d + "/" + localStorage.d);
+ localStorage.setItem('e', 0);
+ log("Type/value for 0 is " + typeof localStorage.e + "/" + localStorage.e);
+ localStorage.setItem('f', function(){});
+ log("Type/value for function(){} is " + typeof localStorage.f + "/" + localStorage.f);
+
+ log("Testing index setters");
+ localStorage['g'] = null;
+ log("Type/value for null is " + typeof localStorage.g + "/" + localStorage.g);
+ localStorage['h'] = 0;
+ log("Type/value for 0 is " + typeof localStorage.h + "/" + localStorage.h);
+ localStorage['i'] = function(){};
+ log("Type/value for function(){} is " + typeof localStorage.i + "/" + localStorage.i);
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test case verifies that local storage only stores strings.
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/localstorage/window-open-expected.txt b/LayoutTests/storage/domstorage/localstorage/window-open-expected.txt
new file mode 100644
index 0000000..9a53f26
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/window-open-expected.txt
@@ -0,0 +1,8 @@
+This is a test to make sure the localStorage object for multiple windows in the same security origin share the same global storage area.
+Value for FOO is BAR
+Logging from second window:
+Value for FOO is BAR
+Value for FOO after changing my own copy is BAR-NEWWINDOW
+Value for FOO in my opening window is BAR-NEWWINDOW
+
+
diff --git a/LayoutTests/storage/domstorage/localstorage/window-open.html b/LayoutTests/storage/domstorage/localstorage/window-open.html
new file mode 100644
index 0000000..b390881
--- /dev/null
+++ b/LayoutTests/storage/domstorage/localstorage/window-open.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<script src="resources/clearLocalStorage.js"></script>
+<script>
+
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.setCanOpenWindows();
+ layoutTestController.waitUntilDone();
+}
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.localStorage) {
+ log("window.localStorage DOES NOT exist");
+ return;
+ }
+
+ window.log = log;
+
+ window.localStorage.setItem("FOO", "BAR");
+ log("Value for FOO is " + window.localStorage.getItem("FOO"));
+ window.open("resources/window-open-second.html");
+
+
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a test to make sure the localStorage object for multiple windows in the same security origin share the same global storage area.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/quota-expected.txt b/LayoutTests/storage/domstorage/quota-expected.txt
new file mode 100644
index 0000000..248ff79
--- /dev/null
+++ b/LayoutTests/storage/domstorage/quota-expected.txt
@@ -0,0 +1,32 @@
+Test the DOM Storage quota code.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+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 another bucket.h
+PASS Insertion worked.
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+Creating 'data' which contains 64K of data
+PASS data.length is 65536
+Putting 'data' into 39 localStorage buckets.
+Putting 'data' into another bucket.h
+PASS Hit exception as expected
+Verify that data was never inserted.
+PASS storage.getItem(39) is null
+Removing bucket 38.
+Adding 'Hello!' into a new bucket.
+PASS Insertion worked.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/domstorage/quota.html b/LayoutTests/storage/domstorage/quota.html
new file mode 100644
index 0000000..58a891b
--- /dev/null
+++ b/LayoutTests/storage/domstorage/quota.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/quota.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/remove-item-expected.txt b/LayoutTests/storage/domstorage/remove-item-expected.txt
new file mode 100644
index 0000000..8a3b391
--- /dev/null
+++ b/LayoutTests/storage/domstorage/remove-item-expected.txt
@@ -0,0 +1,65 @@
+Test .removeItem within DOM Storage.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Testing sessionStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS storage.foo1 is undefined.
+storage.foo1 = 'bar'
+PASS storage.foo1 is "bar"
+storage.removeItem('foo1')
+PASS storage.foo1 is undefined.
+storage.removeItem('foo1')
+PASS storage.foo1 is undefined.
+
+PASS storage['foo2'] is undefined.
+storage['foo2'] = 'bar'
+PASS storage['foo2'] is "bar"
+storage.removeItem('foo2')
+PASS storage['foo2'] is undefined.
+storage.removeItem('foo2')
+PASS storage['foo2'] is undefined.
+
+PASS storage.getItem('foo3') is null
+storage.setItem('foo3', 'bar')
+PASS storage.getItem('foo3') is "bar"
+storage.removeItem('foo3')
+PASS storage.getItem('foo3') is null
+storage.removeItem('foo3')
+PASS storage.getItem('foo3') is null
+
+
+Testing localStorage
+storage.clear()
+PASS storage.length is 0
+
+PASS storage.foo1 is undefined.
+storage.foo1 = 'bar'
+PASS storage.foo1 is "bar"
+storage.removeItem('foo1')
+PASS storage.foo1 is undefined.
+storage.removeItem('foo1')
+PASS storage.foo1 is undefined.
+
+PASS storage['foo2'] is undefined.
+storage['foo2'] = 'bar'
+PASS storage['foo2'] is "bar"
+storage.removeItem('foo2')
+PASS storage['foo2'] is undefined.
+storage.removeItem('foo2')
+PASS storage['foo2'] is undefined.
+
+PASS storage.getItem('foo3') is null
+storage.setItem('foo3', 'bar')
+PASS storage.getItem('foo3') is "bar"
+storage.removeItem('foo3')
+PASS storage.getItem('foo3') is null
+storage.removeItem('foo3')
+PASS storage.getItem('foo3') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/domstorage/remove-item.html b/LayoutTests/storage/domstorage/remove-item.html
new file mode 100644
index 0000000..f9f6ae3
--- /dev/null
+++ b/LayoutTests/storage/domstorage/remove-item.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/remove-item.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/script-tests/TEMPLATE.html b/LayoutTests/storage/domstorage/script-tests/TEMPLATE.html
new file mode 100644
index 0000000..239a794
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/TEMPLATE.html
@@ -0,0 +1,12 @@
+<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>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="YOUR_JS_FILE_HERE"></script>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/script-tests/clear.js b/LayoutTests/storage/domstorage/script-tests/clear.js
new file mode 100644
index 0000000..bf6e5f3
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/clear.js
@@ -0,0 +1,34 @@
+description("Test basic dom storage .clear() functionality.");
+
+function test(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ evalAndLog("storage['FOO'] = 'MyFOO'");
+ evalAndLog("storage['BAR'] = 'MyBar'");
+ shouldBe("storage.length", "2");
+ shouldBeEqualToString("storage['FOO']", "MyFOO");
+ shouldBeEqualToString("storage['BAR']", "MyBar");
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+ shouldBe("storage['FOO']", "undefined"); // FIXME: Wait...shouldn't this be null?
+ shouldBe("storage['BAR']", "undefined"); // ditto
+}
+
+test("sessionStorage");
+debug("");
+debug("");
+test("localStorage");
+
+window.successfullyParsed = true;
+isSuccessfullyParsed();
diff --git a/LayoutTests/storage/domstorage/script-tests/complex-keys.js b/LayoutTests/storage/domstorage/script-tests/complex-keys.js
new file mode 100644
index 0000000..2b6919f
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/complex-keys.js
@@ -0,0 +1,152 @@
+description("Test dom storage with many different types of keys (as opposed to values)");
+
+function test(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("");
+ shouldBeNull("storage.getItem('FOO')");
+ evalAndLog("storage.setItem('FOO', 'BAR')");
+ shouldBe("storage.length", "1");
+
+ shouldBeEqualToString("storage.getItem('FOO')", "BAR");
+ shouldBeNull("storage.getItem('foo')");
+ shouldBeUndefined("storage.foo");
+ shouldBeUndefined("storage['foo']");
+
+ evalAndLog("storage.foo = 'x'");
+ shouldBeEqualToString("storage.foo", "x");
+ shouldBeEqualToString("storage['foo']", "x");
+ shouldBeEqualToString("storage.getItem('foo')", "x");
+ evalAndLog("storage['foo'] = 'y'");
+ shouldBeEqualToString("storage.foo", "y");
+ shouldBeEqualToString("storage['foo']", "y");
+ shouldBeEqualToString("storage.getItem('foo')", "y");
+ evalAndLog("storage.setItem('foo', 'z')");
+ shouldBeEqualToString("storage.foo", "z");
+ shouldBeEqualToString("storage['foo']", "z");
+ shouldBeEqualToString("storage.getItem('foo')", "z");
+ shouldBe("storage.length", "2");
+
+ debug("");
+ debug("Testing a null key");
+ evalAndLog("storage.setItem(null, 'asdf')");
+ shouldBeEqualToString("storage.getItem('null')", "asdf");
+ shouldBeEqualToString("storage.getItem(null)", "asdf");
+ shouldBeEqualToString("storage['null']", "asdf");
+ shouldBeEqualToString("storage[null]", "asdf");
+ shouldBe("storage.length", "3");
+
+ evalAndLog("storage[null] = 1");
+ shouldBeEqualToString("storage.getItem(null)", "1");
+ evalAndLog("storage['null'] = 2");
+ shouldBeEqualToString("storage.getItem(null)", "2");
+ evalAndLog("storage.setItem('null', 3)");
+ shouldBeEqualToString("storage.getItem(null)", "3");
+ shouldBe("storage.length", "3");
+
+ debug("");
+ debug("Testing an undefined key");
+ evalAndLog("storage[undefined] = 'xyz'");
+ shouldBeEqualToString("storage.getItem('undefined')", "xyz");
+ shouldBeEqualToString("storage.getItem(undefined)", "xyz");
+ shouldBeEqualToString("storage['undefined']", "xyz");
+ shouldBeEqualToString("storage[undefined]", "xyz");
+ shouldBe("storage.length", "4");
+
+ evalAndLog("storage['undefined'] = 4");
+ shouldBeEqualToString("storage.getItem(undefined)", "4");
+ evalAndLog("storage.setItem(undefined, 5)");
+ shouldBeEqualToString("storage.getItem(undefined)", "5");
+ evalAndLog("storage.setItem('undefined', 6)");
+ shouldBeEqualToString("storage.getItem(undefined)", "6");
+ shouldBe("storage.length", "4");
+
+ debug("");
+ debug("Testing a numeric key");
+ evalAndLog("storage['2'] = 'ppp'");
+ shouldBeEqualToString("storage.getItem('2')", "ppp");
+ shouldBeEqualToString("storage.getItem(2)", "ppp");
+ shouldBeEqualToString("storage['2']", "ppp");
+ shouldBeEqualToString("storage[2]", "ppp");
+ shouldBe("storage.length", "5");
+
+ evalAndLog("storage[2] = 7");
+ shouldBeEqualToString("storage.getItem(2)", "7");
+ evalAndLog("storage.setItem(2, 8)");
+ shouldBeEqualToString("storage.getItem(2)", "8");
+ evalAndLog("storage.setItem('2', 9)");
+ shouldBeEqualToString("storage.getItem(2)", "9");
+ shouldBe("storage.length", "5");
+
+ debug("");
+ debug("Setting a non-ascii string to foo");
+ k = String.fromCharCode(255425) + String.fromCharCode(255) + String.fromCharCode(2554252321) + String.fromCharCode(0) + 'hello';
+ evalAndLog("storage[k] = 'hello'");
+ shouldBeEqualToString("storage.getItem(k)", "hello");
+ shouldBeEqualToString("storage[k]", "hello");
+ shouldBe("storage.length", "6");
+
+ debug("");
+ debug("Testing case differences");
+ evalAndLog("storage.foo1 = 'lower1'");
+ evalAndLog("storage.FOO1 = 'UPPER1'");
+ evalAndLog("storage['foo2'] = 'lower2'");
+ evalAndLog("storage['FOO2'] = 'UPPER2'");
+ evalAndLog("storage.setItem('foo3', 'lower3')");
+ evalAndLog("storage.setItem('FOO3', 'UPPER3')");
+ shouldBeEqualToString("storage.foo1", "lower1");
+ shouldBeEqualToString("storage.FOO1", "UPPER1");
+ shouldBeEqualToString("storage['foo2']", "lower2");
+ shouldBeEqualToString("storage['FOO2']", "UPPER2");
+ shouldBeEqualToString("storage.getItem('foo3')", "lower3");
+ shouldBeEqualToString("storage.getItem('FOO3')", "UPPER3");
+ shouldBe("storage.length", "12");
+
+
+ debug("");
+ debug("Testing overriding length");
+ shouldBe("storage.length", "12");
+ shouldBe("storage['length']", "12");
+ shouldBeNull("storage.getItem('length')");
+
+ evalAndLog("storage.length = 0");
+ shouldBe("storage.length", "12");
+ shouldBe("storage['length']", "12");
+ shouldBeNull("storage.getItem('length')");
+
+ evalAndLog("storage['length'] = 0");
+ shouldBe("storage.length", "12");
+ shouldBe("storage['length']", "12");
+ shouldBeNull("storage.getItem('length')");
+
+ evalAndLog("storage.setItem('length', 0)");
+ shouldBe("storage.length", "13");
+ shouldBe("storage['length']", "13");
+ shouldBeEqualToString("storage.getItem('length')", "0");
+
+ evalAndLog("storage.removeItem('length')");
+ shouldBe("storage.length", "12");
+ shouldBe("storage['length']", "12");
+ shouldBeNull("storage.getItem('length')");
+
+ evalAndLog("storage.setItem('length', 0)");
+ shouldBe("storage.length", "13");
+}
+
+test("sessionStorage");
+debug("");
+debug("");
+test("localStorage");
+
+window.successfullyParsed = true;
+isSuccessfullyParsed();
diff --git a/LayoutTests/storage/domstorage/script-tests/complex-values.js b/LayoutTests/storage/domstorage/script-tests/complex-values.js
new file mode 100644
index 0000000..8c6e29d
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/complex-values.js
@@ -0,0 +1,79 @@
+description("Test some corner case DOM Storage values.");
+
+function testKeyValue(key, value)
+{
+ keyString = "storage['" + key + "']";
+ shouldBeEqualToString("typeof " + keyString, "string");
+ shouldBeEqualToString(keyString, value);
+
+ keyString = "storage." + key;
+ shouldBeEqualToString("typeof " + keyString, "string");
+ shouldBeEqualToString(keyString, value);
+
+ keyString = "storage.getItem('" + key + "')";
+ shouldBeEqualToString("typeof " + keyString, "string");
+ shouldBeEqualToString(keyString, value);
+}
+
+function test(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("");
+ shouldBeEqualToString("typeof storage['foo']", "undefined");
+ shouldBeUndefined("storage['foo']");
+ shouldBeEqualToString("typeof storage.foo", "undefined");
+ shouldBeUndefined("storage.foo");
+ shouldBeEqualToString("typeof storage.getItem('foo')", "object");
+ shouldBeNull("storage.getItem('foo')");
+
+ debug("");
+ evalAndLog("storage.foo1 = null");
+ testKeyValue("foo1", "null");
+ evalAndLog("storage['foo2'] = null");
+ testKeyValue("foo2", "null");
+ evalAndLog("storage.setItem('foo3', null)");
+ testKeyValue("foo3", "null");
+
+ debug("");
+ evalAndLog("storage.foo4 = undefined");
+ testKeyValue("foo4", "undefined");
+ evalAndLog("storage['foo5'] = undefined");
+ testKeyValue("foo5", "undefined");
+ evalAndLog("storage.setItem('foo6', undefined)");
+ testKeyValue("foo6", "undefined");
+
+ debug("");
+ evalAndLog("storage.foo7 = 2");
+ testKeyValue("foo7", "2");
+ evalAndLog("storage['foo8'] = 2");
+ testKeyValue("foo8", "2");
+ evalAndLog("storage.setItem('foo9', 2)");
+ testKeyValue("foo9", "2");
+
+ debug("");
+ k = String.fromCharCode(255425) + String.fromCharCode(255) + String.fromCharCode(2554252321) + String.fromCharCode(0) + 'hello';
+ evalAndLog("storage.foo10 = k");
+ testKeyValue("foo10", k);
+ evalAndLog("storage['foo11'] = k");
+ testKeyValue("foo11", k);
+ evalAndLog("storage.setItem('foo12', k)");
+ testKeyValue("foo12", k);
+}
+
+test("sessionStorage");
+debug("");
+debug("");
+test("localStorage");
+
+window.successfullyParsed = true;
+isSuccessfullyParsed();
diff --git a/LayoutTests/storage/domstorage/script-tests/quota.js b/LayoutTests/storage/domstorage/script-tests/quota.js
new file mode 100644
index 0000000..ad9afe9
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/quota.js
@@ -0,0 +1,87 @@
+description("Test the DOM Storage quota code.");
+
+function testQuota(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("Creating 'data' which contains 64K of data");
+ data = "X";
+ for (var i=0; i<16; i++)
+ data += data;
+ shouldBe("data.length", "65536");
+
+ 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[39] = data;
+ testFailed("Did not hit quota error.");
+ } catch (e) {
+ testPassed("Hit exception as expected");
+ }
+
+ debug("Verify that data was never inserted.");
+ shouldBeNull("storage.getItem(39)");
+
+ debug("Removing bucket 38.");
+ storage.removeItem('38');
+
+ debug("Adding 'Hello!' into a new bucket.");
+ try {
+ storage['foo'] = "Hello!";
+ testPassed("Insertion worked.");
+ } catch (e) {
+ testFailed("Exception: " + e);
+ }
+}
+
+function testNoQuota(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("Creating 'data' which contains 64K of data");
+ data = "X";
+ for (var i=0; i<16; i++)
+ data += data;
+ shouldBe("data.length", "65536");
+
+ debug("Putting 'data' into 40 " + storageString + " buckets.");
+ for (var i=0; i<40; i++)
+ storage[i] = data;
+
+ debug("Putting 'data' into another bucket.h");
+ try {
+ storage[40] = data;
+ testPassed("Insertion worked.");
+ } catch (e) {
+ testFailed("Exception: " + e);
+ }
+}
+
+testNoQuota("sessionStorage");
+debug("");
+debug("");
+testQuota("localStorage");
+
+window.successfullyParsed = true;
+isSuccessfullyParsed();
diff --git a/LayoutTests/storage/domstorage/script-tests/remove-item.js b/LayoutTests/storage/domstorage/script-tests/remove-item.js
new file mode 100644
index 0000000..62731f1
--- /dev/null
+++ b/LayoutTests/storage/domstorage/script-tests/remove-item.js
@@ -0,0 +1,50 @@
+description("Test .removeItem within DOM Storage.");
+
+function test(storageString)
+{
+ storage = eval(storageString);
+ if (!storage) {
+ testFailed(storageString + " DOES NOT exist");
+ return;
+ }
+
+ debug("Testing " + storageString);
+
+ evalAndLog("storage.clear()");
+ shouldBe("storage.length", "0");
+
+ debug("");
+ shouldBeUndefined("storage.foo1");
+ evalAndLog("storage.foo1 = 'bar'");
+ shouldBeEqualToString("storage.foo1", "bar");
+ evalAndLog("storage.removeItem('foo1')");
+ shouldBeUndefined("storage.foo1");
+ evalAndLog("storage.removeItem('foo1')");
+ shouldBeUndefined("storage.foo1");
+
+ debug("");
+ shouldBeUndefined("storage['foo2']");
+ evalAndLog("storage['foo2'] = 'bar'");
+ shouldBeEqualToString("storage['foo2']", "bar");
+ evalAndLog("storage.removeItem('foo2')");
+ shouldBeUndefined("storage['foo2']");
+ evalAndLog("storage.removeItem('foo2')");
+ shouldBeUndefined("storage['foo2']");
+
+ debug("");
+ shouldBeNull("storage.getItem('foo3')");
+ evalAndLog("storage.setItem('foo3', 'bar')");
+ shouldBeEqualToString("storage.getItem('foo3')", "bar");
+ evalAndLog("storage.removeItem('foo3')");
+ shouldBeNull("storage.getItem('foo3')");
+ evalAndLog("storage.removeItem('foo3')");
+ shouldBeNull("storage.getItem('foo3')");
+}
+
+test("sessionStorage");
+debug("");
+debug("");
+test("localStorage");
+
+window.successfullyParsed = true;
+isSuccessfullyParsed();
diff --git a/LayoutTests/storage/domstorage/sessionstorage/delete-removal-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/delete-removal-expected.txt
new file mode 100644
index 0000000..ce04017
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/delete-removal-expected.txt
@@ -0,0 +1,13 @@
+This test makes sure that using the syntax `delete sessionStorage.keyName` works as an alias to `sessionStorage.removeItem(keyName).`
+foo (before anything) is: undefined
+foo (after a named property setter set) is: bar
+foo (after a delete) is: undefined
+foo (after an indexed setter set) is: bar
+foo (after deleting FOO (not foo)) is : bar
+foo (after a delete) is: undefined
+foo (after calling setItem) is: bar
+foo (after a delete) is: undefined
+foo (after a redundant delete) is: undefined
+foo (after a named property setter set) is: bar
+foo (after an indexed delete) is: undefined
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/delete-removal.html b/LayoutTests/storage/domstorage/sessionstorage/delete-removal.html
new file mode 100644
index 0000000..7a4b8d2
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/delete-removal.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById('logger').appendChild(document.createTextNode(a));
+ document.getElementById('logger').appendChild(document.createElement("br"));
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ log("foo (before anything) is: " + sessionStorage.foo);
+ sessionStorage.foo = "bar";
+ log("foo (after a named property setter set) is: " + sessionStorage.foo);
+ delete sessionStorage.foo;
+ log("foo (after a delete) is: " + sessionStorage.foo);
+ sessionStorage["foo"] = "bar";
+ log("foo (after an indexed setter set) is: " + sessionStorage.foo);
+ delete sessionStorage.FOO;
+ log("foo (after deleting FOO (not foo)) is : " + sessionStorage.foo);
+ delete sessionStorage.foo;
+ log("foo (after a delete) is: " + sessionStorage.foo);
+ sessionStorage.setItem("foo", "bar");
+ log("foo (after calling setItem) is: " + sessionStorage.foo);
+ delete sessionStorage.foo;
+ log("foo (after a delete) is: " + sessionStorage.foo);
+ delete sessionStorage.foo;
+ log("foo (after a redundant delete) is: " + sessionStorage.foo);
+ sessionStorage.foo = "bar";
+ log("foo (after a named property setter set) is: " + sessionStorage.foo);
+ delete sessionStorage["foo"];
+ log("foo (after an indexed delete) is: " + sessionStorage.foo);
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test makes sure that using the syntax `delete sessionStorage.keyName` works as an alias to `sessionStorage.removeItem(keyName).`<br><hr>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage-expected.txt
new file mode 100644
index 0000000..7c87325
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage-expected.txt
@@ -0,0 +1,9 @@
+This test checks to see that you can enumber a Storage object and get only the keys as a result. The built-in properties of the Storage object should be ignored. The test operates on the SessionStorage object.
+alpha
+bar
+batman
+foo
+fu
+prototypeTestKey
+zeta
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage.html b/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage.html
new file mode 100644
index 0000000..7321f9a
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/enumerate-storage.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function startTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ Storage.prototype.prototypeTestKey = "prototypeTestValue";
+ sessionStorage.foo = "bar";
+ sessionStorage.fu = "baz";
+ sessionStorage.batman = "bin suparman";
+ sessionStorage.bar = "foo";
+ sessionStorage.alpha = "beta";
+ sessionStorage.zeta = "gamma";
+
+ // Enumerate sessionStorage, appending each key onto an array
+ var enumeratedArray = new Array();
+ for (var n in sessionStorage)
+ enumeratedArray.push(n);
+
+ // Sort the array, since the storage order isn't guaranteed
+ enumeratedArray.sort();
+
+ for (var n in enumeratedArray)
+ log(enumeratedArray[n]);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+This test checks to see that you can enumber a Storage object and get only the keys as a result. The built-in properties of the Storage object should be ignored. The test operates on the SessionStorage object.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key-expected.txt
new file mode 100644
index 0000000..f879759
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key-expected.txt
@@ -0,0 +1,9 @@
+This test attempts to enumerate all the keys in sessionStorage with .length + .key(). The built-in properties of the Storage object should be ignored. The test operates on the sessionStorage object.
+alpha
+bar
+batman
+foo
+fu
+prototypeTestKey
+zeta
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key.html b/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key.html
new file mode 100644
index 0000000..27266f1
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/enumerate-with-length-and-key.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function startTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ Storage.prototype.prototypeTestKey = "prototypeTestValue";
+ sessionStorage.foo = "bar";
+ sessionStorage.fu = "baz";
+ sessionStorage.batman = "bin suparman";
+ sessionStorage.bar = "foo";
+ sessionStorage.alpha = "beta";
+ sessionStorage.zeta = "gamma";
+
+ // Enumerate sessionStorage, appending each key onto an array
+ var enumeratedArray = new Array();
+ for (var n in sessionStorage)
+ enumeratedArray.push(n);
+
+ // Sort the array, since the storage order isn't guaranteed
+ enumeratedArray.sort();
+
+ for (var n in enumeratedArray)
+ log(enumeratedArray[n]);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+This test attempts to enumerate all the keys in sessionStorage with .length + .key(). The built-in properties of the Storage object should be ignored. The test operates on the sessionStorage object.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set-expected.txt
new file mode 100644
index 0000000..ff66bdc
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set-expected.txt
@@ -0,0 +1,32 @@
+This is a test to make sure you can get and set values in SessionStorage by index.
+Setting FOO using the index setter.
+Reading FOO:
+BAR
+BAR
+BAR
+
+Setting FOO again, using setItem.
+Reading FOO:
+BAZ
+BAZ
+BAZ
+
+Setting FOO again, using the index setter.
+Reading FOO:
+BAT
+BAT
+BAT
+
+Setting FOO again, using property-slot syntax
+Reading FOO:
+BATMAN
+BATMAN
+BATMAN
+
+Removing FOO, then trying to read it
+Reading FOO:
+undefined
+undefined
+null
+
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set.html b/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set.html
new file mode 100644
index 0000000..7922d0b
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/index-get-and-set.html
@@ -0,0 +1,79 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function finish()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone()
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ finish();
+ return;
+ }
+
+ log("Setting FOO using the index setter.");
+ sessionStorage["FOO"] = "BAR";
+ log("Reading FOO:");
+ log(sessionStorage.FOO);
+ log(sessionStorage["FOO"]);
+ log(sessionStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using setItem.");
+ sessionStorage.setItem("FOO", "BAZ");
+ log("Reading FOO:");
+ log(sessionStorage.FOO);
+ log(sessionStorage["FOO"]);
+ log(sessionStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using the index setter.");
+ sessionStorage["FOO"] = "BAT";
+ log("Reading FOO:");
+ log(sessionStorage.FOO);
+ log(sessionStorage["FOO"]);
+ log(sessionStorage.getItem("FOO"));
+ log("");
+
+ log("Setting FOO again, using property-slot syntax");
+ sessionStorage.FOO = "BATMAN";
+ log("Reading FOO:");
+ log(sessionStorage.FOO);
+ log(sessionStorage["FOO"]);
+ log(sessionStorage.getItem("FOO"));
+ log("");
+
+ log("Removing FOO, then trying to read it");
+ sessionStorage.removeItem("FOO");
+ log("Reading FOO:");
+ log(sessionStorage.FOO);
+ log(sessionStorage["FOO"]);
+ log(sessionStorage.getItem("FOO"));
+ log("");
+
+ finish();
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a test to make sure you can get and set values in SessionStorage by index.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage-expected.txt
new file mode 100644
index 0000000..ed2017a
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage-expected.txt
@@ -0,0 +1,7 @@
+This is a test to make sure that when private browsing is on any attempt to change the sessionStorage area fail.
+Initial value of testItem is: InitialValue
+Caught exception trying to change item: Error: QUOTA_EXCEEDED_ERR: DOM Exception 22
+After change attempt, testItem is: InitialValue
+After remove attempt, testItem is: InitialValue
+After clear attempt, testItem is: InitialValue
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage.html b/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage.html
new file mode 100644
index 0000000..8dd7a6a
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/private-browsing-affects-storage.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+ if (!window.layoutTestController) {
+ log("Test only designed to be run under DumpRenderTree");
+ return;
+ }
+
+ sessionStorage.setItem("testItem", "InitialValue");
+ layoutTestController.setPrivateBrowsingEnabled(true);
+ log("Initial value of testItem is: " + sessionStorage.getItem("testItem"));
+
+ try {
+ sessionStorage.setItem("testItem", "ChangedValue");
+ } catch(e) {
+ log("Caught exception trying to change item: " + e);
+ }
+
+ log("After change attempt, testItem is: " + sessionStorage.getItem("testItem"));
+
+ sessionStorage.removeItem("testItem");
+ log("After remove attempt, testItem is: " + sessionStorage.getItem("testItem"));
+
+ sessionStorage.clear();
+ log("After clear attempt, testItem is: " + sessionStorage.getItem("testItem"));
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a test to make sure that when private browsing is on any attempt to change the sessionStorage area fail.
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/resources/clearSessionStorage.js b/LayoutTests/storage/domstorage/sessionstorage/resources/clearSessionStorage.js
new file mode 100644
index 0000000..489ef2a
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/resources/clearSessionStorage.js
@@ -0,0 +1,12 @@
+function clearSessionStorage()
+{
+ var keys = new Array();
+ for (key in sessionStorage)
+ keys.push(key);
+
+ for (key in keys)
+ sessionStorage.removeItem(keys[key]);
+}
+
+if (window.sessionStorage)
+ clearSessionStorage();
diff --git a/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html b/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html
new file mode 100644
index 0000000..2d7b004
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/resources/iframe-events-second.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script>
+function handleStorageEvent(e)
+{
+ parent.log("Subframe received storage event:");
+ 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("Storage Area - " + ((e.storageArea == window.sessionStorage) ? "This window's window.sessionStorage" : "Another window's window.sessionStorage"));
+ parent.log("");
+
+ if (e.key != "Subframe") {
+ parent.log("Subframe about to change sessionStorage...");
+ sessionStorage.setItem("Subframe", "SET");
+ }
+}
+</script>
+</head>
+<body onload="window.addEventListener('storage', handleStorageEvent, false);">
+This is the subframe which exists to make sure that both frames of a same security origin receive the event for that origin's sessionStorage object mutating
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/resources/window-open-second.html b/LayoutTests/storage/domstorage/sessionstorage/resources/window-open-second.html
new file mode 100644
index 0000000..b8dede6
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/resources/window-open-second.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script>
+
+var secondWindowLog = "Logging from second window:<br>";
+
+function log(a)
+{
+ secondWindowLog += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+ window.sessionStorage.setItem("FOO", "BAR-NEWWINDOW");
+ log("Value for FOO after changing my own copy is " + window.sessionStorage.getItem("FOO"));
+
+ log("Value for FOO in my opening window is " + window.opener.sessionStorage.getItem("FOO"));
+
+ window.opener.log(secondWindowLog);
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/simple-usage-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/simple-usage-expected.txt
new file mode 100644
index 0000000..399fd08
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/simple-usage-expected.txt
@@ -0,0 +1,13 @@
+This test trys simple operations on SessionStorage
+Length is 0
+Value for FOO is null
+Length is 1
+Value for FOO is BAR
+Key for index 0 is FOO
+Key for index 1 is null
+Key for index -1 is null
+Length is 1
+Value for FOO is BAZ
+Length is 0
+Value for FOO is null
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/simple-usage.html b/LayoutTests/storage/domstorage/sessionstorage/simple-usage.html
new file mode 100644
index 0000000..8e45521
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/simple-usage.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ log("Length is " + window.sessionStorage.length);
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+
+ window.sessionStorage.setItem("FOO", "BAR");
+
+ log("Length is " + window.sessionStorage.length);
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+ log("Key for index 0 is " + window.sessionStorage.key(0));
+ log("Key for index 1 is " + window.sessionStorage.key(1));
+ log("Key for index -1 is " + window.sessionStorage.key(-1));
+
+ window.sessionStorage.setItem("FOO", "BAZ");
+
+ log("Length is " + window.sessionStorage.length);
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+
+ window.sessionStorage.removeItem("FOO");
+
+ log("Length is " + window.sessionStorage.length);
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test trys simple operations on SessionStorage<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/string-conversion-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/string-conversion-expected.txt
new file mode 100644
index 0000000..65dcb2c
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/string-conversion-expected.txt
@@ -0,0 +1,15 @@
+This test case verifies that session storage only stores strings.
+Length is 0
+Testing implicit setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+Testing explicit setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+Testing index setters
+Type/value for null is string/null
+Type/value for 0 is string/0
+Type/value for function(){} is string/function () {}
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/string-conversion.html b/LayoutTests/storage/domstorage/sessionstorage/string-conversion.html
new file mode 100644
index 0000000..64a4e14
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/string-conversion.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ log("Length is " + sessionStorage.length);
+
+ log("Testing implicit setters");
+ sessionStorage.a = null;
+ log("Type/value for null is " + typeof sessionStorage.a + "/" + sessionStorage.a);
+ sessionStorage.b = 0;
+ log("Type/value for 0 is " + typeof sessionStorage.b + "/" + sessionStorage.b);
+ sessionStorage.c = function(){};
+ log("Type/value for function(){} is " + typeof sessionStorage.c + "/" + sessionStorage.c);
+
+ log("Testing explicit setters");
+ sessionStorage.setItem('d', null);
+ log("Type/value for null is " + typeof sessionStorage.d + "/" + sessionStorage.d);
+ sessionStorage.setItem('e', 0);
+ log("Type/value for 0 is " + typeof sessionStorage.e + "/" + sessionStorage.e);
+ sessionStorage.setItem('f', function(){});
+ log("Type/value for function(){} is " + typeof sessionStorage.f + "/" + sessionStorage.f);
+
+ log("Testing index setters");
+ sessionStorage['g'] = null;
+ log("Type/value for null is " + typeof sessionStorage.g + "/" + sessionStorage.g);
+ sessionStorage['h'] = 0;
+ log("Type/value for 0 is " + typeof sessionStorage.h + "/" + sessionStorage.h);
+ sessionStorage['i'] = function(){};
+ log("Type/value for function(){} is " + typeof sessionStorage.i + "/" + sessionStorage.i);
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test case verifies that session storage only stores strings.
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/sessionstorage/window-open-expected.txt b/LayoutTests/storage/domstorage/sessionstorage/window-open-expected.txt
new file mode 100644
index 0000000..c613414
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/window-open-expected.txt
@@ -0,0 +1,8 @@
+This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change
+Value for FOO is BAR
+Logging from second window:
+Value for FOO is BAR
+Value for FOO after changing my own copy is BAR-NEWWINDOW
+Value for FOO in my opening window is BAR
+
+
diff --git a/LayoutTests/storage/domstorage/sessionstorage/window-open.html b/LayoutTests/storage/domstorage/sessionstorage/window-open.html
new file mode 100644
index 0000000..34f8f39
--- /dev/null
+++ b/LayoutTests/storage/domstorage/sessionstorage/window-open.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<script src="resources/clearSessionStorage.js"></script>
+<script>
+
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.setCanOpenWindows();
+ layoutTestController.waitUntilDone();
+}
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function runTest()
+{
+ if (!window.sessionStorage) {
+ log("window.sessionStorage DOES NOT exist");
+ return;
+ }
+
+ window.log = log;
+
+ window.sessionStorage.setItem("FOO", "BAR");
+ log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
+ window.open("resources/window-open-second.html");
+
+
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/domstorage/window-attributes-exist-expected.txt b/LayoutTests/storage/domstorage/window-attributes-exist-expected.txt
new file mode 100644
index 0000000..e7668fd
--- /dev/null
+++ b/LayoutTests/storage/domstorage/window-attributes-exist-expected.txt
@@ -0,0 +1,21 @@
+This test checks to see if window.localStorage, window.sessionStorage and window.onstorage exist.
+window.sessionStorage exists
+Storage object sessionStorage has length
+Storage object sessionStorage has key
+Storage object sessionStorage has getItem
+Storage object sessionStorage has setItem
+Storage object sessionStorage has removeItem
+Storage object sessionStorage has clear
+window.sessionStorage == window.sessionStorage: true
+window.sessionStorage === window.sessionStorage: true
+window.localStorage exists
+Storage object localStorage has length
+Storage object localStorage has key
+Storage object localStorage has getItem
+Storage object localStorage has setItem
+Storage object localStorage has removeItem
+Storage object localStorage has clear
+window.localStorage == window.localStorage: true
+window.localStorage === window.localStorage: true
+window.onstorage exists
+
diff --git a/LayoutTests/storage/domstorage/window-attributes-exist.html b/LayoutTests/storage/domstorage/window-attributes-exist.html
new file mode 100644
index 0000000..d01ba7c
--- /dev/null
+++ b/LayoutTests/storage/domstorage/window-attributes-exist.html
@@ -0,0 +1,59 @@
+<html>
+<head>
+<script>
+
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+function log(a)
+{
+ document.getElementById("logger").innerHTML += a + "<br>";
+}
+
+function testStorage(name, storage)
+{
+ if ("length" in storage)
+ log("Storage object " + name + " has length");
+ if ("key" in storage)
+ log("Storage object " + name + " has key");
+ if ("getItem" in storage)
+ log("Storage object " + name + " has getItem");
+ if ("setItem" in storage)
+ log("Storage object " + name + " has setItem");
+ if ("removeItem" in storage)
+ log("Storage object " + name + " has removeItem");
+ if ("clear" in storage)
+ log("Storage object " + name + " has clear");
+}
+
+function runTest()
+{
+ if ("sessionStorage" in window) {
+ log("window.sessionStorage exists");
+ testStorage("sessionStorage", window.sessionStorage);
+ log("window.sessionStorage == window.sessionStorage: " + (window.sessionStorage == window.sessionStorage));
+ log("window.sessionStorage === window.sessionStorage: " + (window.sessionStorage === window.sessionStorage));
+ } else
+ log("window.sessionStorage DOES NOT exist");
+
+ if ("localStorage" in window) {
+ log("window.localStorage exists");
+ testStorage("localStorage", window.localStorage);
+ log("window.localStorage == window.localStorage: " + (window.localStorage == window.localStorage));
+ log("window.localStorage === window.localStorage: " + (window.localStorage === window.localStorage));
+ } else
+ log("window.localStorage DOES NOT exist");
+
+ if ("onstorage" in window)
+ log("window.onstorage exists");
+ else
+ log("window.onstorage DOES NOT exist");
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test checks to see if window.localStorage, window.sessionStorage and window.onstorage exist.<br>
+<div id="logger"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/empty-statement-expected.txt b/LayoutTests/storage/empty-statement-expected.txt
new file mode 100644
index 0000000..78d9dca
--- /dev/null
+++ b/LayoutTests/storage/empty-statement-expected.txt
@@ -0,0 +1,2 @@
+Executed an empty statement. If you didn't see a crash or assertion, the test passed.
+
diff --git a/LayoutTests/storage/empty-statement.html b/LayoutTests/storage/empty-statement.html
new file mode 100644
index 0000000..36ec5b8
--- /dev/null
+++ b/LayoutTests/storage/empty-statement.html
@@ -0,0 +1,36 @@
+<html>
+
+<head>
+<script>
+
+function writeMessageToLog(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+function executeEmptyStatement(transaction)
+{
+ transaction.executeSql("");
+ writeMessageToLog("Executed an empty statement. If you didn't see a crash or assertion, the test passed.");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+ var db = openDatabase("EmptyStatementTest", "1.0", "Database for an empty statement test", 1);
+ db.transaction(executeEmptyStatement);
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<pre id="console"></pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/execute-sql-args-expected.txt b/LayoutTests/storage/execute-sql-args-expected.txt
new file mode 100644
index 0000000..0e30b4b
--- /dev/null
+++ b/LayoutTests/storage/execute-sql-args-expected.txt
@@ -0,0 +1,29 @@
+PASS. executeSql(null) did not throw an exception
+PASS. executeSql(undefined) did not throw an exception
+PASS. executeSql(0) did not throw an exception
+PASS. executeSql("") did not throw an exception
+PASS. executeSql("", null) did not throw an exception
+PASS. executeSql("", undefined) did not throw an exception
+PASS. executeSql("", []) did not throw an exception
+PASS. executeSql("", [ "arg0" ]) did not throw an exception
+PASS. executeSql("", { }) did not throw an exception
+PASS. executeSql("", { length: 0 }) did not throw an exception
+PASS. executeSql("", { length: 1, 0: "arg0" }) did not throw an exception
+PASS. executeSql("", null, null) did not throw an exception
+PASS. executeSql("", null, undefined) did not throw an exception
+PASS. executeSql("", null, { }) did not throw an exception
+PASS. executeSql("", null, null, null) did not throw an exception
+PASS. executeSql("", null, null, undefined) did not throw an exception
+PASS. executeSql("", null, null, { }) did not throw an exception
+PASS. executeSql() threw an exception as expected.
+PASS. executeSql(throwOnToStringObject) threw an exception as expected.
+PASS. executeSql("", throwOnGetLengthObject) threw an exception as expected.
+PASS. executeSql("", throwOnGetZeroObject) threw an exception as expected.
+PASS. executeSql("", [ throwOnToStringObject ]) threw an exception as expected.
+PASS. executeSql("", 0) threw an exception as expected.
+PASS. executeSql("", "") threw an exception as expected.
+PASS. executeSql("", null, 0) threw an exception as expected.
+PASS. executeSql("", null, "") threw an exception as expected.
+PASS. executeSql("", null, null, 0) threw an exception as expected.
+PASS. executeSql("", null, null, "") threw an exception as expected.
+
diff --git a/LayoutTests/storage/execute-sql-args.html b/LayoutTests/storage/execute-sql-args.html
new file mode 100644
index 0000000..43814c6
--- /dev/null
+++ b/LayoutTests/storage/execute-sql-args.html
@@ -0,0 +1,109 @@
+<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>
+</head>
+
+<body onload="runTest()">
+<pre id="console"></pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/hash-change-with-xhr-expected.txt b/LayoutTests/storage/hash-change-with-xhr-expected.txt
new file mode 100644
index 0000000..a481746
--- /dev/null
+++ b/LayoutTests/storage/hash-change-with-xhr-expected.txt
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..9cbf276
--- /dev/null
+++ b/LayoutTests/storage/hash-change-with-xhr.html
@@ -0,0 +1,140 @@
+<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>
+</head>
+<body onload="runTest()">
+<div id="msgs"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/multiple-databases-garbage-collection-expected.txt b/LayoutTests/storage/multiple-databases-garbage-collection-expected.txt
new file mode 100644
index 0000000..d01514a
--- /dev/null
+++ b/LayoutTests/storage/multiple-databases-garbage-collection-expected.txt
@@ -0,0 +1,4 @@
+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.
+Forgotten Database Transaction Complete
+Persistent Database Transaction Complete
+
diff --git a/LayoutTests/storage/multiple-databases-garbage-collection.html b/LayoutTests/storage/multiple-databases-garbage-collection.html
new file mode 100644
index 0000000..45246a9
--- /dev/null
+++ b/LayoutTests/storage/multiple-databases-garbage-collection.html
@@ -0,0 +1,77 @@
+<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();">
+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>
+</body>
+</html>
diff --git a/LayoutTests/storage/multiple-transactions-expected.txt b/LayoutTests/storage/multiple-transactions-expected.txt
new file mode 100644
index 0000000..3e9c0f6
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions-expected.txt
@@ -0,0 +1,6 @@
+This is a test to see if the database API allows multiple transactions to be queued on the same database at once:
+Transaction 1 Started
+Transaction 1 Succeeded
+Transaction 2 Started
+Transaction 2 Succeeded
+
diff --git a/LayoutTests/storage/multiple-transactions-on-different-handles-expected.txt b/LayoutTests/storage/multiple-transactions-on-different-handles-expected.txt
new file mode 100644
index 0000000..ba6a2d1
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions-on-different-handles-expected.txt
@@ -0,0 +1,8 @@
+This is a test to see if queueing up multiple transactions on different handles to the same database results in a deadlock.
+db1 read statement succeeded
+db1 write statement succeeded
+db1 transaction succeeded
+db2 read statement succeeded
+db2 write statement succeeded
+db2 transaction succeeded
+
diff --git a/LayoutTests/storage/multiple-transactions-on-different-handles.html b/LayoutTests/storage/multiple-transactions-on-different-handles.html
new file mode 100644
index 0000000..bcdab82
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions-on-different-handles.html
@@ -0,0 +1,96 @@
+<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>
+</head>
+<body onload="runTest();">
+This is a test to see if queueing up multiple transactions on different handles to the same database results in a deadlock.<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/multiple-transactions.html b/LayoutTests/storage/multiple-transactions.html
new file mode 100644
index 0000000..56656ca
--- /dev/null
+++ b/LayoutTests/storage/multiple-transactions.html
@@ -0,0 +1,53 @@
+<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>
+<head>
+<body onload="runTest();">
+This is a test to see if the database API allows multiple transactions to be queued on the same database at once:<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/open-database-empty-version-expected.txt b/LayoutTests/storage/open-database-empty-version-expected.txt
new file mode 100644
index 0000000..32e2ffc
--- /dev/null
+++ b/LayoutTests/storage/open-database-empty-version-expected.txt
@@ -0,0 +1,2 @@
+This tests that calling openDatabase with an empty version string does not cause an exception to be thrown.
+SUCCESS! Did not throw an exception.
diff --git a/LayoutTests/storage/open-database-empty-version.html b/LayoutTests/storage/open-database-empty-version.html
new file mode 100644
index 0000000..4604113
--- /dev/null
+++ b/LayoutTests/storage/open-database-empty-version.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<script>
+function runTest() {
+ if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+
+ try {
+ var db = openDatabase('5562470Test', '', 'Test for <rdar://problem/5562470> openDatabase does not work when the version string is empty', 1);
+ } catch (e) {
+ document.getElementById('result').innerHTML = 'FAILURE'
+ }
+}
+</script>
+</head>
+<body onload="runTest()">
+<div>This tests that calling openDatabase with an empty version string does not cause an exception to be thrown.
+<div id="result">
+SUCCESS! Did not throw an exception.
+</div>
+</body>
+</html>
diff --git a/LayoutTests/storage/open-database-set-empty-version-expected.txt b/LayoutTests/storage/open-database-set-empty-version-expected.txt
new file mode 100644
index 0000000..fa2a450
--- /dev/null
+++ b/LayoutTests/storage/open-database-set-empty-version-expected.txt
@@ -0,0 +1,2 @@
+This tests that calling openDatabase with an empty version string sets the current version of that database to the empty string and subsequent attempts to open the database with a different expected version throw an exception.
+SUCCESS, an exception was thrown. Error: INVALID_STATE_ERR: DOM Exception 11
diff --git a/LayoutTests/storage/open-database-set-empty-version.html b/LayoutTests/storage/open-database-set-empty-version.html
new file mode 100644
index 0000000..f3607fd
--- /dev/null
+++ b/LayoutTests/storage/open-database-set-empty-version.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+<script>
+function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.clearAllDatabases();
+ }
+
+ try {
+ var db = openDatabase('28417Test', '', 'Test for bug 28417: openDatabase() with empty version sets db version up incorrectly', 0);
+ // The next openDatabase call should fail because the database version was set to '' by the call above, and now we are expecting a different version.
+ var db2 = openDatabase('28417Test', 'test', 'Test for bug 28417: openDatabase() with empty version sets db version up incorrectly', 0);
+ } catch (e) {
+ document.getElementById('result').innerHTML = 'SUCCESS, an exception was thrown. ' + e;
+ }
+}
+</script>
+</head>
+<body onload="runTest()">
+<div>This tests that calling openDatabase with an empty version string sets the current version of that database to the empty string and subsequent attempts to open the database with a different expected version throw an exception.</div>
+<div id="result">
+FAILURE - an exception was expected.
+</div>
+</body>
+</html>
diff --git a/LayoutTests/storage/open-database-while-transaction-in-progress-expected.txt b/LayoutTests/storage/open-database-while-transaction-in-progress-expected.txt
new file mode 100644
index 0000000..acac8af
--- /dev/null
+++ b/LayoutTests/storage/open-database-while-transaction-in-progress-expected.txt
@@ -0,0 +1,3 @@
+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.
+openDatabase() succeeded.
+
diff --git a/LayoutTests/storage/open-database-while-transaction-in-progress.html b/LayoutTests/storage/open-database-while-transaction-in-progress.html
new file mode 100644
index 0000000..691f6fe
--- /dev/null
+++ b/LayoutTests/storage/open-database-while-transaction-in-progress.html
@@ -0,0 +1,71 @@
+<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>
+</head>
+<body onload="runTest();">
+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>
+</body>
+</html>
diff --git a/LayoutTests/storage/private-browsing-readonly-expected.txt b/LayoutTests/storage/private-browsing-readonly-expected.txt
new file mode 100644
index 0000000..d5f4e0b
--- /dev/null
+++ b/LayoutTests/storage/private-browsing-readonly-expected.txt
@@ -0,0 +1,13 @@
+This test makes sure that attempts to change the database during private browsing fail.
+Setup statement 1 completed successfully
+Setup statement 2 completed successfully
+Private browsing statement 1 completed with an error
+not authorized
+Private browsing statement 2 completed with an error
+not authorized
+Private browsing statement 3 completed with an error
+not authorized
+Private browsing statement 4 completed with an error
+not authorized
+Test ended
+
diff --git a/LayoutTests/storage/private-browsing-readonly.html b/LayoutTests/storage/private-browsing-readonly.html
new file mode 100644
index 0000000..70a209a
--- /dev/null
+++ b/LayoutTests/storage/private-browsing-readonly.html
@@ -0,0 +1,100 @@
+<html>
+<head>
+<script>
+
+function writeMessageToLog(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+var setupStatements = [
+ "CREATE TABLE IF NOT EXISTS PrivateTest1 (randomData)",
+ "INSERT INTO PrivateTest1 VALUES ('somedata')"
+];
+
+var privateBrowsingStatements = [
+ "CREATE TABLE IF NOT EXISTS PrivateTest2 (randomData)",
+ "DELETE FROM PrivateTest1",
+ "DROP TABLE PrivateTest1",
+ "INSERT INTO PrivateTest1 VALUES ('somedata')"
+];
+
+var completed = 0;
+var theTransaction;
+
+function setupSuccessFunction(tx, result)
+{
+ ++completed;
+ writeMessageToLog("Setup statement " + completed + " completed successfully");
+ checkSetupComplete();
+}
+
+function setupErrorFunction(tx, error)
+{
+ ++completed;
+ writeMessageToLog("Setup statement " + completed + " completed with an error\n" + error.message);
+ checkSetupComplete();
+}
+
+function privateBrowsingSuccessFunction(tx, result)
+{
+ ++completed;
+ writeMessageToLog("Private browsing statement " + completed + " completed successfully");
+}
+
+function privateBrowsingErrorFunction(tx, error)
+{
+ ++completed;
+ writeMessageToLog("Private browsing statement " + completed + " completed with an error\n" + error.message);
+}
+
+function runSetup(transaction)
+{
+ theTransaction = transaction;
+ for (i in setupStatements)
+ theTransaction.executeSql(setupStatements[i], [], setupSuccessFunction, setupErrorFunction);
+}
+
+function checkSetupComplete()
+{
+ if (completed == setupStatements.length)
+ runPrivateBrowsingTests();
+}
+
+function runPrivateBrowsingTests()
+{
+ completed = 0;
+
+ if (window.layoutTestController)
+ layoutTestController.setPrivateBrowsingEnabled(true);
+
+ for (i in privateBrowsingStatements)
+ theTransaction.executeSql(privateBrowsingStatements[i], [], privateBrowsingSuccessFunction, privateBrowsingErrorFunction);
+}
+
+function endTest()
+{
+ writeMessageToLog("Test ended");
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ var database = openDatabase("PrivateBrowsingReadOnlyTest", "1.0", "Test private browsing read-only safety", 1);
+ database.transaction(runSetup, endTest, endTest);
+}
+
+</script>
+</head>
+<body onload="runTest();">
+This test makes sure that attempts to change the database during private browsing fail.<br>
+<div id="console"></div>
+</body>
+</html>
diff --git a/LayoutTests/storage/quota-tracking-expected.txt b/LayoutTests/storage/quota-tracking-expected.txt
new file mode 100644
index 0000000..62a2c6b
--- /dev/null
+++ b/LayoutTests/storage/quota-tracking-expected.txt
@@ -0,0 +1,15 @@
+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.
+Adding a table
+Inserting some data
+Done adding data
+Adding a table
+Inserting some data
+Expected quota exception - there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space
+Done adding data
+Adding a table
+Inserting some data
+Done adding data
+Test Complete
+
diff --git a/LayoutTests/storage/quota-tracking.html b/LayoutTests/storage/quota-tracking.html
new file mode 100644
index 0000000..fb2cc9e
--- /dev/null
+++ b/LayoutTests/storage/quota-tracking.html
@@ -0,0 +1,117 @@
+<html>
+<head>
+<script>
+var database1;
+var database2;
+var database3;
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ log("Test Complete");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function statementErrorFunction(tx, error)
+{
+ log("Unexpected exception - " + error.message);
+ finishTest();
+}
+
+function transactionErrorFunction(db, error)
+{
+ // We only expect an error message for database2
+ if (db == database2) {
+ log("Expected quota exception - " + error.message);
+ checkCompletion(db);
+ } else {
+ log("Unexpected exception - " + error.message);
+ finishTest();
+ }
+}
+
+function checkCompletion(db)
+{
+ log("Done adding data");
+
+ db.complete = true;
+ if (database1.complete && database2.complete && database3.complete)
+ finishTest();
+ else if (database2.complete)
+ testDatabase(database3);
+ else
+ testDatabase(database2);
+}
+
+function addData(db)
+{
+ db.transaction(function(tx) {
+ log("Inserting some data");
+ tx.executeSql("INSERT INTO DataTest (randomData) VALUES (ZEROBLOB(17408))", [],
+ function(tx, result) { }, statementErrorFunction);
+ if (db == database2) {
+ // Try to run this statement on 'database2' only.
+ // It should not be run, because the previous statement should've
+ // resulted in a failure that made sqlite roll back the entire transaction.
+ tx.executeSql("INSERT INTO DataTest (randomData) VALUES (ZEROBLOB(10))", [],
+ function(tx, result) {
+ log("This statement should not have been run.");
+ finishTest();
+ }, statementErrorFunction);
+ }
+ }, function(error) {
+ transactionErrorFunction(db, error);
+ }, function() {
+ checkCompletion(db);
+ });
+}
+
+function testDatabase(db)
+{
+ db.transaction(function(tx) {
+ log("Adding a table");
+ tx.executeSql("CREATE TABLE DataTest (randomData)", [],
+ function(tx, result) { }, statementErrorFunction);
+ }, function(error) {
+ transactionErrorFunction(db, error);
+ }, function() {
+ addData(db);
+ });
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpDatabaseCallbacks();
+ layoutTestController.setDatabaseQuota(32768);
+ 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);
+}
+
+</script>
+</head>
+
+<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.
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/read-and-write-transactions-dont-run-together-expected.txt b/LayoutTests/storage/read-and-write-transactions-dont-run-together-expected.txt
new file mode 100644
index 0000000..cc8979c
--- /dev/null
+++ b/LayoutTests/storage/read-and-write-transactions-dont-run-together-expected.txt
@@ -0,0 +1,12 @@
+This test tests that read and write transactions on different handles to the same database don't run together.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+Transaction successful.
+
diff --git a/LayoutTests/storage/read-and-write-transactions-dont-run-together.html b/LayoutTests/storage/read-and-write-transactions-dont-run-together.html
new file mode 100644
index 0000000..a7565b5
--- /dev/null
+++ b/LayoutTests/storage/read-and-write-transactions-dont-run-together.html
@@ -0,0 +1,103 @@
+<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>
+</head>
+<body onload="runTest();">
+This test tests that read and write transactions on different handles to the same database don't run together.<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/read-transactions-running-concurrently-expected.txt b/LayoutTests/storage/read-transactions-running-concurrently-expected.txt
new file mode 100644
index 0000000..f58d2df
--- /dev/null
+++ b/LayoutTests/storage/read-transactions-running-concurrently-expected.txt
@@ -0,0 +1,3 @@
+This test tests that two read-only transactions on different handles to the same database run concurrently.
+Read transactions running concurrently.
+
diff --git a/LayoutTests/storage/read-transactions-running-concurrently.html b/LayoutTests/storage/read-transactions-running-concurrently.html
new file mode 100644
index 0000000..b2d862c
--- /dev/null
+++ b/LayoutTests/storage/read-transactions-running-concurrently.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+<script>
+
+function log(message)
+{
+ document.body.innerHTML += message + "<br>";
+}
+
+function terminateTest()
+{
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function openTestDatabase()
+{
+ return openDatabase("ReadTransactionsRunningConcurrentlyTest",
+ "1.0",
+ "Test to make sure that multiple read transactions on different DB handles to the same DB run concurrently.",
+ 32768);
+}
+
+var readTransactionsInProgress = 0;
+
+function runReadTransaction(db)
+{
+ db.readTransaction(function(tx) {
+ readTransactionsInProgress++;
+ }, function(error) {
+ log("Read transaction failed: " + error.message);
+ terminateTest();
+ }, function() {
+ if (readTransactionsInProgress == 2)
+ log("Read transactions running concurrently.");
+ readTransactionsInProgress--;
+ if (readTransactionsInProgress == 0)
+ terminateTest();
+ });
+}
+
+function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ try {
+ var db1 = openTestDatabase();
+ var db2 = 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() {
+ runReadTransaction(db1);
+ runReadTransaction(db2);
+ });
+ } catch(err) { log(err); }
+}
+</script>
+</head>
+<body onload="runTest();">
+This test tests that two read-only transactions on different handles to the same database run concurrently.<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/resources/database-lock-after-reload-2.html b/LayoutTests/storage/resources/database-lock-after-reload-2.html
new file mode 100644
index 0000000..d73a0df
--- /dev/null
+++ b/LayoutTests/storage/resources/database-lock-after-reload-2.html
@@ -0,0 +1,59 @@
+<html>
+<head>
+<script>
+var database;
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ log("Test part 2 Complete");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function errorFunction(tx, error)
+{
+ log("Test failed - " + error.message);
+ finishTest();
+}
+
+function addData(db)
+{
+ db.transaction(function(tx) {
+ log("Inserting some data");
+
+ tx.executeSql("INSERT INTO DataTest (testData) VALUES (?)", ["A"], function(tx, result) { }, errorFunction);
+ }, function(){}, function() {
+ finishTest();
+ });
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ try {
+ database = openDatabase("DatabaseLockTest", "1.0", "Test for database locking", 5242880);
+ addData(database);
+ } catch(e) {
+ log("Error - could not open database");
+ finishTest();
+ }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/resources/stress-frame.html b/LayoutTests/storage/resources/stress-frame.html
new file mode 100644
index 0000000..b99af3c
--- /dev/null
+++ b/LayoutTests/storage/resources/stress-frame.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+<head>
+<script>
+var db;
+
+try {
+ if (window.openDatabase) {
+ db = openDatabase("StressTest2", "1.0", "Database stress test", 200000);
+ if (!db)
+ alert("Failed to open the database on disk. This is probably because the version was bad or there is not enough space left in this domain's quota");
+ } else
+ alert("Couldn't open the database. Please try with a WebKit nightly with this feature enabled");
+} catch(err) { }
+
+function loaded()
+{
+ db.transaction(function(tx) {
+ tx.executeSql("SELECT COUNT(*) FROM WebkitStickyNotes", [], function(result) {
+ loadNotes();
+ }, function(tx, error) {
+ tx.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT)", [], function(result) {
+ tx.executeSql("INSERT INTO WebKitStickyNotes (id, note) VALUES (?, ?)", [1, 'Text'], function(result) {
+ tx.executeSql("INSERT INTO WebKitStickyNotes (id, note) VALUES (?, ?)", [2, 'More Text'], function(result) {
+ loadNotes();
+ });
+ });
+ });
+ });
+ });
+}
+
+function loadNotes()
+{
+ db.transaction(function(tx) {
+ tx.executeSql("SELECT id, note FROM WebKitStickyNotes", [], function(tx, result) {
+ loadNotes();
+ }, function(tx, error) {
+ alert('Failed to retrieve notes from database - ' + error.message);
+ return;
+ });
+ });
+}
+
+addEventListener('load', loaded, false);
+</script>
+</head>
+<body>
+<p>This test needs to run without crashes and assertion failures for a while.<p>
+</body>
+</html>
diff --git a/LayoutTests/storage/sql-data-types-expected.txt b/LayoutTests/storage/sql-data-types-expected.txt
new file mode 100644
index 0000000..f60caf5
--- /dev/null
+++ b/LayoutTests/storage/sql-data-types-expected.txt
@@ -0,0 +1,7 @@
+PASS: property 'timestamp' ok, type was number
+PASS: property 'id' ok, type was number
+PASS: property 'real' ok, type was number
+PASS: property 'text' ok, type was string
+PASS: property 'blob' ok, type was string
+PASS: database clean up ok.
+
diff --git a/LayoutTests/storage/sql-data-types.html b/LayoutTests/storage/sql-data-types.html
new file mode 100644
index 0000000..cd1c98f
--- /dev/null
+++ b/LayoutTests/storage/sql-data-types.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<script type="text/javascript" src="sql-data-types.js"></script>
+</head>
+
+<body onload="runTest()">
+<pre id="console"></pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/sql-data-types.js b/LayoutTests/storage/sql-data-types.js
new file mode 100644
index 0000000..36fcd01
--- /dev/null
+++ b/LayoutTests/storage/sql-data-types.js
@@ -0,0 +1,87 @@
+//description("This test verifies that the javascript values returned by database queries are of same type as the values put into the database.");
+
+function writeMessageToLog(message)
+{
+ document.getElementById("console").innerText += message + "\n";
+}
+
+function notifyDone(str) {
+ writeMessageToLog(str);
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+var testValues = {
+ timestamp: new Date("Wed Feb 06 2008 12:16:52 GMT+0200 (EET)").valueOf(),
+ id: 1001,
+ real: 101.444,
+ text: "WebKit db TEXT",
+ blob: "supercalifragilistic"
+};
+
+function shouldBeSameTypeAndValue(propName, testValue, result) {
+ if (testValue == result && typeof testValue == typeof result) {
+ writeMessageToLog("PASS: property '" + propName + "' ok, type was " + typeof result);
+ return true;
+ }
+ writeMessageToLog("FAIL: property '" + propName + "' failed."
+ + " expected: " + typeof testValue + ":'" + testValue + "' "
+ + " got: " + typeof result + ":'" + result +"'");
+ return false;
+}
+
+function testDBValues(tx, result) {
+ var rs = result.rows.item(0);
+ // Avoid for .. in because (theretically) the order can change
+ i = "timestamp"; shouldBeSameTypeAndValue(i, testValues[i], rs[i]);
+ i = "id"; shouldBeSameTypeAndValue(i, testValues[i], rs[i]);
+ i = "real"; shouldBeSameTypeAndValue(i, testValues[i], rs[i]);
+ i = "text"; shouldBeSameTypeAndValue(i, testValues[i], rs[i]);
+ i = "blob"; shouldBeSameTypeAndValue(i, testValues[i], rs[i]);
+
+ tx.executeSql("DROP TABLE DataTypeTestTable", [],
+ function(tx, result) {
+ notifyDone("PASS: database clean up ok.");
+ },
+ function(tx, result) {
+ notifyDone("FAIL: Database clean up failed.");
+ });
+}
+
+function fetchDBValuesStmt(tx, result) {
+ tx.executeSql("SELECT * FROM DataTypeTestTable", [],
+ testDBValues,
+ function(tx,error) {
+ notifyDone("FAIL: Error fetching values from the db.")
+ });
+}
+
+function insertTestValuesStmt(tx, result) {
+ tx.executeSql("INSERT INTO DataTypeTestTable (id, real, timestamp, text, blob) VALUES (?,?,?,?,?)",
+ [testValues.id, testValues.real, testValues.timestamp, testValues.text, testValues.blob],
+ fetchDBValuesStmt,
+ function(tx, error) {
+ notifyDone("FAIL: Error inserting values to the db.");
+ });
+}
+
+function createTestDBStmt(tx)
+{
+ tx.executeSql("CREATE TABLE IF NOT EXISTS DataTypeTestTable (id INTEGER UNIQUE, real REAL, timestamp INTEGER, text TEXT, blob BLOB)", [],
+ insertTestValuesStmt,
+ function(tx, error) {
+ notifyDone("FAIL: Error creating the db.");
+ });
+}
+
+function runTest() {
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+ var db = openDatabase("DataTypeTest", "1.0", "Database for sql data type test", 1);
+ if (db)
+ db.transaction(createTestDBStmt);
+ else
+ notifyDone("FAIL: Error opening the db");
+}
diff --git a/LayoutTests/storage/statement-error-callback-expected.txt b/LayoutTests/storage/statement-error-callback-expected.txt
new file mode 100644
index 0000000..ca117a8
--- /dev/null
+++ b/LayoutTests/storage/statement-error-callback-expected.txt
@@ -0,0 +1,6 @@
+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.
+PASS - the transaction error callback was invoked.
+PASS - the transaction error callback was invoked.
+Test Complete
+
diff --git a/LayoutTests/storage/statement-error-callback.html b/LayoutTests/storage/statement-error-callback.html
new file mode 100644
index 0000000..3675548
--- /dev/null
+++ b/LayoutTests/storage/statement-error-callback.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+<script>
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ log("Test Complete");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+var txCallbackCount = 0;
+var NUMBER_OF_TRANSACTIONS = 2;
+
+function transactionErrorFunction(error)
+{
+ 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();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ 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);
+}
+
+</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.
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/success-callback-expected.txt b/LayoutTests/storage/success-callback-expected.txt
new file mode 100644
index 0000000..7619024
--- /dev/null
+++ b/LayoutTests/storage/success-callback-expected.txt
@@ -0,0 +1,5 @@
+This test confirms that a successful transaction - both with and without a statement - receives its successCallback
+Transaction succeeded - Transaction with one statement
+Transaction succeeded - Empty transaction
+Test Complete
+
diff --git a/LayoutTests/storage/success-callback.html b/LayoutTests/storage/success-callback.html
new file mode 100644
index 0000000..c237aba
--- /dev/null
+++ b/LayoutTests/storage/success-callback.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+<script>
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+function finishTest()
+{
+ log("Test Complete");
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function errorFunction(error)
+{
+ log("Test failed - " + error.message);
+ finishTest();
+}
+
+var successCount = 0;
+
+function successFunction(message)
+{
+ log("Transaction succeeded - " + message);
+ if (++successCount == 2)
+ finishTest();
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ var database = openDatabase("SuccessCallbackDatabase", "1.0", "Test for success callback <rdar://5737692>", 1);
+ database.transaction(function(tx) { tx.executeSql("CREATE TABLE IF NOT EXISTS SuccessCallbackTest (randomData)", []); }, errorFunction, function() { successFunction("Transaction with one statement"); });
+ database.transaction(function(tx) { }, errorFunction, function() { successFunction("Empty transaction"); });
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+This test confirms that a successful transaction - both with and without a statement - receives its successCallback
+<pre id="console">
+</pre>
+</body>
+
+</html>
diff --git a/LayoutTests/storage/test-authorizer-expected.txt b/LayoutTests/storage/test-authorizer-expected.txt
new file mode 100644
index 0000000..9ee0603
--- /dev/null
+++ b/LayoutTests/storage/test-authorizer-expected.txt
@@ -0,0 +1,68 @@
+This test tests the database authorizer.
+SQLITE_CREATE_TABLE statement succeeded.
+SQLITE_CREATE_INDEX statement succeeded.
+SQLITE_CREATE_TEMP_TABLE statement succeeded.
+SQLITE_CREATE_TEMP_TRIGGER statement succeeded.
+SQLITE_CREATE_TEMP_VIEW statement succeeded.
+SQLITE_CREATE_TRIGGER statement succeeded.
+SQLITE_CREATE_VIEW statement succeeded.
+SQLITE_CREATE_VTABLE statement failed: not authorized
+SQLITE_READ statement succeeded.
+SQLITE_SELECT statement succeeded.
+SQLITE_DELETE statement succeeded.
+SQLITE_INSERT statement succeeded.
+SQLITE_UPDATE statement succeeded.
+SQLITE_PRAGMA statement failed: not authorized
+SQLITE_ALTER_TABLE statement succeeded.
+SQLITE_ALTER_TABLE statement succeeded.
+SQLITE_TRANSACTION statement failed: not authorized
+SQLITE_ATTACH statement failed: not authorized
+SQLITE_DETACH statement failed: not authorized
+SQLITE_REINDEX statement succeeded.
+SQLITE_ANALYZE statement failed: not authorized
+SQLITE_DROP_INDEX statement succeeded.
+SQLITE_DROP_TEMP_TABLE statement succeeded.
+SQLITE_DROP_TEMP_TRIGGER statement succeeded.
+SQLITE_DROP_TEMP_VIEW statement succeeded.
+SQLITE_DROP_TRIGGER statement succeeded.
+SQLITE_DROP_VIEW statement succeeded.
+SQLITE_DROP_TABLE statement succeeded.
+Write transaction succeeded.
+SQLITE_CREATE_TABLE statement failed: not authorized
+SQLITE_CREATE_TABLE statement succeeded.
+SQLITE_CREATE_INDEX statement failed: not authorized
+SQLITE_CREATE_TEMP_TABLE statement failed: not authorized
+SQLITE_CREATE_TEMP_TRIGGER statement failed: not authorized
+SQLITE_CREATE_TEMP_VIEW statement failed: not authorized
+SQLITE_CREATE_TRIGGER statement failed: not authorized
+SQLITE_CREATE_VIEW statement failed: not authorized
+SQLITE_CREATE_VTABLE statement failed: not authorized
+SQLITE_CREATE_INDEX statement succeeded.
+SQLITE_CREATE_TEMP_TABLE statement succeeded.
+SQLITE_CREATE_TEMP_TRIGGER statement succeeded.
+SQLITE_CREATE_TEMP_VIEW statement succeeded.
+SQLITE_CREATE_TRIGGER statement succeeded.
+SQLITE_CREATE_VIEW statement succeeded.
+SQLITE_CREATE_VTABLE statement failed: not authorized
+SQLITE_READ statement succeeded.
+SQLITE_SELECT statement succeeded.
+SQLITE_DELETE statement failed: not authorized
+SQLITE_INSERT statement failed: not authorized
+SQLITE_UPDATE statement failed: not authorized
+SQLITE_PRAGMA statement failed: not authorized
+SQLITE_ALTER_TABLE statement failed: not authorized
+SQLITE_ALTER_TABLE statement failed: no such table: TestTable
+SQLITE_TRANSACTION statement failed: not authorized
+SQLITE_ATTACH statement failed: not authorized
+SQLITE_DETACH statement failed: not authorized
+SQLITE_REINDEX statement failed: not authorized
+SQLITE_ANALYZE statement failed: not authorized
+SQLITE_DROP_INDEX statement failed: not authorized
+SQLITE_DROP_TEMP_TABLE statement failed: not authorized
+SQLITE_DROP_TEMP_TRIGGER statement failed: not authorized
+SQLITE_DROP_TEMP_VIEW statement failed: not authorized
+SQLITE_DROP_TRIGGER statement failed: not authorized
+SQLITE_DROP_VIEW statement failed: not authorized
+SQLITE_DROP_TABLE statement failed: not authorized
+Read transactions succeeded.
+
diff --git a/LayoutTests/storage/test-authorizer.html b/LayoutTests/storage/test-authorizer.html
new file mode 100644
index 0000000..57785a6
--- /dev/null
+++ b/LayoutTests/storage/test-authorizer.html
@@ -0,0 +1,175 @@
+<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>
+</head>
+<body onload="runTest();">
+This test tests the database authorizer.<br>
+</body>
+</html>
diff --git a/LayoutTests/storage/transaction-callback-exception-crash-expected.txt b/LayoutTests/storage/transaction-callback-exception-crash-expected.txt
new file mode 100644
index 0000000..0e2eb33
--- /dev/null
+++ b/LayoutTests/storage/transaction-callback-exception-crash-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: line 0: TransactionCallbackError
+If WebKit doesn't crash, this test has passed
diff --git a/LayoutTests/storage/transaction-callback-exception-crash.html b/LayoutTests/storage/transaction-callback-exception-crash.html
new file mode 100644
index 0000000..4bcc732
--- /dev/null
+++ b/LayoutTests/storage/transaction-callback-exception-crash.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<script>
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+
+var db = openDatabase("15976Test", "1.0", "Test for http://bugs.webkit.org/show_bug.cgi?id=15976", 1);
+db.transaction(function(tx) {
+ if (window.layoutTestController)
+ window.setTimeout(function() { layoutTestController.notifyDone() }, 0);
+ throw "TransactionCallbackError";
+});
+</script>
+</head>
+<body>
+If WebKit doesn't crash, this test has passed
+</body>
+</html>
diff --git a/LayoutTests/storage/transaction-error-callback-expected.txt b/LayoutTests/storage/transaction-error-callback-expected.txt
new file mode 100644
index 0000000..6fb33bd
--- /dev/null
+++ b/LayoutTests/storage/transaction-error-callback-expected.txt
@@ -0,0 +1,9 @@
+CONSOLE MESSAGE: line 63: [object Object]
+CONSOLE MESSAGE: line 63: [object Object]
+This test confirms that SQLTransactionErrorCallback is invoked correctly and regardless of its output, the transaction is always rolled back on failure.
+Testing transaction failing mid-way and error callback returning true : SUCCESS
+Testing transaction failing mid-way and error callback return false : SUCCESS
+Testing statement callback throwing exception and error callback returning true : SUCCESS
+Testing statement callback throwing exception and error callback returning false : SUCCESS
+All Tests are complete.
+
diff --git a/LayoutTests/storage/transaction-error-callback.html b/LayoutTests/storage/transaction-error-callback.html
new file mode 100644
index 0000000..90b2ed0
--- /dev/null
+++ b/LayoutTests/storage/transaction-error-callback.html
@@ -0,0 +1,105 @@
+<html>
+<head>
+<script>
+
+function log(message)
+{
+ document.getElementById("console").innerHTML += message + "<br>";
+}
+
+// signal to layoutTestController when this reaches zero.
+var testCount = 4;
+// we first retrieve and store the number of rows already in our test database.
+// our goal is to keep the number unchanged through the tests.
+var initialRowCount = 0;
+var database;
+var successCallbackCalled;
+
+function finishTest()
+{
+ if (--testCount)
+ return;
+
+ log("All Tests are complete.");
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+
+function successCallback()
+{
+ successCallbackCalled = true;
+}
+
+function verifySuccess(msg)
+{
+ database.transaction(function(tx)
+ {
+ tx.executeSql("SELECT count(*) AS count FROM ErrorCallbackTest", [], function(tx, rs)
+ {
+ log(msg + " : " + (rs.rows.item(0).count == initialRowCount && !successCallbackCalled ? "SUCCESS" : "FAILURE"));
+ finishTest();
+ });
+ });
+}
+
+function failMidWay(errorCallback)
+{
+ successCallbackCalled = false;
+ database.transaction(function(tx)
+ {
+ tx.executeSql("INSERT INTO ErrorCallbackTest(someValue) VALUES(?);", [ 1 ]);
+ tx.executeSql("MUTTER SOMETHING ILLEGIBLE");
+ }, errorCallback, successCallback);
+}
+
+function statementCallbackThrowsException(errorCallback)
+{
+ successCallbackCalled = false;
+ database.transaction(function(tx)
+ {
+ tx.executeSql("INSERT INTO ErrorCallbackTest(someValue) VALUES(?);", [ 1 ], function()
+ {
+ throw {};
+ });
+ });
+}
+
+function runTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.clearAllDatabases();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ database = openDatabase("ErrorCallbackDatabase", "1.0", "Test for error callback", 1);
+ database.transaction(function(tx)
+ {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS ErrorCallbackTest (someValue)", []);
+ tx.executeSql("SELECT count(*) AS count FROM ErrorCallbackTest", [], function(tx, rs)
+ {
+ initialRowCount = rs.rows.item(0).count;
+ });
+ });
+
+ failMidWay(function() { return true; });
+ verifySuccess("Testing transaction failing mid-way and error callback returning true");
+ failMidWay(function() { return false; });
+ verifySuccess("Testing transaction failing mid-way and error callback return false");
+ statementCallbackThrowsException(function() { return true; });
+ verifySuccess("Testing statement callback throwing exception and error callback returning true");
+ statementCallbackThrowsException(function() { return false; });
+ verifySuccess("Testing statement callback throwing exception and error callback returning false");
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+This test confirms that <code>SQLTransactionErrorCallback</code> is invoked correctly and regardless of its output, the transaction is always rolled back on failure.
+<pre id="console">
+</pre>
+</body>
+
+</html>