summaryrefslogtreecommitdiffstats
path: root/dom/quota/test/xpcshell
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/quota/test/xpcshell
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/quota/test/xpcshell')
-rw-r--r--dom/quota/test/xpcshell/basics_profile.zipbin0 -> 3405 bytes
-rw-r--r--dom/quota/test/xpcshell/caching/groupMismatch_profile.zipbin0 -> 2576 bytes
-rw-r--r--dom/quota/test/xpcshell/caching/head.js14
-rw-r--r--dom/quota/test/xpcshell/caching/make_unsetLastAccessTime.js25
-rw-r--r--dom/quota/test/xpcshell/caching/removedOrigin_profile.zipbin0 -> 1551 bytes
-rw-r--r--dom/quota/test/xpcshell/caching/test_groupMismatch.js45
-rw-r--r--dom/quota/test/xpcshell/caching/test_removedOrigin.js61
-rw-r--r--dom/quota/test/xpcshell/caching/test_unsetLastAccessTime.js47
-rw-r--r--dom/quota/test/xpcshell/caching/unsetLastAccessTime_profile.zipbin0 -> 1574 bytes
-rw-r--r--dom/quota/test/xpcshell/caching/xpcshell.toml16
-rw-r--r--dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.json729
-rw-r--r--dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.zipbin0 -> 21241 bytes
-rw-r--r--dom/quota/test/xpcshell/clearStoragesForPrincipal_profile.zipbin0 -> 7380 bytes
-rw-r--r--dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.json152
-rw-r--r--dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.zipbin0 -> 7500 bytes
-rw-r--r--dom/quota/test/xpcshell/common/head.js665
-rw-r--r--dom/quota/test/xpcshell/common/utils.js47
-rw-r--r--dom/quota/test/xpcshell/createLocalStorage_profile.zipbin0 -> 1006 bytes
-rw-r--r--dom/quota/test/xpcshell/defaultStorageDirectory_shared.json171
-rw-r--r--dom/quota/test/xpcshell/defaultStorageDirectory_shared.zipbin0 -> 1804 bytes
-rw-r--r--dom/quota/test/xpcshell/getUsage_profile.zipbin0 -> 24717 bytes
-rw-r--r--dom/quota/test/xpcshell/groupMismatch_profile.zipbin0 -> 1706 bytes
-rw-r--r--dom/quota/test/xpcshell/head.js14
-rw-r--r--dom/quota/test/xpcshell/indexedDBDirectory_shared.json35
-rw-r--r--dom/quota/test/xpcshell/indexedDBDirectory_shared.zipbin0 -> 524 bytes
-rw-r--r--dom/quota/test/xpcshell/make_unknownFiles.js176
-rw-r--r--dom/quota/test/xpcshell/make_unsetLastAccessTime.js25
-rw-r--r--dom/quota/test/xpcshell/originMismatch_profile.json77
-rw-r--r--dom/quota/test/xpcshell/originMismatch_profile.zipbin0 -> 3104 bytes
-rw-r--r--dom/quota/test/xpcshell/persistentStorageDirectory_shared.json57
-rw-r--r--dom/quota/test/xpcshell/persistentStorageDirectory_shared.zipbin0 -> 1272 bytes
-rw-r--r--dom/quota/test/xpcshell/removeLocalStorage1_profile.zipbin0 -> 1187 bytes
-rw-r--r--dom/quota/test/xpcshell/removeLocalStorage2_profile.zipbin0 -> 2827 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/head.js14
-rw-r--r--dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js866
-rw-r--r--dom/quota/test/xpcshell/telemetry/version0_0_make_it_unusable.zipbin0 -> 1978 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version0_0_profile.zipbin0 -> 1292 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version1_0_make_it_unusable.zipbin0 -> 1978 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version1_0_profile.zipbin0 -> 1475 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_0_make_it_unusable.zipbin0 -> 1978 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_0_profile.zipbin0 -> 1473 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_1_make_it_unusable.zipbin0 -> 1978 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_1_profile.zipbin0 -> 1472 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_2_make_it_unusable.zipbin0 -> 299 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/version2_2_profile.zipbin0 -> 1507 bytes
-rw-r--r--dom/quota/test/xpcshell/telemetry/xpcshell.toml17
-rw-r--r--dom/quota/test/xpcshell/tempMetadataCleanup_profile.zipbin0 -> 1569 bytes
-rw-r--r--dom/quota/test/xpcshell/test_allowListFiles.js61
-rw-r--r--dom/quota/test/xpcshell/test_bad_origin_directory.js37
-rw-r--r--dom/quota/test/xpcshell/test_basics.js143
-rw-r--r--dom/quota/test/xpcshell/test_clearStoragesForOriginAttributesPattern.js58
-rw-r--r--dom/quota/test/xpcshell/test_clearStoragesForOriginPrefix.js72
-rw-r--r--dom/quota/test/xpcshell/test_clearStoragesForPrincipal.js56
-rw-r--r--dom/quota/test/xpcshell/test_clearStoragesForPrivateBrowsing.js41
-rw-r--r--dom/quota/test/xpcshell/test_createLocalStorage.js155
-rw-r--r--dom/quota/test/xpcshell/test_estimateOrigin.js80
-rw-r--r--dom/quota/test/xpcshell/test_getUsage.js133
-rw-r--r--dom/quota/test/xpcshell/test_groupMismatch.js74
-rw-r--r--dom/quota/test/xpcshell/test_initTemporaryStorage.js49
-rw-r--r--dom/quota/test/xpcshell/test_initializePersistentClient.js46
-rw-r--r--dom/quota/test/xpcshell/test_initializeTemporaryClient.js55
-rw-r--r--dom/quota/test/xpcshell/test_listOrigins.js88
-rw-r--r--dom/quota/test/xpcshell/test_originEndsWithDot.js70
-rw-r--r--dom/quota/test/xpcshell/test_originMismatch.js75
-rw-r--r--dom/quota/test/xpcshell/test_originWithCaret.js17
-rw-r--r--dom/quota/test/xpcshell/test_orpahnedQuotaObject.js44
-rw-r--r--dom/quota/test/xpcshell/test_persist.js133
-rw-r--r--dom/quota/test/xpcshell/test_persist_eviction.js82
-rw-r--r--dom/quota/test/xpcshell/test_persist_globalLimit.js83
-rw-r--r--dom/quota/test/xpcshell/test_persist_groupLimit.js105
-rw-r--r--dom/quota/test/xpcshell/test_removeLocalStorage.js89
-rw-r--r--dom/quota/test/xpcshell/test_simpledb.js6
-rw-r--r--dom/quota/test/xpcshell/test_specialOrigins.js55
-rw-r--r--dom/quota/test/xpcshell/test_storagePressure.js136
-rw-r--r--dom/quota/test/xpcshell/test_tempMetadataCleanup.js45
-rw-r--r--dom/quota/test/xpcshell/test_unaccessedOrigins.js168
-rw-r--r--dom/quota/test/xpcshell/test_unknownFiles.js106
-rw-r--r--dom/quota/test/xpcshell/test_unsetLastAccessTime.js68
-rw-r--r--dom/quota/test/xpcshell/test_validOrigins.js100
-rw-r--r--dom/quota/test/xpcshell/unknownFiles_profile.zipbin0 -> 13859 bytes
-rw-r--r--dom/quota/test/xpcshell/unsetLastAccessTime_profile.zipbin0 -> 1574 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.json64
-rw-r--r--dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.zipbin0 -> 2675 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/head.js14
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.json63
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.zipbin0 -> 1336 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.json55
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.zipbin0 -> 812 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.json65
-rw-r--r--dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.zipbin0 -> 1508 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/localStorageArchive1upgrade_profile.zipbin0 -> 7675 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/localStorageArchive4upgrade_profile.zipbin0 -> 7853 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/localStorageArchiveDowngrade_profile.zipbin0 -> 7799 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.json63
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.zipbin0 -> 1413 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.json64
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.zipbin0 -> 1028 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.json92
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.zipbin0 -> 2720 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.json382
-rw-r--r--dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.zipbin0 -> 12717 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_localStorageArchive1upgrade.js65
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_localStorageArchive4upgrade.js107
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_localStorageArchiveDowngrade.js66
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeCacheFrom1.js79
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js187
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js121
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory_removeOldDirectory.js86
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js378
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_removeOldDirectory.js102
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js162
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js158
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_idb.js43
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeAppsData.js100
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeMorgueDirectory.js60
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js179
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_0.js97
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_1.js85
-rw-r--r--dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_2.js64
-rw-r--r--dom/quota/test/xpcshell/upgrades/version0_0_profile.json88
-rw-r--r--dom/quota/test/xpcshell/upgrades/version0_0_profile.zipbin0 -> 2684 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.json72
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.zipbin0 -> 3145 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.json73
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.zipbin0 -> 3944 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.json57
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.zipbin0 -> 1438 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.json112
-rw-r--r--dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.zipbin0 -> 4843 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_0_profile.json105
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_0_profile.zipbin0 -> 5119 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_1_profile.json69
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_1_profile.zipbin0 -> 2400 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_2_profile.json18
-rw-r--r--dom/quota/test/xpcshell/upgrades/version2_2_profile.zipbin0 -> 237 bytes
-rw-r--r--dom/quota/test/xpcshell/upgrades/xpcshell.toml75
-rw-r--r--dom/quota/test/xpcshell/xpcshell.toml100
137 files changed, 9553 insertions, 0 deletions
diff --git a/dom/quota/test/xpcshell/basics_profile.zip b/dom/quota/test/xpcshell/basics_profile.zip
new file mode 100644
index 0000000000..bbdb0f50cf
--- /dev/null
+++ b/dom/quota/test/xpcshell/basics_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/caching/groupMismatch_profile.zip b/dom/quota/test/xpcshell/caching/groupMismatch_profile.zip
new file mode 100644
index 0000000000..8124e589de
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/groupMismatch_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/caching/head.js b/dom/quota/test/xpcshell/caching/head.js
new file mode 100644
index 0000000000..5c36d82ca6
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/head.js
@@ -0,0 +1,14 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// The path to the top level directory.
+const depth = "../../../../../";
+
+loadScript("dom/quota/test/xpcshell/common/head.js");
+
+function loadScript(path) {
+ let uri = Services.io.newFileURI(do_get_file(depth + path));
+ Services.scriptloader.loadSubScript(uri.spec);
+}
diff --git a/dom/quota/test/xpcshell/caching/make_unsetLastAccessTime.js b/dom/quota/test/xpcshell/caching/make_unsetLastAccessTime.js
new file mode 100644
index 0000000000..9be377e4f3
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/make_unsetLastAccessTime.js
@@ -0,0 +1,25 @@
+/*
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+async function testSteps() {
+ const originDirPath = "storage/default/https+++foo.example.com";
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Creating an empty origin directory");
+
+ let originDir = getRelativeFile(originDirPath);
+ originDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ // The metadata file should be now restored.
+}
diff --git a/dom/quota/test/xpcshell/caching/removedOrigin_profile.zip b/dom/quota/test/xpcshell/caching/removedOrigin_profile.zip
new file mode 100644
index 0000000000..a5ccc05aa9
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/removedOrigin_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/caching/test_groupMismatch.js b/dom/quota/test/xpcshell/caching/test_groupMismatch.js
new file mode 100644
index 0000000000..3f8e843798
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/test_groupMismatch.js
@@ -0,0 +1,45 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that the group loaded from the origin table
+ * gets updated (if necessary) before quota initialization for the given origin.
+ */
+
+async function testSteps() {
+ const principal = getPrincipal("https://foo.bar.mozilla-iot.org");
+ const originUsage = 100;
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains one initialized origin directory with simple database
+ // data, a script for origin initialization and the storage database:
+ // - storage/default/https+++foo.bar.mozilla-iot.org
+ // - create_db.js
+ // - storage.sqlite
+ // The file create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/quota/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Manually change the group and accessed values in the origin table in
+ // storage.sqlite by running this SQL statement:
+ // UPDATE origin SET group_ = 'mozilla-iot.org', accessed = 0
+ // 2. Manually change the group in .metadata-v2 from "bar.mozilla-iot.org" to
+ // "mozilla-iot.org".
+ // 3. Remove the folder "storage/temporary".
+ // 4. Remove the file "storage/ls-archive.sqlite".
+ installPackage("groupMismatch_profile");
+
+ request = getOriginUsage(principal, /* fromMemory */ true);
+ await requestFinished(request);
+
+ is(request.result.usage, originUsage, "Correct origin usage");
+}
diff --git a/dom/quota/test/xpcshell/caching/test_removedOrigin.js b/dom/quota/test/xpcshell/caching/test_removedOrigin.js
new file mode 100644
index 0000000000..8b5702ad9c
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/test_removedOrigin.js
@@ -0,0 +1,61 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Verify that temporary storage initialization will notice a removed origin
+ * that the cache has data for and which indicates the origin was accessed
+ * during the last run. Currently, we expect LoadQuotaFromCache to fail because
+ * of this inconsistency and to fall back to full initialization.
+ */
+
+async function testSteps() {
+ const principal = getPrincipal("http://example.com");
+ const originUsage = 0;
+
+ info("Setting pref");
+
+ // The packaged profile will have a different build ID and we would treat the
+ // cache as invalid if we didn't bypass this check.
+ Services.prefs.setBoolPref("dom.quotaManager.caching.checkBuildId", false);
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains empty default storage, a script for origin
+ // initialization and the storage database:
+ // - storage/default
+ // - create_db.js
+ // - storage.sqlite
+ // The file create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/quota/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/default/http+++example.com".
+ // 2. Remove the folder "storage/temporary".
+ // 3. Remove the file "storage/ls-archive.sqlite".
+ installPackage("removedOrigin_profile");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Getting origin usage");
+
+ request = getOriginUsage(principal, /* fromMemory */ true);
+ await requestFinished(request);
+
+ is(request.result.usage, originUsage, "Correct origin usage");
+}
diff --git a/dom/quota/test/xpcshell/caching/test_unsetLastAccessTime.js b/dom/quota/test/xpcshell/caching/test_unsetLastAccessTime.js
new file mode 100644
index 0000000000..2199201b8a
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/test_unsetLastAccessTime.js
@@ -0,0 +1,47 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const principal = getPrincipal("https://foo.example.com/");
+
+ info("Setting pref");
+
+ // The packaged profile will have a different build ID and we would treat the
+ // cache as invalid if we didn't bypass this check.
+ Services.prefs.setBoolPref("dom.quotaManager.caching.checkBuildId", false);
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains one initialized origin directory and the storage
+ // database:
+ // - storage/default/https+++foo.example.com
+ // - storage.sqlite
+ // The file make_unsetLastAccessTime.js was run locally, specifically it was
+ // temporarily enabled in xpcshell.ini and then executed:
+ // mach test --interactive dom/quota/test/xpcshell/caching/make_unsetLastAccessTime.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ // 2. Remove the file "storage/ls-archive.sqlite".
+ installPackage("unsetLastAccessTime_profile");
+
+ info("Getting full origin metadata");
+
+ request = getFullOriginMetadata("default", principal);
+ await requestFinished(request);
+
+ info("Verifying last access time");
+
+ Assert.notEqual(
+ BigInt(request.result.lastAccessTime),
+ INT64_MIN,
+ "Correct last access time"
+ );
+}
diff --git a/dom/quota/test/xpcshell/caching/unsetLastAccessTime_profile.zip b/dom/quota/test/xpcshell/caching/unsetLastAccessTime_profile.zip
new file mode 100644
index 0000000000..2b14ca7276
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/unsetLastAccessTime_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/caching/xpcshell.toml b/dom/quota/test/xpcshell/caching/xpcshell.toml
new file mode 100644
index 0000000000..4901ed16a7
--- /dev/null
+++ b/dom/quota/test/xpcshell/caching/xpcshell.toml
@@ -0,0 +1,16 @@
+[DEFAULT]
+head = "head.js"
+support-files = [
+ "groupMismatch_profile.zip",
+ "removedOrigin_profile.zip",
+]
+
+["make_unsetLastAccessTime.js"]
+skip-if = ["true"] # Only used for recreating unsetLastAccessTime_profile.zip
+
+["test_groupMismatch.js"]
+
+["test_removedOrigin.js"]
+
+["test_unsetLastAccessTime.js"]
+support-files = ["unsetLastAccessTime_profile.zip"]
diff --git a/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.json b/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.json
new file mode 100644
index 0000000000..6df0726773
--- /dev/null
+++ b/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.json
@@ -0,0 +1,729 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterClearByOriginPrefix",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterClearByOriginPrefix_persistent",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterClearByOriginPrefix_default",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterClearByOriginPrefix_temporary",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ },
+ {
+ "name": "http+++example.com^userContextId=1",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": "cache", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "ls", "dir": true },
+ { "name": "fs", "dir": true }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.zip b/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.zip
new file mode 100644
index 0000000000..b6a3877d3e
--- /dev/null
+++ b/dom/quota/test/xpcshell/clearStoragesForOriginPrefix_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/clearStoragesForPrincipal_profile.zip b/dom/quota/test/xpcshell/clearStoragesForPrincipal_profile.zip
new file mode 100644
index 0000000000..7d7985ddd0
--- /dev/null
+++ b/dom/quota/test/xpcshell/clearStoragesForPrincipal_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.json b/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.json
new file mode 100644
index 0000000000..b4b8e3afda
--- /dev/null
+++ b/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.json
@@ -0,0 +1,152 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "private",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com^privateBrowsingId=1",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterClearPrivateBrowsing",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "cache", "dir": true },
+ { "name": "fs", "dir": true },
+ { "name": "idb", "dir": true },
+ { "name": "ls", "dir": true },
+ { "name": "sdb", "dir": true },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.zip b/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.zip
new file mode 100644
index 0000000000..4bbb8a5c77
--- /dev/null
+++ b/dom/quota/test/xpcshell/clearStoragesForPrivateBrowsing_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/common/head.js b/dom/quota/test/xpcshell/common/head.js
new file mode 100644
index 0000000000..000236b3cd
--- /dev/null
+++ b/dom/quota/test/xpcshell/common/head.js
@@ -0,0 +1,665 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const NS_OK = Cr.NS_OK;
+const NS_ERROR_FAILURE = Cr.NS_ERROR_FAILURE;
+const NS_ERROR_UNEXPECTED = Cr.NS_ERROR_UNEXPECTED;
+const NS_ERROR_FILE_NO_DEVICE_SPACE = Cr.NS_ERROR_FILE_NO_DEVICE_SPACE;
+
+const loggingEnabled = false;
+
+var testGenerator;
+
+loadScript("dom/quota/test/common/xpcshell.js");
+
+function log(msg) {
+ if (loggingEnabled) {
+ info(msg);
+ }
+}
+
+function is(a, b, msg) {
+ Assert.equal(a, b, msg);
+}
+
+function ok(cond, msg) {
+ Assert.ok(!!cond, msg);
+}
+
+function todo(cond, msg) {
+ todo_check_true(cond);
+}
+
+function run_test() {
+ runTest();
+}
+
+if (!this.runTest) {
+ this.runTest = function () {
+ do_get_profile();
+
+ enableStorageTesting();
+ enableTesting();
+
+ // In order to support converting tests to using async functions from using
+ // generator functions, we detect async functions by checking the name of
+ // function's constructor.
+ Assert.ok(
+ typeof testSteps === "function",
+ "There should be a testSteps function"
+ );
+ if (testSteps.constructor.name === "AsyncFunction") {
+ // Do run our existing cleanup function that would normally be called by
+ // the generator's call to finishTest().
+ registerCleanupFunction(function () {
+ resetStorageTesting();
+ resetTesting();
+ });
+
+ add_task(testSteps);
+
+ // Since we defined run_test, we must invoke run_next_test() to start the
+ // async test.
+ run_next_test();
+ } else {
+ Assert.ok(
+ testSteps.constructor.name === "GeneratorFunction",
+ "Unsupported function type"
+ );
+
+ do_test_pending();
+
+ testGenerator = testSteps();
+ testGenerator.next();
+ }
+ };
+}
+
+function finishTest() {
+ resetStorageTesting();
+ resetTesting();
+
+ executeSoon(function () {
+ do_test_finished();
+ });
+}
+
+function grabArgAndContinueHandler(arg) {
+ testGenerator.next(arg);
+}
+
+function continueToNextStep() {
+ executeSoon(function () {
+ testGenerator.next();
+ });
+}
+
+function continueToNextStepSync() {
+ testGenerator.next();
+}
+
+function enableTesting() {
+ SpecialPowers.setBoolPref(
+ "dom.storage.enable_unsupported_legacy_implementation",
+ false
+ );
+}
+
+function resetTesting() {
+ SpecialPowers.clearUserPref(
+ "dom.storage.enable_unsupported_legacy_implementation"
+ );
+}
+
+function setGlobalLimit(globalLimit) {
+ SpecialPowers.setIntPref(
+ "dom.quotaManager.temporaryStorage.fixedLimit",
+ globalLimit
+ );
+}
+
+function resetGlobalLimit() {
+ SpecialPowers.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
+}
+
+function storageInitialized(callback) {
+ let request = SpecialPowers._getQuotaManager().storageInitialized();
+ request.callback = callback;
+
+ return request;
+}
+
+function temporaryStorageInitialized(callback) {
+ let request = SpecialPowers._getQuotaManager().temporaryStorageInitialized();
+ request.callback = callback;
+
+ return request;
+}
+
+function init(callback) {
+ let request = SpecialPowers._getQuotaManager().init();
+ request.callback = callback;
+
+ return request;
+}
+
+function initTemporaryStorage(callback) {
+ let request = SpecialPowers._getQuotaManager().initTemporaryStorage();
+ request.callback = callback;
+
+ return request;
+}
+
+function initPersistentOrigin(principal, callback) {
+ let request =
+ SpecialPowers._getQuotaManager().initializePersistentOrigin(principal);
+ request.callback = callback;
+
+ return request;
+}
+
+function initTemporaryOrigin(persistence, principal, callback) {
+ let request = SpecialPowers._getQuotaManager().initializeTemporaryOrigin(
+ persistence,
+ principal
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function initPersistentClient(principal, client, callback) {
+ let request = SpecialPowers._getQuotaManager().initializePersistentClient(
+ principal,
+ client
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function initTemporaryClient(persistence, principal, client, callback) {
+ let request = SpecialPowers._getQuotaManager().initializeTemporaryClient(
+ persistence,
+ principal,
+ client
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function getFullOriginMetadata(persistence, principal, callback) {
+ const request = SpecialPowers._getQuotaManager().getFullOriginMetadata(
+ persistence,
+ principal
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function clearClient(principal, persistence, client, callback) {
+ let request = SpecialPowers._getQuotaManager().clearStoragesForPrincipal(
+ principal,
+ persistence,
+ client
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function clearOrigin(principal, persistence, callback) {
+ let request = SpecialPowers._getQuotaManager().clearStoragesForPrincipal(
+ principal,
+ persistence
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function clearOriginsByPrefix(principal, persistence, callback) {
+ let request = SpecialPowers._getQuotaManager().clearStoragesForOriginPrefix(
+ principal,
+ persistence
+ );
+ request.callback = callback;
+
+ return request;
+}
+
+function clearPrivateBrowsing(callback) {
+ let request =
+ SpecialPowers._getQuotaManager().clearStoragesForPrivateBrowsing();
+ request.callback = callback;
+
+ return request;
+}
+
+function resetClient(principal, client) {
+ let request = Services.qms.resetStoragesForPrincipal(
+ principal,
+ "default",
+ client
+ );
+
+ return request;
+}
+
+function persist(principal, callback) {
+ let request = SpecialPowers._getQuotaManager().persist(principal);
+ request.callback = callback;
+
+ return request;
+}
+
+function persisted(principal, callback) {
+ let request = SpecialPowers._getQuotaManager().persisted(principal);
+ request.callback = callback;
+
+ return request;
+}
+
+function estimateOrigin(principal, callback) {
+ let request = SpecialPowers._getQuotaManager().estimate(principal);
+ request.callback = callback;
+
+ return request;
+}
+
+function listOrigins(callback) {
+ let request = SpecialPowers._getQuotaManager().listOrigins(callback);
+ request.callback = callback;
+
+ return request;
+}
+
+function getPersistedFromMetadata(readBuffer) {
+ const persistedPosition = 8; // Persisted state is stored in the 9th byte
+ let view =
+ readBuffer instanceof Uint8Array ? readBuffer : new Uint8Array(readBuffer);
+
+ return !!view[persistedPosition];
+}
+
+function grabResultAndContinueHandler(request) {
+ testGenerator.next(request.result);
+}
+
+function grabUsageAndContinueHandler(request) {
+ testGenerator.next(request.result.usage);
+}
+
+function getUsage(usageHandler, getAll) {
+ let request = SpecialPowers._getQuotaManager().getUsage(usageHandler, getAll);
+
+ return request;
+}
+
+function getOriginUsage(principal, fromMemory = false) {
+ let request = Services.qms.getUsageForPrincipal(
+ principal,
+ function () {},
+ fromMemory
+ );
+
+ return request;
+}
+
+function getCurrentUsage(usageHandler) {
+ let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(
+ Ci.nsIPrincipal
+ );
+ let request = SpecialPowers._getQuotaManager().getUsageForPrincipal(
+ principal,
+ usageHandler
+ );
+
+ return request;
+}
+
+function getPrincipal(url, attr = {}) {
+ let uri = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(url);
+ let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(
+ Ci.nsIScriptSecurityManager
+ );
+ return ssm.createContentPrincipal(uri, attr);
+}
+
+var SpecialPowers = {
+ getBoolPref(prefName) {
+ return this._getPrefs().getBoolPref(prefName);
+ },
+
+ setBoolPref(prefName, value) {
+ this._getPrefs().setBoolPref(prefName, value);
+ },
+
+ setIntPref(prefName, value) {
+ this._getPrefs().setIntPref(prefName, value);
+ },
+
+ clearUserPref(prefName) {
+ this._getPrefs().clearUserPref(prefName);
+ },
+
+ _getPrefs() {
+ let prefService = Cc["@mozilla.org/preferences-service;1"].getService(
+ Ci.nsIPrefService
+ );
+ return prefService.getBranch(null);
+ },
+
+ _getQuotaManager() {
+ return Cc["@mozilla.org/dom/quota-manager-service;1"].getService(
+ Ci.nsIQuotaManagerService
+ );
+ },
+};
+
+function installPackages(packageRelativePaths) {
+ if (packageRelativePaths.length != 2) {
+ throw new Error("Unsupported number of package relative paths");
+ }
+
+ for (const packageRelativePath of packageRelativePaths) {
+ installPackage(packageRelativePath);
+ }
+}
+
+// Take current storage structure on disk and compare it with the expected
+// structure. The expected structure is defined in JSON and consists of a per
+// test package definition and a shared package definition. The shared package
+// definition should contain unknown stuff which needs to be properly handled
+// in all situations.
+function verifyStorage(packageDefinitionRelativePaths, key, sharedKey) {
+ if (packageDefinitionRelativePaths.length != 2) {
+ throw new Error("Unsupported number of package definition relative paths");
+ }
+
+ function verifyEntries(entries, name, indent = "") {
+ log(`${indent}Verifying ${name} entries`);
+
+ indent += " ";
+
+ for (const entry of entries) {
+ const maybeName = entry.name;
+
+ log(`${indent}Verifying entry ${maybeName}`);
+
+ let hasName = false;
+ let hasDir = false;
+ let hasEntries = false;
+
+ for (const property in entry) {
+ switch (property) {
+ case "note":
+ case "todo":
+ break;
+
+ case "name":
+ hasName = true;
+ break;
+
+ case "dir":
+ hasDir = true;
+ break;
+
+ case "entries":
+ hasEntries = true;
+ break;
+
+ default:
+ throw new Error(`Unknown property ${property}`);
+ }
+ }
+
+ if (!hasName) {
+ throw new Error("An entry must have the name property");
+ }
+
+ if (!hasDir) {
+ throw new Error("An entry must have the dir property");
+ }
+
+ if (hasEntries && !entry.dir) {
+ throw new Error("An entry can't have entries if it's not a directory");
+ }
+
+ if (hasEntries) {
+ verifyEntries(entry.entries, entry.name, indent);
+ }
+ }
+ }
+
+ function getCurrentEntries() {
+ log("Getting current entries");
+
+ function getEntryForFile(file) {
+ let entry = {
+ name: file.leafName,
+ dir: file.isDirectory(),
+ };
+
+ if (file.isDirectory()) {
+ const enumerator = file.directoryEntries;
+ let nextFile;
+ while ((nextFile = enumerator.nextFile)) {
+ if (!entry.entries) {
+ entry.entries = [];
+ }
+ entry.entries.push(getEntryForFile(nextFile));
+ }
+ }
+
+ return entry;
+ }
+
+ let entries = [];
+
+ let file = getRelativeFile("indexedDB");
+ if (file.exists()) {
+ entries.push(getEntryForFile(file));
+ }
+
+ file = getRelativeFile("storage");
+ if (file.exists()) {
+ entries.push(getEntryForFile(file));
+ }
+
+ file = getRelativeFile("storage.sqlite");
+ if (file.exists()) {
+ entries.push(getEntryForFile(file));
+ }
+
+ verifyEntries(entries, "current");
+
+ return entries;
+ }
+
+ function getEntriesFromPackageDefinition(
+ packageDefinitionRelativePath,
+ lookupKey
+ ) {
+ log(`Getting ${lookupKey} entries from ${packageDefinitionRelativePath}`);
+
+ const currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ const file = getRelativeFile(
+ packageDefinitionRelativePath + ".json",
+ currentDir
+ );
+
+ const fileInputStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ fileInputStream.init(file, -1, -1, 0);
+
+ const scriptableInputStream = Cc[
+ "@mozilla.org/scriptableinputstream;1"
+ ].createInstance(Ci.nsIScriptableInputStream);
+ scriptableInputStream.init(fileInputStream);
+
+ const data = scriptableInputStream.readBytes(
+ scriptableInputStream.available()
+ );
+
+ const obj = JSON.parse(data);
+
+ const result = obj.find(({ key: elementKey }) => elementKey == lookupKey);
+
+ if (!result) {
+ throw new Error("The file doesn't contain an element for given key");
+ }
+
+ if (!result.entries) {
+ throw new Error("The element doesn't have the entries property");
+ }
+
+ verifyEntries(result.entries, lookupKey);
+
+ return result.entries;
+ }
+
+ function addSharedEntries(expectedEntries, sharedEntries, name, indent = "") {
+ log(`${indent}Checking common ${name} entries`);
+
+ indent += " ";
+
+ for (const sharedEntry of sharedEntries) {
+ const expectedEntry = expectedEntries.find(
+ ({ name: elementName }) => elementName == sharedEntry.name
+ );
+
+ if (expectedEntry) {
+ log(`${indent}Checking common entry ${sharedEntry.name}`);
+
+ if (!expectedEntry.dir || !sharedEntry.dir) {
+ throw new Error("A common entry must be a directory");
+ }
+
+ if (!expectedEntry.entries && !sharedEntry.entries) {
+ throw new Error("A common entry must not be a leaf");
+ }
+
+ if (sharedEntry.entries) {
+ if (!expectedEntry.entries) {
+ expectedEntry.entries = [];
+ }
+
+ addSharedEntries(
+ expectedEntry.entries,
+ sharedEntry.entries,
+ sharedEntry.name,
+ indent
+ );
+ }
+ } else {
+ log(`${indent}Adding entry ${sharedEntry.name}`);
+ expectedEntries.push(sharedEntry);
+ }
+ }
+ }
+
+ function compareEntries(currentEntries, expectedEntries, name, indent = "") {
+ log(`${indent}Comparing ${name} entries`);
+
+ indent += " ";
+
+ if (currentEntries.length != expectedEntries.length) {
+ throw new Error("Entries must have the same length");
+ }
+
+ for (const currentEntry of currentEntries) {
+ log(`${indent}Comparing entry ${currentEntry.name}`);
+
+ const expectedEntry = expectedEntries.find(
+ ({ name: elementName }) => elementName == currentEntry.name
+ );
+
+ if (!expectedEntry) {
+ throw new Error("Cannot find a matching entry");
+ }
+
+ if (expectedEntry.dir != currentEntry.dir) {
+ throw new Error("The dir property doesn't match");
+ }
+
+ if (
+ (expectedEntry.entries && !currentEntry.entries) ||
+ (!expectedEntry.entries && currentEntry.entries)
+ ) {
+ throw new Error("The entries property doesn't match");
+ }
+
+ if (expectedEntry.entries) {
+ compareEntries(
+ currentEntry.entries,
+ expectedEntry.entries,
+ currentEntry.name,
+ indent
+ );
+ }
+ }
+ }
+
+ const currentEntries = getCurrentEntries();
+
+ log("Stringified current entries: " + JSON.stringify(currentEntries));
+
+ const expectedEntries = getEntriesFromPackageDefinition(
+ packageDefinitionRelativePaths[0],
+ key
+ );
+ const sharedEntries = getEntriesFromPackageDefinition(
+ packageDefinitionRelativePaths[1],
+ sharedKey ? sharedKey : key
+ );
+
+ addSharedEntries(expectedEntries, sharedEntries, key);
+
+ log("Stringified expected entries: " + JSON.stringify(expectedEntries));
+
+ compareEntries(currentEntries, expectedEntries, key);
+}
+
+async function verifyInitializationStatus(
+ expectStorageIsInitialized,
+ expectTemporaryStorageIsInitialized
+) {
+ if (!expectStorageIsInitialized && expectTemporaryStorageIsInitialized) {
+ throw new Error("Invalid expectation");
+ }
+
+ let request = storageInitialized();
+ await requestFinished(request);
+
+ const storageIsInitialized = request.result;
+
+ request = temporaryStorageInitialized();
+ await requestFinished(request);
+
+ const temporaryStorageIsInitialized = request.result;
+
+ ok(
+ !(!storageIsInitialized && temporaryStorageIsInitialized),
+ "Initialization status is consistent"
+ );
+
+ if (expectStorageIsInitialized) {
+ ok(storageIsInitialized, "Storage is initialized");
+ } else {
+ ok(!storageIsInitialized, "Storage is not initialized");
+ }
+
+ if (expectTemporaryStorageIsInitialized) {
+ ok(temporaryStorageIsInitialized, "Temporary storage is initialized");
+ } else {
+ ok(!temporaryStorageIsInitialized, "Temporary storage is not initialized");
+ }
+}
diff --git a/dom/quota/test/xpcshell/common/utils.js b/dom/quota/test/xpcshell/common/utils.js
new file mode 100644
index 0000000000..ee21c90cf2
--- /dev/null
+++ b/dom/quota/test/xpcshell/common/utils.js
@@ -0,0 +1,47 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+function getOriginDir(persistence, origin) {
+ return getRelativeFile(`storage/${persistence}/${origin}`);
+}
+
+function getMetadataFile(persistence, origin) {
+ const metadataFile = getOriginDir(persistence, origin);
+ metadataFile.append(".metadata-v2");
+ return metadataFile;
+}
+
+function populateRepository(persistence) {
+ const originDir = getOriginDir(persistence, "https+++good-example.com");
+ originDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+}
+
+function makeRepositoryUnusable(persistence) {
+ // For the purpose of testing, we make a repository unusable by creating an
+ // origin directory with the metadata file created as a directory (not a
+ // file).
+ const metadataFile = getMetadataFile(persistence, "https+++bad-example.com");
+ metadataFile.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+}
+
+async function fillOrigin(principal, size) {
+ let database = getSimpleDatabase(principal);
+
+ let request = database.open("data");
+ await requestFinished(request);
+
+ try {
+ request = database.write(getBuffer(size));
+ await requestFinished(request);
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ request = database.close();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/createLocalStorage_profile.zip b/dom/quota/test/xpcshell/createLocalStorage_profile.zip
new file mode 100644
index 0000000000..d5958dbd59
--- /dev/null
+++ b/dom/quota/test/xpcshell/createLocalStorage_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/defaultStorageDirectory_shared.json b/dom/quota/test/xpcshell/defaultStorageDirectory_shared.json
new file mode 100644
index 0000000000..6306bac35a
--- /dev/null
+++ b/dom/quota/test/xpcshell/defaultStorageDirectory_shared.json
@@ -0,0 +1,171 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInitTemporaryStorage",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ { "name": "ls-archive.sqlite", "dir": false },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "default",
+ "dir": true,
+ "todo": "Add entry invalid+++example.com once bug 1594075 is fixed",
+ "entries": [{ "name": "foo.bar", "dir": false }]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "todo": "Add entry invalid+++example.com once bug 1594075 is fixed",
+ "entries": [{ "name": "foo.bar", "dir": false }]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterClearPrivateBrowsing",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ { "name": "ls-archive.sqlite", "dir": false },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterClearByOriginPrefix",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ { "name": "ls-archive.sqlite", "dir": false },
+ {
+ "name": "permanent",
+ "dir": true,
+ "todo": "Add entry invalid+++example.com once bug 1594075 is fixed",
+ "entries": [{ "name": "foo.bar", "dir": false }]
+ },
+ {
+ "name": "default",
+ "dir": true,
+ "todo": "Add entry invalid+++example.com once bug 1594075 is fixed",
+ "entries": [{ "name": "foo.bar", "dir": false }]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "todo": "Add entry invalid+++example.com once bug 1594075 is fixed",
+ "entries": [{ "name": "foo.bar", "dir": false }]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/defaultStorageDirectory_shared.zip b/dom/quota/test/xpcshell/defaultStorageDirectory_shared.zip
new file mode 100644
index 0000000000..61e5b60a87
--- /dev/null
+++ b/dom/quota/test/xpcshell/defaultStorageDirectory_shared.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/getUsage_profile.zip b/dom/quota/test/xpcshell/getUsage_profile.zip
new file mode 100644
index 0000000000..5144112bde
--- /dev/null
+++ b/dom/quota/test/xpcshell/getUsage_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/groupMismatch_profile.zip b/dom/quota/test/xpcshell/groupMismatch_profile.zip
new file mode 100644
index 0000000000..182b013de0
--- /dev/null
+++ b/dom/quota/test/xpcshell/groupMismatch_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/head.js b/dom/quota/test/xpcshell/head.js
new file mode 100644
index 0000000000..bf9ba22ce3
--- /dev/null
+++ b/dom/quota/test/xpcshell/head.js
@@ -0,0 +1,14 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// The path to the top level directory.
+const depth = "../../../../";
+
+loadScript("dom/quota/test/xpcshell/common/head.js");
+
+function loadScript(path) {
+ let uri = Services.io.newFileURI(do_get_file(depth + path));
+ Services.scriptloader.loadSubScript(uri.spec);
+}
diff --git a/dom/quota/test/xpcshell/indexedDBDirectory_shared.json b/dom/quota/test/xpcshell/indexedDBDirectory_shared.json
new file mode 100644
index 0000000000..6e3d63bafc
--- /dev/null
+++ b/dom/quota/test/xpcshell/indexedDBDirectory_shared.json
@@ -0,0 +1,35 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "indexedDB",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/indexedDBDirectory_shared.zip b/dom/quota/test/xpcshell/indexedDBDirectory_shared.zip
new file mode 100644
index 0000000000..6b959e7525
--- /dev/null
+++ b/dom/quota/test/xpcshell/indexedDBDirectory_shared.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/make_unknownFiles.js b/dom/quota/test/xpcshell/make_unknownFiles.js
new file mode 100644
index 0000000000..b48cbd8ace
--- /dev/null
+++ b/dom/quota/test/xpcshell/make_unknownFiles.js
@@ -0,0 +1,176 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+async function testSteps() {
+ const principal = getPrincipal("http://example.com");
+
+ const repoRelativePath = "storage/default";
+ const originRelativePath = `${repoRelativePath}/http+++example.com`;
+
+ let unknownFileCounter = 1;
+ let unknownDirCounter = 1;
+
+ function createUnknownFileIn(dirRelativePath, recursive) {
+ const dir = getRelativeFile(dirRelativePath);
+
+ let file = dir.clone();
+ file.append("foo" + unknownFileCounter + ".bar");
+
+ const ostream = Cc[
+ "@mozilla.org/network/file-output-stream;1"
+ ].createInstance(Ci.nsIFileOutputStream);
+
+ ostream.init(file, -1, parseInt("0644", 8), 0);
+
+ ostream.write("x".repeat(unknownFileCounter), unknownFileCounter);
+
+ ostream.close();
+
+ unknownFileCounter++;
+
+ if (recursive) {
+ const entries = dir.directoryEntries;
+ while ((file = entries.nextFile)) {
+ if (file.isDirectory()) {
+ createUnknownFileIn(dirRelativePath + "/" + file.leafName);
+ }
+ }
+ }
+ }
+
+ function createUnknownDirectoryIn(dirRelativePath) {
+ createUnknownFileIn(dirRelativePath + "/foo" + unknownDirCounter++);
+ }
+
+ // storage.sqlite and storage/ls-archive.sqlite
+ {
+ const request = init();
+ await requestFinished(request);
+ }
+
+ // Unknown file in the repository
+ {
+ createUnknownFileIn(repoRelativePath);
+ }
+
+ // Unknown file and unknown directory in the origin directory
+ {
+ let request = init();
+ await requestFinished(request);
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ request = initTemporaryOrigin("default", principal);
+ await requestFinished(request);
+
+ Assert.strictEqual(
+ request.result,
+ true,
+ "The origin directory was created"
+ );
+
+ createUnknownFileIn(originRelativePath);
+ createUnknownDirectoryIn(originRelativePath);
+ }
+
+ // Unknown files in idb client directory and its subdirectories and unknown
+ // directory in .files directory
+ {
+ const request = indexedDB.openForPrincipal(principal, "myIndexedDB");
+ await openDBRequestUpgradeNeeded(request);
+
+ const database = request.result;
+
+ const objectStore = database.createObjectStore("Blobs", {});
+
+ objectStore.add(getNullBlob(200), 42);
+
+ await openDBRequestSucceeded(request);
+
+ database.close();
+
+ createUnknownFileIn(`${originRelativePath}/idb`);
+ createUnknownFileIn(
+ `${originRelativePath}/idb/2320029346mByDIdnedxe.files`
+ );
+ createUnknownDirectoryIn(
+ `${originRelativePath}/idb/2320029346mByDIdnedxe.files`
+ );
+ createUnknownFileIn(
+ `${originRelativePath}/idb/2320029346mByDIdnedxe.files/journals`
+ );
+ }
+
+ // Unknown files in cache client directory and its subdirectories
+ {
+ async function sandboxScript() {
+ const cache = await caches.open("myCache");
+ const request = new Request("http://example.com/index.html");
+ const response = new Response("hello world");
+ await cache.put(request, response);
+ }
+
+ const sandbox = new Cu.Sandbox(principal, {
+ wantGlobalProperties: ["caches", "fetch"],
+ });
+
+ const promise = new Promise(function (resolve, reject) {
+ sandbox.resolve = resolve;
+ sandbox.reject = reject;
+ });
+
+ Cu.evalInSandbox(
+ sandboxScript.toSource() + " sandboxScript().then(resolve, reject);",
+ sandbox
+ );
+ await promise;
+
+ createUnknownFileIn(`${originRelativePath}/cache`);
+ createUnknownFileIn(
+ `${originRelativePath}/cache/morgue`,
+ /* recursive */ true
+ );
+ }
+
+ // Unknown file and unknown directory in sdb client directory
+ {
+ const database = getSimpleDatabase(principal);
+
+ let request = database.open("mySimpleDB");
+ await requestFinished(request);
+
+ request = database.write(getBuffer(100));
+ await requestFinished(request);
+
+ request = database.close();
+ await requestFinished(request);
+
+ createUnknownFileIn(`${originRelativePath}/sdb`);
+ createUnknownDirectoryIn(`${originRelativePath}/sdb`);
+ }
+
+ // Unknown file and unknown directory in ls client directory
+ {
+ Services.prefs.setBoolPref("dom.storage.testing", true);
+ Services.prefs.setBoolPref("dom.storage.client_validation", false);
+
+ const storage = Services.domStorageManager.createStorage(
+ null,
+ principal,
+ principal,
+ ""
+ );
+
+ storage.setItem("foo", "bar");
+
+ storage.close();
+
+ createUnknownFileIn(`${originRelativePath}/ls`);
+ createUnknownDirectoryIn(`${originRelativePath}/ls`);
+ }
+}
diff --git a/dom/quota/test/xpcshell/make_unsetLastAccessTime.js b/dom/quota/test/xpcshell/make_unsetLastAccessTime.js
new file mode 100644
index 0000000000..9be377e4f3
--- /dev/null
+++ b/dom/quota/test/xpcshell/make_unsetLastAccessTime.js
@@ -0,0 +1,25 @@
+/*
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+async function testSteps() {
+ const originDirPath = "storage/default/https+++foo.example.com";
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Creating an empty origin directory");
+
+ let originDir = getRelativeFile(originDirPath);
+ originDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ // The metadata file should be now restored.
+}
diff --git a/dom/quota/test/xpcshell/originMismatch_profile.json b/dom/quota/test/xpcshell/originMismatch_profile.json
new file mode 100644
index 0000000000..cbeaef728a
--- /dev/null
+++ b/dom/quota/test/xpcshell/originMismatch_profile.json
@@ -0,0 +1,77 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.example.com",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata-v2", "dir": false },
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [{ "name": ".padding", "dir": false }]
+ }
+ ]
+ },
+ { "name": "http+++www.example.com.", "dir": true },
+ {
+ "name": "http+++www.example.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata-v2", "dir": false },
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [{ "name": ".padding", "dir": false }]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInitTemporaryStorage",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.example.com.",
+ "dir": true,
+ "entries": [{ "name": ".metadata-v2", "dir": false }]
+ },
+ {
+ "name": "http+++www.example.org.",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata-v2", "dir": false },
+ { "name": "cache", "dir": true }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/originMismatch_profile.zip b/dom/quota/test/xpcshell/originMismatch_profile.zip
new file mode 100644
index 0000000000..dd5795736f
--- /dev/null
+++ b/dom/quota/test/xpcshell/originMismatch_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/persistentStorageDirectory_shared.json b/dom/quota/test/xpcshell/persistentStorageDirectory_shared.json
new file mode 100644
index 0000000000..8d36293bbf
--- /dev/null
+++ b/dom/quota/test/xpcshell/persistentStorageDirectory_shared.json
@@ -0,0 +1,57 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ { "name": "invalid+++example.com", "dir": true },
+ { "name": "foo.bar", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/persistentStorageDirectory_shared.zip b/dom/quota/test/xpcshell/persistentStorageDirectory_shared.zip
new file mode 100644
index 0000000000..b80bfe904b
--- /dev/null
+++ b/dom/quota/test/xpcshell/persistentStorageDirectory_shared.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/removeLocalStorage1_profile.zip b/dom/quota/test/xpcshell/removeLocalStorage1_profile.zip
new file mode 100644
index 0000000000..19e971433c
--- /dev/null
+++ b/dom/quota/test/xpcshell/removeLocalStorage1_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/removeLocalStorage2_profile.zip b/dom/quota/test/xpcshell/removeLocalStorage2_profile.zip
new file mode 100644
index 0000000000..04d1a3462b
--- /dev/null
+++ b/dom/quota/test/xpcshell/removeLocalStorage2_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/head.js b/dom/quota/test/xpcshell/telemetry/head.js
new file mode 100644
index 0000000000..5c36d82ca6
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/head.js
@@ -0,0 +1,14 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// The path to the top level directory.
+const depth = "../../../../../";
+
+loadScript("dom/quota/test/xpcshell/common/head.js");
+
+function loadScript(path) {
+ let uri = Services.io.newFileURI(do_get_file(depth + path));
+ Services.scriptloader.loadSubScript(uri.spec);
+}
diff --git a/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js
new file mode 100644
index 0000000000..9a3afba8c5
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js
@@ -0,0 +1,866 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+const storageDirName = "storage";
+const storageFileName = "storage.sqlite";
+const indexedDBDirName = "indexedDB";
+const persistentStorageDirName = "storage/persistent";
+const histogramName = "QM_FIRST_INITIALIZATION_ATTEMPT";
+
+const testcases = [
+ {
+ mainKey: "Storage",
+ async setup(expectedInitResult) {
+ if (!expectedInitResult) {
+ // Make the database unusable by creating it as a directory (not a
+ // file).
+ const storageFile = getRelativeFile(storageFileName);
+ storageFile.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryStorage",
+ async setup(expectedInitResult) {
+ // We need to initialize storage before populating the repositories. If
+ // we don't do that, the storage directory created for the repositories
+ // would trigger storage upgrades (from version 0 to current version).
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("temporary");
+ populateRepository("default");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("temporary");
+ makeRepositoryUnusable("default");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ getExpectedSnapshots() {
+ const expectedSnapshotsInNightly = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ DefaultRepository: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [1, 1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ const expectedSnapshotsInOthers = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ return AppConstants.NIGHTLY_BUILD
+ ? expectedSnapshotsInNightly
+ : expectedSnapshotsInOthers;
+ },
+ },
+ {
+ mainKey: "DefaultRepository",
+ async setup(expectedInitResult) {
+ // See the comment for "TemporaryStorage".
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("default");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("default");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ DefaultRepository: {
+ values: [1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ DefaultRepository: {
+ values: [1, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryRepository",
+ async setup(expectedInitResult) {
+ // See the comment for "TemporaryStorage".
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("temporary");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("temporary");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ getExpectedSnapshots() {
+ const expectedSnapshotsInNightly = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ const expectedSnapshotsInOthers = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ return AppConstants.NIGHTLY_BUILD
+ ? expectedSnapshotsInNightly
+ : expectedSnapshotsInOthers;
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom0_0To1_0",
+ async setup(expectedInitResult) {
+ // storage used prior FF 49 (storage version 0.0)
+ installPackage("version0_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version0_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom0_0To1_0: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom0_0To1_0: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom1_0To2_0",
+ async setup(expectedInitResult) {
+ // storage used by FF 49-54 (storage version 1.0)
+ installPackage("version1_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version1_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom1_0To2_0: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom1_0To2_0: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_0To2_1",
+ async setup(expectedInitResult) {
+ // storage used by FF 55-56 (storage version 2.0)
+ installPackage("version2_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version2_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_0To2_1: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_0To2_1: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_1To2_2",
+ async setup(expectedInitResult) {
+ // storage used by FF 57-67 (storage version 2.1)
+ installPackage("version2_1_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version2_1_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_1To2_2: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_1To2_2: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_2To2_3",
+ async setup(expectedInitResult) {
+ // storage used by FF 68-69 (storage version 2.2)
+ installPackage("version2_2_profile");
+
+ if (!expectedInitResult) {
+ installPackage(
+ "version2_2_make_it_unusable",
+ /* allowFileOverwrites */ true
+ );
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_2To2_3: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_2To2_3: {
+ values: [1, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeFromIndexedDBDirectory",
+ async setup(expectedInitResult) {
+ const indexedDBDir = getRelativeFile(indexedDBDirName);
+ indexedDBDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ if (!expectedInitResult) {
+ // "indexedDB" directory will be moved under "storage" directory and at
+ // the same time renamed to "persistent". Create a storage file to cause
+ // the moves to fail.
+ const storageFile = getRelativeFile(storageDirName);
+ storageFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeFromIndexedDBDirectory: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeFromIndexedDBDirectory: {
+ values: [1, 1, 0],
+ },
+ UpgradeFromPersistentStorageDirectory: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom0_0To1_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeFromPersistentStorageDirectory",
+ async setup(expectedInitResult) {
+ const persistentStorageDir = getRelativeFile(persistentStorageDirName);
+ persistentStorageDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ if (!expectedInitResult) {
+ // Create a metadata directory to break creating or upgrading directory
+ // metadata files.
+ const metadataDir = getRelativeFile(
+ "storage/persistent/https+++bad.example.com/.metadata"
+ );
+ metadataDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeFromPersistentStorageDirectory: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeFromPersistentStorageDirectory: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom0_0To1_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "PersistentOrigin",
+ async setup(expectedInitResult) {
+ // We need to initialize storage before creating the origin files. If we
+ // don't do that, the storage directory created for the origin files
+ // would trigger storage upgrades (from version 0 to current version).
+ let request = init();
+ await requestFinished(request);
+
+ if (!expectedInitResult) {
+ const originFiles = [
+ getRelativeFile("storage/permanent/https+++example.com"),
+ getRelativeFile("storage/permanent/https+++example1.com"),
+ getRelativeFile("storage/default/https+++example2.com"),
+ ];
+
+ for (const originFile of originFiles) {
+ originFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ }
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ },
+ initFunctions: [
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example.com")],
+ },
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example1.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example2.com")],
+ },
+ ],
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ PersistentOrigin: {
+ values: [2, 0],
+ },
+ TemporaryOrigin: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ PersistentOrigin: {
+ values: [2, 2, 0],
+ },
+ TemporaryOrigin: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryOrigin",
+ async setup(expectedInitResult) {
+ // See the comment for "PersistentOrigin".
+ let request = init();
+ await requestFinished(request);
+
+ if (!expectedInitResult) {
+ const originFiles = [
+ getRelativeFile("storage/temporary/https+++example.com"),
+ getRelativeFile("storage/default/https+++example.com"),
+ getRelativeFile("storage/default/https+++example1.com"),
+ getRelativeFile("storage/permanent/https+++example2.com"),
+ ];
+
+ for (const originFile of originFiles) {
+ originFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ }
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ },
+ initFunctions: [
+ {
+ name: initTemporaryOrigin,
+ args: ["temporary", getPrincipal("https://example.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example1.com")],
+ },
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example2.com")],
+ },
+ ],
+ // Only the first result of EnsureTemporaryOriginIsInitialized per origin
+ // should be reported. Thus, only the results for (temporary, example.com),
+ // and (default, example1.com) should be reported.
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 1, 0],
+ },
+ PersistentOrigin: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryOrigin: {
+ values: [2, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 2, 0],
+ },
+ PersistentOrigin: {
+ values: [1, 1, 0],
+ },
+ // mainKey
+ TemporaryOrigin: {
+ values: [2, 2, 0],
+ },
+ },
+ },
+ },
+];
+
+loadScript("dom/quota/test/xpcshell/common/utils.js");
+
+function verifyHistogram(histogram, mainKey, expectedSnapshot) {
+ const snapshot = histogram.snapshot();
+
+ ok(
+ mainKey in snapshot,
+ `The histogram ${histogram.name()} must contain the main key ${mainKey}`
+ );
+
+ const keys = Object.keys(snapshot);
+
+ is(
+ keys.length,
+ Object.keys(expectedSnapshot).length,
+ `The number of keys must match the expected number of keys for ` +
+ `${histogram.name()}`
+ );
+
+ for (const key of keys) {
+ ok(
+ key in expectedSnapshot,
+ `The key ${key} must match the expected keys for ${histogram.name()}`
+ );
+
+ const values = Object.entries(snapshot[key].values);
+ const expectedValues = expectedSnapshot[key].values;
+
+ is(
+ values.length,
+ expectedValues.length,
+ `The number of values should match the expected number of values for ` +
+ `${histogram.name()}`
+ );
+
+ for (let [i, val] of values) {
+ is(
+ val,
+ expectedValues[i],
+ `Expected counts should match for ${histogram.name()} at index ${i}`
+ );
+ }
+ }
+}
+
+async function testSteps() {
+ let request;
+ for (const testcase of testcases) {
+ const mainKey = testcase.mainKey;
+
+ info(`Verifying ${histogramName} histogram for the main key ${mainKey}`);
+
+ const histogram =
+ TelemetryTestUtils.getAndClearKeyedHistogram(histogramName);
+
+ for (const expectedInitResult of [false, true]) {
+ info(
+ `Verifying the histogram when the initialization ` +
+ `${expectedInitResult ? "failed and then succeeds" : "fails"}`
+ );
+
+ await testcase.setup(expectedInitResult);
+
+ const msg = `Should ${expectedInitResult ? "not " : ""} have thrown`;
+
+ // Call the initialization function twice, so we can verify below that
+ // only the first initialization attempt has been reported.
+ for (let i = 0; i < 2; ++i) {
+ let initFunctions;
+
+ if (testcase.initFunctions) {
+ initFunctions = testcase.initFunctions;
+ } else {
+ initFunctions = [
+ {
+ name: testcase.initFunction,
+ args: [],
+ },
+ ];
+ }
+
+ for (const initFunction of initFunctions) {
+ request = initFunction.name(...initFunction.args);
+ try {
+ await requestFinished(request);
+ ok(expectedInitResult, msg);
+ } catch (ex) {
+ ok(!expectedInitResult, msg);
+ }
+ }
+ }
+
+ const expectedSnapshots = testcase.getExpectedSnapshots
+ ? testcase.getExpectedSnapshots()
+ : testcase.expectedSnapshots;
+
+ const expectedSnapshot = expectedInitResult
+ ? expectedSnapshots.initFailureThenSuccess
+ : expectedSnapshots.initFailure;
+
+ verifyHistogram(histogram, mainKey, expectedSnapshot);
+
+ // The first initialization attempt has been reported in the histogram
+ // and any new attemps wouldn't be reported if we didn't reset or clear
+ // the storage here. We need a clean profile for the next iteration
+ // anyway.
+ // However, the clear storage operation needs initialized storage, so
+ // clearing can fail if the storage is unusable and it can also increase
+ // some of the telemetry counters. Instead of calling clear, we can just
+ // call reset and clear profile manually.
+ request = reset();
+ await requestFinished(request);
+
+ const indexedDBDir = getRelativeFile(indexedDBDirName);
+ if (indexedDBDir.exists()) {
+ indexedDBDir.remove(false);
+ }
+
+ const storageDir = getRelativeFile(storageDirName);
+ if (storageDir.exists()) {
+ storageDir.remove(true);
+ }
+
+ const storageFile = getRelativeFile(storageFileName);
+ if (storageFile.exists()) {
+ // It could be a non empty directory, so remove it recursively.
+ storageFile.remove(true);
+ }
+ }
+ }
+}
diff --git a/dom/quota/test/xpcshell/telemetry/version0_0_make_it_unusable.zip b/dom/quota/test/xpcshell/telemetry/version0_0_make_it_unusable.zip
new file mode 100644
index 0000000000..92dcfb777e
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version0_0_make_it_unusable.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version0_0_profile.zip b/dom/quota/test/xpcshell/telemetry/version0_0_profile.zip
new file mode 100644
index 0000000000..2fb0f525c2
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version0_0_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version1_0_make_it_unusable.zip b/dom/quota/test/xpcshell/telemetry/version1_0_make_it_unusable.zip
new file mode 100644
index 0000000000..92dcfb777e
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version1_0_make_it_unusable.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version1_0_profile.zip b/dom/quota/test/xpcshell/telemetry/version1_0_profile.zip
new file mode 100644
index 0000000000..6169f439c1
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version1_0_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_0_make_it_unusable.zip b/dom/quota/test/xpcshell/telemetry/version2_0_make_it_unusable.zip
new file mode 100644
index 0000000000..92dcfb777e
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_0_make_it_unusable.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_0_profile.zip b/dom/quota/test/xpcshell/telemetry/version2_0_profile.zip
new file mode 100644
index 0000000000..465f53cea9
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_0_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_1_make_it_unusable.zip b/dom/quota/test/xpcshell/telemetry/version2_1_make_it_unusable.zip
new file mode 100644
index 0000000000..92dcfb777e
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_1_make_it_unusable.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_1_profile.zip b/dom/quota/test/xpcshell/telemetry/version2_1_profile.zip
new file mode 100644
index 0000000000..81463235ab
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_1_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_2_make_it_unusable.zip b/dom/quota/test/xpcshell/telemetry/version2_2_make_it_unusable.zip
new file mode 100644
index 0000000000..b6b8eecabf
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_2_make_it_unusable.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/version2_2_profile.zip b/dom/quota/test/xpcshell/telemetry/version2_2_profile.zip
new file mode 100644
index 0000000000..e572726cca
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/version2_2_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/telemetry/xpcshell.toml b/dom/quota/test/xpcshell/telemetry/xpcshell.toml
new file mode 100644
index 0000000000..949ef3cb06
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/xpcshell.toml
@@ -0,0 +1,17 @@
+[DEFAULT]
+head = "head.js"
+support-files = [
+ "version0_0_make_it_unusable.zip",
+ "version0_0_profile.zip",
+ "version1_0_make_it_unusable.zip",
+ "version1_0_profile.zip",
+ "version2_0_make_it_unusable.zip",
+ "version2_0_profile.zip",
+ "version2_1_make_it_unusable.zip",
+ "version2_1_profile.zip",
+ "version2_2_make_it_unusable.zip",
+ "version2_2_profile.zip",
+]
+
+["test_qm_first_initialization_attempt.js"]
+skip-if = ["appname == 'thunderbird'"]
diff --git a/dom/quota/test/xpcshell/tempMetadataCleanup_profile.zip b/dom/quota/test/xpcshell/tempMetadataCleanup_profile.zip
new file mode 100644
index 0000000000..da1de0979b
--- /dev/null
+++ b/dom/quota/test/xpcshell/tempMetadataCleanup_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/test_allowListFiles.js b/dom/quota/test/xpcshell/test_allowListFiles.js
new file mode 100644
index 0000000000..04c64c2ef5
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_allowListFiles.js
@@ -0,0 +1,61 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify thoes unexpected files are in the allow list of
+ * QuotaManager. They aren't expected in the repository but if there are,
+ * QuotaManager shouldn't fail to initialize an origin and getting usage, though
+ * those files aren't managed by QuotaManager.
+ */
+
+async function testSteps() {
+ const allowListFiles = [
+ ".dot-file",
+ "desktop.ini",
+ "Desktop.ini",
+ "Thumbs.db",
+ "thumbs.db",
+ ];
+
+ for (let allowListFile of allowListFiles) {
+ info("Testing " + allowListFile + " in the repository");
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Creating unknown files");
+
+ for (let dir of ["persistenceType dir", "origin dir"]) {
+ let dirPath =
+ dir == "persistenceType dir"
+ ? "storage/default/"
+ : "storage/default/http+++example.com/";
+ let file = getRelativeFile(dirPath + allowListFile);
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Resetting");
+
+ request = reset();
+ await requestFinished(request);
+
+ info("Getting usage");
+
+ request = getCurrentUsage(continueToNextStepSync);
+ await requestFinished(request);
+
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+ }
+}
diff --git a/dom/quota/test/xpcshell/test_bad_origin_directory.js b/dom/quota/test/xpcshell/test_bad_origin_directory.js
new file mode 100644
index 0000000000..303fd82bd8
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_bad_origin_directory.js
@@ -0,0 +1,37 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const invalidOrigin = {
+ url: "ws://ws.invalid.origin",
+ path: "storage/default/ws+++ws.invalid.origin",
+ };
+
+ info("Persisting an invalid origin");
+
+ let invalidPrincipal = getPrincipal(invalidOrigin.url);
+
+ let request = persist(invalidPrincipal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(
+ request.resultCode,
+ NS_ERROR_FAILURE,
+ "Persist() failed because of the invalid origin"
+ );
+ Assert.strictEqual(request.result, null, "The request result is null");
+
+ let originDir = getRelativeFile(invalidOrigin.path);
+ let exists = originDir.exists();
+ ok(!exists, "Directory for invalid origin doesn't exist");
+
+ request = persisted(invalidPrincipal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persisted() succeeded");
+ ok(!request.result, "The origin isn't persisted since the operation failed");
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_basics.js b/dom/quota/test/xpcshell/test_basics.js
new file mode 100644
index 0000000000..efe5aa7926
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_basics.js
@@ -0,0 +1,143 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const storageFile = "storage.sqlite";
+
+ const metadataFiles = [
+ {
+ path: "storage/permanent/chrome/.metadata",
+ shouldExistAfterInit: false,
+ },
+
+ {
+ path: "storage/permanent/chrome/.metadata-tmp",
+ shouldExistAfterInit: false,
+ },
+
+ {
+ path: "storage/permanent/chrome/.metadata-v2",
+ shouldExistAfterInit: true,
+ },
+
+ {
+ path: "storage/permanent/chrome/.metadata-v2-tmp",
+ shouldExistAfterInit: false,
+ },
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(false, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Getting usage");
+
+ getCurrentUsage(grabUsageAndContinueHandler);
+ let usage = yield undefined;
+
+ Assert.equal(usage, 0, "Usage is zero");
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(true, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(false, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Installing package");
+
+ // The profile contains just one empty IndexedDB database. The file
+ // create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/quota/test/xpcshell/create_db.js
+ installPackage("basics_profile");
+
+ info("Getting usage");
+
+ getCurrentUsage(grabUsageAndContinueHandler);
+ usage = yield undefined;
+
+ Assert.greater(usage, 0, "Usage is not zero");
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(true, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Checking storage file");
+
+ let file = getRelativeFile(storageFile);
+
+ let exists = file.exists();
+ ok(!exists, "Storage file doesn't exist");
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(false, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Initializing");
+
+ request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ exists = file.exists();
+ ok(exists, "Storage file does exist");
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(true, false).then(continueToNextStepSync);
+ yield undefined;
+
+ info("Initializing origin");
+
+ request = initPersistentOrigin(getCurrentPrincipal(), continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(request.result, "Origin directory was created");
+
+ for (let metadataFile of metadataFiles) {
+ file = getRelativeFile(metadataFile.path);
+
+ exists = file.exists();
+
+ if (metadataFile.shouldExistAfterInit) {
+ ok(exists, "Metadata file does exist");
+ } else {
+ ok(!exists, "Metadata file doesn't exist");
+ }
+ }
+
+ info("Verifying initialization status");
+
+ verifyInitializationStatus(true, false).then(continueToNextStepSync);
+
+ yield undefined;
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_clearStoragesForOriginAttributesPattern.js b/dom/quota/test/xpcshell/test_clearStoragesForOriginAttributesPattern.js
new file mode 100644
index 0000000000..096cf2be70
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_clearStoragesForOriginAttributesPattern.js
@@ -0,0 +1,58 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const baseRelativePath = "storage/default";
+ const userContextForRemoval = 2;
+
+ const origins = [
+ {
+ userContextId: 1,
+ baseDirName: "https+++example.com",
+ },
+
+ {
+ userContextId: userContextForRemoval,
+ baseDirName: "https+++example.com",
+ },
+
+ // TODO: Uncomment this once bug 1638831 is fixed.
+ /*
+ {
+ userContextId: userContextForRemoval,
+ baseDirName: "https+++example.org",
+ },
+ */
+ ];
+
+ function getOriginDirectory(origin) {
+ return getRelativeFile(
+ `${baseRelativePath}/${origin.baseDirName}^userContextId=` +
+ `${origin.userContextId}`
+ );
+ }
+
+ let request = init();
+ await requestFinished(request);
+
+ for (const origin of origins) {
+ const directory = getOriginDirectory(origin);
+ directory.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+ }
+
+ request = Services.qms.clearStoragesForOriginAttributesPattern(
+ `{ "userContextId": ${userContextForRemoval} }`
+ );
+ await requestFinished(request);
+
+ for (const origin of origins) {
+ const directory = getOriginDirectory(origin);
+ if (origin.userContextId === userContextForRemoval) {
+ ok(!directory.exists(), "Origin directory should have been removed");
+ } else {
+ ok(directory.exists(), "Origin directory shouldn't have been removed");
+ }
+ }
+}
diff --git a/dom/quota/test/xpcshell/test_clearStoragesForOriginPrefix.js b/dom/quota/test/xpcshell/test_clearStoragesForOriginPrefix.js
new file mode 100644
index 0000000000..161dd9722c
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_clearStoragesForOriginPrefix.js
@@ -0,0 +1,72 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify clearing by origin prefix.
+ */
+
+async function testSteps() {
+ const packages = [
+ "clearStoragesForOriginPrefix_profile",
+ "defaultStorageDirectory_shared",
+ ];
+
+ const testData = [
+ {
+ origin: "http://example.com",
+ persistence: null,
+ key: "afterClearByOriginPrefix",
+ },
+ {
+ origin: "http://example.com",
+ persistence: "default",
+ key: "afterClearByOriginPrefix_default",
+ },
+ {
+ origin: "http://example.com",
+ persistence: "persistent",
+ key: "afterClearByOriginPrefix_persistent",
+ },
+ {
+ origin: "http://example.com",
+ persistence: "temporary",
+ key: "afterClearByOriginPrefix_temporary",
+ },
+ ];
+
+ for (const item of testData) {
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing package");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ // TODO: Remove this block once origin clearing is able to ignore unknown
+ // directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/permanent/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Clearing origin by prefix");
+
+ request = clearOriginsByPrefix(getPrincipal(item.origin), item.persistence);
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, item.key, "afterClearByOriginPrefix");
+ }
+}
diff --git a/dom/quota/test/xpcshell/test_clearStoragesForPrincipal.js b/dom/quota/test/xpcshell/test_clearStoragesForPrincipal.js
new file mode 100644
index 0000000000..1e8bed54b7
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_clearStoragesForPrincipal.js
@@ -0,0 +1,56 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is an xpcshell test for clearStoragesForPrincipal. It verifies that
+ * if the removing client is the last client in the targeting origin, then it
+ * is expected to remove the origin directory as well.
+ */
+
+async function testSteps() {
+ const testingOrigins = [
+ {
+ origin: "http://example.com",
+ path: "storage/default/http+++example.com/",
+ only_idb: false,
+ },
+ {
+ origin: "http://www.mozilla.org",
+ path: "storage/default/http+++www.mozilla.org/",
+ only_idb: true,
+ },
+ ];
+ const removingClient = "idb";
+
+ info("Installing package to create the environment");
+ // The package is manually created and it contains:
+ // - storage/default/http+++www.mozilla.org/idb/
+ // - storage/default/http+++www.example.com/idb/
+ // - storage/default/http+++www.example.com/cache/
+ installPackage("clearStoragesForPrincipal_profile");
+
+ let request;
+ let file;
+ for (let i = 0; i < testingOrigins.length; ++i) {
+ info("Clearing");
+ request = clearClient(
+ getPrincipal(testingOrigins[i].origin),
+ null,
+ removingClient
+ );
+ await requestFinished(request);
+
+ info("Verifying");
+ file = getRelativeFile(testingOrigins[i].path + removingClient);
+ ok(!file.exists(), "Client file doesn't exist");
+
+ file = getRelativeFile(testingOrigins[i].path);
+ if (testingOrigins[i].only_idb) {
+ todo(!file.exists(), "Origin file doesn't exist");
+ } else {
+ ok(file.exists(), "Origin file does exist");
+ }
+ }
+}
diff --git a/dom/quota/test/xpcshell/test_clearStoragesForPrivateBrowsing.js b/dom/quota/test/xpcshell/test_clearStoragesForPrivateBrowsing.js
new file mode 100644
index 0000000000..6a94e5c98e
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_clearStoragesForPrivateBrowsing.js
@@ -0,0 +1,41 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify clearing of storages for private browsing.
+ */
+
+async function testSteps() {
+ const packages = [
+ "clearStoragesForPrivateBrowsing_profile",
+ "defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing package");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Clearing private browsing");
+
+ request = clearPrivateBrowsing();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterClearPrivateBrowsing");
+}
diff --git a/dom/quota/test/xpcshell/test_createLocalStorage.js b/dom/quota/test/xpcshell/test_createLocalStorage.js
new file mode 100644
index 0000000000..8394a7d235
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_createLocalStorage.js
@@ -0,0 +1,155 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const webAppsStoreFile = "webappsstore.sqlite";
+ const lsArchiveFile = "storage/ls-archive.sqlite";
+ const lsArchiveTmpFile = "storage/ls-archive-tmp.sqlite";
+
+ function checkArchiveFileNotExists() {
+ info("Checking archive tmp file");
+
+ let archiveTmpFile = getRelativeFile(lsArchiveTmpFile);
+
+ let exists = archiveTmpFile.exists();
+ ok(!exists, "archive tmp file doesn't exist");
+
+ info("Checking archive file");
+
+ let archiveFile = getRelativeFile(lsArchiveFile);
+
+ exists = archiveFile.exists();
+ ok(!exists, "archive file doesn't exist");
+ }
+
+ function checkArchiveFileExists() {
+ info("Checking archive tmp file");
+
+ let archiveTmpFile = getRelativeFile(lsArchiveTmpFile);
+
+ let exists = archiveTmpFile.exists();
+ ok(!exists, "archive tmp file doesn't exist");
+
+ info("Checking archive file");
+
+ let archiveFile = getRelativeFile(lsArchiveFile);
+
+ exists = archiveFile.exists();
+ ok(exists, "archive file does exist");
+
+ info("Checking archive file size");
+
+ let fileSize = archiveFile.fileSize;
+ Assert.greater(fileSize, 0, "archive file size is greater than zero");
+ }
+
+ // Profile 1 - Nonexistent apps store file.
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ let appsStoreFile = getRelativeFile(webAppsStoreFile);
+
+ let exists = appsStoreFile.exists();
+ ok(!exists, "apps store file doesn't exist");
+
+ checkArchiveFileNotExists();
+
+ try {
+ request = init();
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ checkArchiveFileExists();
+
+ // Profile 2 - apps store file is a directory.
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+
+ appsStoreFile.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+
+ checkArchiveFileNotExists();
+
+ try {
+ request = init();
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ checkArchiveFileExists();
+
+ appsStoreFile.remove(true);
+
+ // Profile 3 - Corrupted apps store file.
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+
+ let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ ostream.init(appsStoreFile, -1, parseInt("0644", 8), 0);
+ ostream.write("foobar", 6);
+ ostream.close();
+
+ checkArchiveFileNotExists();
+
+ try {
+ request = init();
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ checkArchiveFileExists();
+
+ appsStoreFile.remove(false);
+
+ // Profile 4 - Nonupdateable apps store file.
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains storage.sqlite and webappsstore.sqlite
+ // webappstore.sqlite was taken from FF 54 to force an upgrade.
+ // There's just one record in the webappsstore2 table. The record was
+ // modified by renaming the origin attribute userContextId to userContextKey.
+ // This triggers an error during the upgrade.
+ installPackage("createLocalStorage_profile");
+
+ let fileSize = appsStoreFile.fileSize;
+ Assert.greater(fileSize, 0, "apps store file size is greater than zero");
+
+ checkArchiveFileNotExists();
+
+ try {
+ request = init();
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ checkArchiveFileExists();
+
+ appsStoreFile.remove(false);
+}
diff --git a/dom/quota/test/xpcshell/test_estimateOrigin.js b/dom/quota/test/xpcshell/test_estimateOrigin.js
new file mode 100644
index 0000000000..31d6f01686
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_estimateOrigin.js
@@ -0,0 +1,80 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+loadScript("dom/quota/test/xpcshell/common/utils.js");
+
+async function verifyOriginEstimation(principal, expectedUsage, expectedLimit) {
+ info("Estimating origin");
+
+ const request = estimateOrigin(principal);
+ await requestFinished(request);
+
+ is(request.result.usage, expectedUsage, "Correct usage");
+ is(request.result.limit, expectedLimit, "Correct limit");
+}
+
+async function testSteps() {
+ // The group limit is calculated as 20% of the global limit and the minimum
+ // value of the group limit is 10 MB.
+
+ const groupLimitKB = 10 * 1024;
+ const groupLimitBytes = groupLimitKB * 1024;
+ const globalLimitKB = groupLimitKB * 5;
+ const globalLimitBytes = globalLimitKB * 1024;
+
+ info("Setting limits");
+
+ setGlobalLimit(globalLimitKB);
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Filling origins");
+
+ await fillOrigin(getPrincipal("https://foo1.example1.com"), 100);
+ await fillOrigin(getPrincipal("https://foo2.example1.com"), 200);
+ await fillOrigin(getPrincipal("https://foo1.example2.com"), 300);
+ await fillOrigin(getPrincipal("https://foo2.example2.com"), 400);
+
+ info("Verifying origin estimations");
+
+ await verifyOriginEstimation(
+ getPrincipal("https://foo1.example1.com"),
+ 300,
+ groupLimitBytes
+ );
+ await verifyOriginEstimation(
+ getPrincipal("https://foo2.example1.com"),
+ 300,
+ groupLimitBytes
+ );
+ await verifyOriginEstimation(
+ getPrincipal("https://foo1.example2.com"),
+ 700,
+ groupLimitBytes
+ );
+ await verifyOriginEstimation(
+ getPrincipal("https://foo2.example2.com"),
+ 700,
+ groupLimitBytes
+ );
+
+ info("Persisting origin");
+
+ request = persist(getPrincipal("https://foo2.example2.com"));
+ await requestFinished(request);
+
+ info("Verifying origin estimation");
+
+ await verifyOriginEstimation(
+ getPrincipal("https://foo2.example2.com"),
+ 1000,
+ globalLimitBytes
+ );
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_getUsage.js b/dom/quota/test/xpcshell/test_getUsage.js
new file mode 100644
index 0000000000..538af934f6
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_getUsage.js
@@ -0,0 +1,133 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const origins = [
+ {
+ origin: "http://example.com",
+ persisted: false,
+ usage: 49152,
+ },
+
+ {
+ origin: "http://localhost",
+ persisted: false,
+ usage: 147456,
+ },
+
+ {
+ origin: "http://www.mozilla.org",
+ persisted: true,
+ usage: 98304,
+ },
+ ];
+
+ const allOrigins = [
+ {
+ origin: "chrome",
+ persisted: false,
+ usage: 147456,
+ },
+
+ {
+ origin: "http://example.com",
+ persisted: false,
+ usage: 49152,
+ },
+
+ {
+ origin: "http://localhost",
+ persisted: false,
+ usage: 147456,
+ },
+
+ {
+ origin: "http://www.mozilla.org",
+ persisted: true,
+ usage: 98304,
+ },
+ ];
+
+ function verifyResult(result, expectedOrigins) {
+ ok(result instanceof Array, "Got an array object");
+ Assert.equal(
+ result.length,
+ expectedOrigins.length,
+ "Correct number of elements"
+ );
+
+ info("Sorting elements");
+
+ result.sort(function (a, b) {
+ let originA = a.origin;
+ let originB = b.origin;
+
+ if (originA < originB) {
+ return -1;
+ }
+ if (originA > originB) {
+ return 1;
+ }
+ return 0;
+ });
+
+ info("Verifying elements");
+
+ for (let i = 0; i < result.length; i++) {
+ let a = result[i];
+ let b = expectedOrigins[i];
+ Assert.equal(a.origin, b.origin, "Origin equals");
+ Assert.equal(a.persisted, b.persisted, "Persisted equals");
+ Assert.equal(a.usage, b.usage, "Usage equals");
+ }
+ }
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Getting usage");
+
+ getUsage(grabResultAndContinueHandler, /* getAll */ true);
+ let result = yield undefined;
+
+ info("Verifying result");
+
+ verifyResult(result, []);
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Installing package");
+
+ // The profile contains IndexedDB databases placed across the repositories.
+ // The file create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/quota/test/xpcshell/create_db.js
+ installPackage("getUsage_profile");
+
+ info("Getting usage");
+
+ getUsage(grabResultAndContinueHandler, /* getAll */ false);
+ result = yield undefined;
+
+ info("Verifying result");
+
+ verifyResult(result, origins);
+
+ info("Getting usage");
+
+ getUsage(grabResultAndContinueHandler, /* getAll */ true);
+ result = yield undefined;
+
+ info("Verifying result");
+
+ verifyResult(result, allOrigins);
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_groupMismatch.js b/dom/quota/test/xpcshell/test_groupMismatch.js
new file mode 100644
index 0000000000..41a72d51ad
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_groupMismatch.js
@@ -0,0 +1,74 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that metadata files with old group information
+ * get updated. See bug 1535995.
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+async function testSteps() {
+ const metadataFile = getRelativeFile(
+ "storage/default/https+++foo.bar.mozilla-iot.org/.metadata-v2"
+ );
+
+ async function readMetadataFile() {
+ let file = await File.createFromNsIFile(metadataFile);
+
+ let buffer = await new Promise(resolve => {
+ let reader = new FileReader();
+ reader.onloadend = () => resolve(reader.result);
+ reader.readAsArrayBuffer(file);
+ });
+
+ return buffer;
+ }
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains one initialized origin directory, a script for origin
+ // initialization and the storage database:
+ // - storage/default/https+++foo.bar.mozilla-iot.org
+ // - create_db.js
+ // - storage.sqlite
+ // The file create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/localstorage/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Manually change the group in .metadata and .metadata-v2 from
+ // "bar.mozilla-iot.org" to "mozilla-iot.org".
+ // 2. Remove the folder "storage/temporary".
+ // 3. Remove the file "storage/ls-archive.sqlite".
+ installPackage("groupMismatch_profile");
+
+ info("Reading out contents of metadata file");
+
+ let metadataBuffer = await readMetadataFile();
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Reading out contents of metadata file");
+
+ let metadataBuffer2 = await readMetadataFile();
+
+ info("Verifying blobs differ");
+
+ ok(!compareBuffers(metadataBuffer, metadataBuffer2), "Metadata differ");
+}
diff --git a/dom/quota/test/xpcshell/test_initTemporaryStorage.js b/dom/quota/test/xpcshell/test_initTemporaryStorage.js
new file mode 100644
index 0000000000..617ff2d2c9
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_initTemporaryStorage.js
@@ -0,0 +1,49 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify initTemporaryStorage() does call
+ * QuotaManager::EnsureTemporaryStorageIsInitializedInternal() which does
+ * various things, for example, it restores the directory metadata if it's
+ * broken or missing.
+ */
+
+async function testSteps() {
+ const originDirPath = "storage/default/https+++foo.example.com";
+ const metadataFileName = ".metadata-v2";
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Verifying initialization status");
+
+ await verifyInitializationStatus(true, false);
+
+ info("Creating an empty directory");
+
+ let originDir = getRelativeFile(originDirPath);
+ originDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+
+ info("Initializing the temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info(
+ "Verifying directory metadata was restored after calling " +
+ "initTemporaryStorage()"
+ );
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ ok(metadataFile.exists(), "Directory metadata file does exist");
+
+ info("Verifying initialization status");
+
+ await verifyInitializationStatus(true, true);
+}
diff --git a/dom/quota/test/xpcshell/test_initializePersistentClient.js b/dom/quota/test/xpcshell/test_initializePersistentClient.js
new file mode 100644
index 0000000000..342fb80171
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_initializePersistentClient.js
@@ -0,0 +1,46 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that initializePersistentClient() does call
+ * QuotaManager::EnsurePersistentClientIsInitialized() which ensures client
+ * directory existence.
+ */
+
+async function testSteps() {
+ const clientMetadata = {
+ persistence: "default",
+ principal: getPrincipal("https://foo.example.com"),
+ client: "sdb",
+ file: getRelativeFile("storage/permanent/https+++foo.example.com/sdb"),
+ };
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing persistent origin");
+
+ request = initPersistentOrigin(clientMetadata.principal);
+ await requestFinished(request);
+
+ ok(!clientMetadata.file.exists(), "Client directory does not exist");
+
+ info("Initializing persistent client");
+
+ request = initPersistentClient(
+ clientMetadata.principal,
+ clientMetadata.client
+ );
+ await requestFinished(request);
+
+ ok(clientMetadata.file.exists(), "Client directory does exist");
+}
diff --git a/dom/quota/test/xpcshell/test_initializeTemporaryClient.js b/dom/quota/test/xpcshell/test_initializeTemporaryClient.js
new file mode 100644
index 0000000000..f721560bda
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_initializeTemporaryClient.js
@@ -0,0 +1,55 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that initializeTemporaryClient() does call
+ * QuotaManager::EnsureTemporaryClientIsInitialized() which ensures client
+ * directory existence.
+ */
+
+async function testSteps() {
+ const clientMetadata = {
+ persistence: "default",
+ principal: getPrincipal("https://foo.example.com"),
+ client: "sdb",
+ file: getRelativeFile("storage/default/https+++foo.example.com/sdb"),
+ };
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing temporary origin");
+
+ request = initTemporaryOrigin(
+ clientMetadata.persistence,
+ clientMetadata.principal
+ );
+ await requestFinished(request);
+
+ ok(!clientMetadata.file.exists(), "Client directory does not exist");
+
+ info("Initializing temporary client");
+
+ request = initTemporaryClient(
+ clientMetadata.persistence,
+ clientMetadata.principal,
+ clientMetadata.client
+ );
+ await requestFinished(request);
+
+ ok(clientMetadata.file.exists(), "Client directory does exist");
+}
diff --git a/dom/quota/test/xpcshell/test_listOrigins.js b/dom/quota/test/xpcshell/test_listOrigins.js
new file mode 100644
index 0000000000..fbd8554046
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_listOrigins.js
@@ -0,0 +1,88 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const origins = [
+ "https://example.com",
+ "https://localhost",
+ "https://www.mozilla.org",
+ ];
+
+ function verifyResult(result, expectedOrigins) {
+ ok(result instanceof Array, "Got an array object");
+ Assert.equal(
+ result.length,
+ expectedOrigins.length,
+ "Correct number of elements"
+ );
+
+ info("Sorting elements");
+
+ result.sort(function (a, b) {
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ return 0;
+ });
+
+ info("Verifying elements");
+
+ for (let i = 0; i < result.length; i++) {
+ Assert.equal(
+ result[i],
+ expectedOrigins[i],
+ "Result matches expected origin"
+ );
+ }
+ }
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Listing origins");
+
+ request = listOrigins();
+ await requestFinished(request);
+
+ info("Verifying result");
+
+ verifyResult(request.result, []);
+
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ request = initTemporaryOrigin("default", getPrincipal(origin));
+ await requestFinished(request);
+ }
+
+ info("Listing origins");
+
+ request = listOrigins();
+ await requestFinished(request);
+
+ info("Verifying result");
+
+ verifyResult(request.result, origins);
+}
diff --git a/dom/quota/test/xpcshell/test_originEndsWithDot.js b/dom/quota/test/xpcshell/test_originEndsWithDot.js
new file mode 100644
index 0000000000..6fd017a58b
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_originEndsWithDot.js
@@ -0,0 +1,70 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+async function testSteps() {
+ // First, ensure the origin can be initialized and used by a client that uses
+ // SQLite databases.
+
+ // Todo: consider using simpleDB once it supports using storage for SQLite.
+ info("Testing SQLite database with an origin that ends with a dot");
+
+ const principal = getPrincipal("https://example.com.");
+ let request = indexedDB.openForPrincipal(principal, "myIndexedDB");
+ await openDBRequestUpgradeNeeded(request);
+
+ info("Testing simple operations");
+
+ const database = request.result;
+
+ const objectStore = database.createObjectStore("Blobs", {});
+
+ objectStore.add(getNullBlob(200), 42);
+
+ await openDBRequestSucceeded(request);
+
+ database.close();
+
+ info("Reseting");
+
+ request = reset();
+ await requestFinished(request);
+
+ let idbDB = getRelativeFile(
+ "storage/default/https+++example.com./idb/2320029346mByDIdnedxe.sqlite"
+ );
+ ok(idbDB.exists(), "IDB database was created successfully");
+
+ // Second, ensure storage initialization works fine with the origin.
+
+ info("Testing storage initialization and temporary storage initialization");
+
+ request = init();
+ await requestFinished(request);
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ // Third, ensure QMS APIs that touch the client directory for the origin work
+ // fine.
+
+ info("Testing getUsageForPrincipal");
+
+ request = getOriginUsage(principal);
+ await requestFinished(request);
+
+ ok(
+ request.result instanceof Ci.nsIQuotaOriginUsageResult,
+ "The result is nsIQuotaOriginUsageResult instance"
+ );
+ Assert.greater(request.result.usage, 0, "Total usage is not empty");
+ Assert.greater(request.result.fileUsage, 0, "File usage is not empty");
+
+ info("Testing clearStoragesForPrincipal");
+
+ request = clearOrigin(principal, "default");
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_originMismatch.js b/dom/quota/test/xpcshell/test_originMismatch.js
new file mode 100644
index 0000000000..23186977b2
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_originMismatch.js
@@ -0,0 +1,75 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that temporary storage initialization should
+ * succeed while there is an origin directory that has an inconsistency between
+ * its directory name and the origin name in its directory metadata file.
+ */
+
+async function testSteps() {
+ const packages = ["originMismatch_profile", "defaultStorageDirectory_shared"];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing package");
+
+ // The profile contains:
+ // - storage.sqlite (v2_3)
+ // (A) Verify we are okay while the directory that we want to restore has
+ // already existed.
+ // - storage/default/http+++www.example.com/.metadata-v2
+ // (origin: http://www.example.com.)
+ // - storage/default/http+++www.example.com/cache/.padding
+ // - storage/default/http+++www.example.com./
+ // (B) Verify restoring origin directory succeed.
+ // - storage/default/http+++www.example.org/.metadata-v2
+ // (origin: http://www.example.org.)
+ // - storage/default/http+++www.example.org/cache/.padding
+ //
+ // ToDo: Test case like:
+ // - storage/default/http+++www.example.org(1)/.metadata-v2
+ // (origin: http://www.example.org)
+ // - storage/default/http+++www.example.org/
+ //
+ // - storage/default/http+++www.foo.com/.metadata-v2
+ // (origin: http://www.bar.com)
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Initializing storage");
+
+ request = init();
+ await requestFinished(request);
+
+ // ToDo: Remove this code once we support unknown directories in respository
+ // (bug 1594075).
+ let invalidDir = getRelativeFile("storage/default/invalid+++example.com");
+ invalidDir.remove(true);
+ invalidDir = getRelativeFile("storage/temporary/invalid+++example.com");
+ invalidDir.remove(true);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInitTemporaryStorage");
+
+ request = clear();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_originWithCaret.js b/dom/quota/test/xpcshell/test_originWithCaret.js
new file mode 100644
index 0000000000..7afce4f5cc
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_originWithCaret.js
@@ -0,0 +1,17 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+async function testSteps() {
+ Assert.throws(
+ () => {
+ const principal = getPrincipal("http://example.com^123");
+ getSimpleDatabase(principal);
+ },
+ /NS_ERROR_MALFORMED_URI/,
+ "^ is not allowed in the hostname"
+ );
+}
diff --git a/dom/quota/test/xpcshell/test_orpahnedQuotaObject.js b/dom/quota/test/xpcshell/test_orpahnedQuotaObject.js
new file mode 100644
index 0000000000..6cbca13d8c
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_orpahnedQuotaObject.js
@@ -0,0 +1,44 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const principal = getPrincipal("https://example.com");
+
+ info("Setting pref");
+
+ Services.prefs.setBoolPref("dom.storage.client_validation", false);
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Creating simpledb");
+
+ let database = getSimpleDatabase(principal);
+
+ request = database.open("data");
+ await requestFinished(request);
+
+ info("Creating localStorage");
+
+ let storage = Services.domStorageManager.createStorage(
+ null,
+ principal,
+ principal,
+ ""
+ );
+ storage.setItem("key", "value");
+
+ info("Clearing simpledb");
+
+ request = clearClient(principal, "default", "sdb");
+ await requestFinished(request);
+
+ info("Resetting localStorage");
+
+ request = resetClient(principal, "ls");
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_persist.js b/dom/quota/test/xpcshell/test_persist.js
new file mode 100644
index 0000000000..879343754c
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_persist.js
@@ -0,0 +1,133 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const origin = {
+ url: "http://default.test.persist",
+ path: "storage/default/http+++default.test.persist",
+ persistence: "default",
+ };
+
+ const metadataFileName = ".metadata-v2";
+
+ let principal = getPrincipal(origin.url);
+
+ info("Persisting an uninitialized origin");
+
+ // Origin directory doesn't exist yet, so only check the result for
+ // persisted().
+ let request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persisted() succeeded");
+ ok(!request.result, "The origin is not persisted");
+
+ info("Verifying persist() does update the metadata");
+
+ request = persist(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persist() succeeded");
+
+ let originDir = getRelativeFile(origin.path);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ let originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(originPersisted, "The origin is persisted");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persisted() succeeded");
+ Assert.strictEqual(
+ request.result,
+ originPersisted,
+ "Persisted() concurs with metadata"
+ );
+
+ info("Clearing the origin");
+
+ // Clear the origin since we'll test the same directory again under different
+ // circumstances.
+ clearOrigin(principal, origin.persistence, continueToNextStepSync);
+ yield undefined;
+
+ info("Persisting an already initialized origin");
+
+ initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ initTemporaryOrigin(origin.persistence, principal, continueToNextStepSync);
+ yield undefined;
+
+ info("Reading out contents of metadata file");
+
+ fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(!originPersisted, "The origin isn't persisted after clearing");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persisted() succeeded");
+ Assert.strictEqual(
+ request.result,
+ originPersisted,
+ "Persisted() concurs with metadata"
+ );
+
+ info("Verifying persist() does update the metadata");
+
+ request = persist(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persist() succeeded");
+
+ info("Reading out contents of metadata file");
+
+ fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(originPersisted, "The origin is persisted");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ Assert.strictEqual(request.resultCode, NS_OK, "Persisted() succeeded");
+ Assert.strictEqual(
+ request.result,
+ originPersisted,
+ "Persisted() concurs with metadata"
+ );
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_persist_eviction.js b/dom/quota/test/xpcshell/test_persist_eviction.js
new file mode 100644
index 0000000000..9a62f91b50
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_persist_eviction.js
@@ -0,0 +1,82 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that normally the oldest origin will be
+ * evicted if the global limit is reached, but if the oldest origin is
+ * persisted or is an extension origin, then it won't be evicted.
+ */
+
+loadScript("dom/quota/test/xpcshell/common/utils.js");
+
+async function testSteps() {
+ // The group limit is calculated as 20% of the global limit and the minimum
+ // value of the group limit is 10 MB.
+
+ const groupLimitKB = 10 * 1024;
+ const globalLimitKB = groupLimitKB * 5;
+
+ setGlobalLimit(globalLimitKB);
+
+ let request = clear();
+ await requestFinished(request);
+
+ for (let persistOldestOrigin of [false, true]) {
+ info(
+ "Testing " +
+ (persistOldestOrigin ? "with" : "without") +
+ " persisting the oldest origin"
+ );
+
+ info(
+ "Step 0: Filling a moz-extension origin as the oldest origin with non-persisted data"
+ );
+
+ // Just a fake moz-extension origin to mock an extension origin.
+ let extUUID = "20445ca5-75f9-420e-a1d4-9cccccb5e891";
+ let spec = `moz-extension://${extUUID}`;
+ await fillOrigin(getPrincipal(spec), groupLimitKB * 1024);
+
+ info(
+ "Step 1: Filling five separate web origins to reach the global limit " +
+ "and trigger eviction"
+ );
+
+ for (let index = 1; index <= 5; index++) {
+ let spec = "http://example" + index + ".com";
+ if (index == 1 && persistOldestOrigin) {
+ request = persist(getPrincipal(spec));
+ await requestFinished(request);
+ }
+ await fillOrigin(getPrincipal(spec), groupLimitKB * 1024);
+ }
+
+ info("Step 2: Verifying origin directories");
+
+ for (let index = 1; index <= 5; index++) {
+ let path = "storage/default/http+++example" + index + ".com";
+ let file = getRelativeFile(path);
+ if (index == (persistOldestOrigin ? 2 : 1)) {
+ ok(!file.exists(), "The origin directory " + path + " doesn't exist");
+ } else {
+ ok(file.exists(), "The origin directory " + path + " does exist");
+ }
+ }
+
+ // Verify that the extension storage data has not been evicted (even if it wasn't marked as
+ // persisted and it was the less recently used origin).
+ let path = `storage/default/moz-extension+++${extUUID}`;
+ let file = getRelativeFile(path);
+ ok(file.exists(), "The origin directory " + path + "does exist");
+
+ request = clear();
+ await requestFinished(request);
+ }
+
+ resetGlobalLimit();
+
+ request = reset();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_persist_globalLimit.js b/dom/quota/test/xpcshell/test_persist_globalLimit.js
new file mode 100644
index 0000000000..99ed0f43ff
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_persist_globalLimit.js
@@ -0,0 +1,83 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that persisted origins are always bounded by
+ * the global limit.
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+async function testSteps() {
+ const globalLimitKB = 1;
+
+ const principal = getPrincipal("https://persisted.example.com");
+
+ info("Setting limits");
+
+ setGlobalLimit(globalLimitKB);
+
+ let request = clear();
+ await requestFinished(request);
+
+ for (let initializeStorageBeforePersist of [false, true]) {
+ if (initializeStorageBeforePersist) {
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing the temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ }
+
+ info("Persisting an origin");
+
+ request = persist(principal);
+ await requestFinished(request);
+
+ info("Verifying the persisted origin is bounded by global limit");
+
+ let database = getSimpleDatabase(principal);
+
+ info("Opening a database for the persisted origin");
+
+ request = database.open("data");
+ await requestFinished(request);
+
+ try {
+ info("Writing over the limit shouldn't succeed");
+
+ request = database.write(getBuffer(globalLimitKB * 1024 + 1));
+ await requestFinished(request);
+
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(true, "Should have thrown");
+ Assert.strictEqual(
+ e.resultCode,
+ NS_ERROR_FILE_NO_DEVICE_SPACE,
+ "Threw right result code"
+ );
+ }
+
+ info("Closing the database and clearing");
+
+ request = database.close();
+ await requestFinished(request);
+
+ request = clear();
+ await requestFinished(request);
+ }
+
+ info("Resetting limits");
+
+ resetGlobalLimit();
+
+ request = reset();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_persist_groupLimit.js b/dom/quota/test/xpcshell/test_persist_groupLimit.js
new file mode 100644
index 0000000000..d2dc1a987d
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_persist_groupLimit.js
@@ -0,0 +1,105 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that persisted origins are not constrained by
+ * the group limit. It consits of these steps:
+ * - Set the limits as small as our limits allow. This does result in needing
+ * to perform 10 megs of writes which is a lot for a test but not horrible.
+ * - Create databases for 2 origins under the same group.
+ * - Have the foo2 origin use up the shared group quota.
+ * - Verify neither origin can write additional data (via a single byte write).
+ * - Do navigator.storage.persist() for that foo2 origin.
+ * - Verify that both origins can now write an additional byte. This
+ * demonstrates that:
+ * - foo2 no longer counts against the group limit at all since foo1 can
+ * write a byte.
+ * - foo2 is no longer constrained by the group limit itself.
+ */
+async function testSteps() {
+ // The group limit is calculated as 20% of the global limit and the minimum
+ // value of the group limit is 10 MB.
+
+ const groupLimitKB = 10 * 1024;
+ const globalLimitKB = groupLimitKB * 5;
+
+ const urls = ["http://foo1.example.com", "http://foo2.example.com"];
+
+ const foo2Index = 1;
+
+ let index;
+
+ info("Setting limits");
+
+ setGlobalLimit(globalLimitKB);
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Opening databases");
+
+ let databases = [];
+ for (index = 0; index < urls.length; index++) {
+ let database = getSimpleDatabase(getPrincipal(urls[index]));
+
+ request = database.open("data");
+ await requestFinished(request);
+
+ databases.push(database);
+ }
+
+ info("Filling up the whole group");
+
+ try {
+ request = databases[foo2Index].write(new ArrayBuffer(groupLimitKB * 1024));
+ await requestFinished(request);
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ info("Verifying no more data can be written");
+
+ for (index = 0; index < urls.length; index++) {
+ try {
+ request = databases[index].write(new ArrayBuffer(1));
+ await requestFinished(request);
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(true, "Should have thrown");
+ Assert.equal(
+ e.resultCode,
+ NS_ERROR_FILE_NO_DEVICE_SPACE,
+ "Threw right result code"
+ );
+ }
+ }
+
+ info("Persisting origin");
+
+ request = persist(getPrincipal(urls[foo2Index]));
+ await requestFinished(request);
+
+ info("Verifying more data data can be written");
+
+ for (index = 0; index < urls.length; index++) {
+ try {
+ request = databases[index].write(new ArrayBuffer(1));
+ await requestFinished(request);
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+ }
+
+ info("Closing databases");
+
+ for (index = 0; index < urls.length; index++) {
+ request = databases[index].close();
+ await requestFinished(request);
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_removeLocalStorage.js b/dom/quota/test/xpcshell/test_removeLocalStorage.js
new file mode 100644
index 0000000000..781c71a56f
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_removeLocalStorage.js
@@ -0,0 +1,89 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const lsArchiveFile = "storage/ls-archive.sqlite";
+ const lsArchiveTmpFile = "storage/ls-archive-tmp.sqlite";
+ const lsDir = "storage/default/http+++localhost/ls";
+
+ info("Setting pref");
+
+ SpecialPowers.setBoolPref(
+ "dom.storage.enable_unsupported_legacy_implementation",
+ true
+ );
+
+ // Profile 1
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Installing package");
+
+ installPackage("removeLocalStorage1_profile");
+
+ info("Checking ls archive tmp file");
+
+ let archiveTmpFile = getRelativeFile(lsArchiveTmpFile);
+
+ let exists = archiveTmpFile.exists();
+ ok(exists, "ls archive tmp file does exist");
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Checking ls archive file");
+
+ exists = archiveTmpFile.exists();
+ ok(!exists, "ls archive tmp file doesn't exist");
+
+ // Profile 2
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Installing package");
+
+ installPackage("removeLocalStorage2_profile");
+
+ info("Checking ls archive file");
+
+ let archiveFile = getRelativeFile(lsArchiveFile);
+
+ exists = archiveFile.exists();
+ ok(exists, "ls archive file does exist");
+
+ info("Checking ls dir");
+
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(exists, "ls directory does exist");
+
+ info("Initializing");
+
+ request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Checking ls archive file");
+
+ exists = archiveFile.exists();
+ ok(!exists, "ls archive file doesn't exist");
+
+ info("Checking ls dir");
+
+ exists = dir.exists();
+ ok(!exists, "ls directory doesn't exist");
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_simpledb.js b/dom/quota/test/xpcshell/test_simpledb.js
new file mode 100644
index 0000000000..b698d778e0
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_simpledb.js
@@ -0,0 +1,6 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+loadScript("dom/quota/test/common/test_simpledb.js");
diff --git a/dom/quota/test/xpcshell/test_specialOrigins.js b/dom/quota/test/xpcshell/test_specialOrigins.js
new file mode 100644
index 0000000000..d66700d359
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_specialOrigins.js
@@ -0,0 +1,55 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const origins = [
+ {
+ path: "storage/default/file+++UNIVERSAL_FILE_URI_ORIGIN",
+ url: "file:///Test/test.html",
+ persistence: "default",
+ },
+ ];
+
+ info("Setting pref");
+
+ SpecialPowers.setBoolPref("security.fileuri.strict_origin_policy", false);
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Creating origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.path);
+ originDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing origin directories");
+
+ for (let origin of origins) {
+ let result;
+
+ try {
+ request = initTemporaryOrigin(
+ origin.persistence,
+ getPrincipal(origin.url)
+ );
+ result = await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ ok(!result, "Origin directory wasn't created");
+ }
+}
diff --git a/dom/quota/test/xpcshell/test_storagePressure.js b/dom/quota/test/xpcshell/test_storagePressure.js
new file mode 100644
index 0000000000..0cf145ae1d
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_storagePressure.js
@@ -0,0 +1,136 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that the storage pressure event is fired when
+ * the eviction process is not able to free some space when a quota client
+ * attempts to write over the global limit or when the global limit is reduced
+ * below the global usage.
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+function awaitStoragePressure() {
+ let promise_resolve;
+
+ let promise = new Promise(function (resolve) {
+ promise_resolve = resolve;
+ });
+
+ function observer(subject, topic) {
+ ok(true, "Got the storage pressure event");
+
+ Services.obs.removeObserver(observer, topic);
+
+ let usage = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ promise_resolve(usage);
+ }
+
+ Services.obs.addObserver(observer, "QuotaManager::StoragePressure");
+
+ return promise;
+}
+
+async function testSteps() {
+ const globalLimitKB = 2;
+
+ const principal = getPrincipal("https://example.com");
+
+ info("Setting limits");
+
+ setGlobalLimit(globalLimitKB);
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Persisting and filling an origin");
+
+ // We need to persist the origin first to omit the group limit checks.
+ // Otherwise, we would have to fill five separate origins.
+ request = persist(principal);
+ await requestFinished(request);
+
+ let database = getSimpleDatabase(principal);
+
+ request = database.open("data");
+ await requestFinished(request);
+
+ try {
+ request = database.write(getBuffer(globalLimitKB * 1024));
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ info("Testing storage pressure by writing over the global limit");
+
+ info("Storing one more byte to get the storage pressure event while writing");
+
+ let promiseStoragePressure = awaitStoragePressure();
+
+ try {
+ request = database.write(getBuffer(1));
+ await requestFinished(request);
+
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(true, "Should have thrown");
+ Assert.strictEqual(
+ e.resultCode,
+ NS_ERROR_FILE_NO_DEVICE_SPACE,
+ "Threw right result code"
+ );
+ }
+
+ info("Checking the storage pressure event");
+
+ let usage = await promiseStoragePressure;
+ Assert.equal(usage, globalLimitKB * 1024, "Got correct usage");
+
+ info("Testing storage pressure by reducing the global limit");
+
+ info(
+ "Reducing the global limit to get the storage pressuse event while the" +
+ " temporary storage is being initialized"
+ );
+
+ setGlobalLimit(globalLimitKB - 1);
+
+ request = reset();
+ await requestFinished(request);
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ promiseStoragePressure = awaitStoragePressure();
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Checking the storage pressure event");
+
+ usage = await promiseStoragePressure;
+ Assert.equal(usage, globalLimitKB * 1024, "Got correct usage");
+
+ info("Resetting limits");
+
+ resetGlobalLimit();
+
+ request = reset();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_tempMetadataCleanup.js b/dom/quota/test/xpcshell/test_tempMetadataCleanup.js
new file mode 100644
index 0000000000..3110c0b2a5
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_tempMetadataCleanup.js
@@ -0,0 +1,45 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const tempMetadataFiles = [
+ "storage/permanent/chrome/.metadata-tmp",
+ "storage/permanent/chrome/.metadata-v2-tmp",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Installing package");
+
+ installPackage("tempMetadataCleanup_profile");
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ info("Initializing origin");
+
+ request = initPersistentOrigin(getCurrentPrincipal(), continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+
+ for (let tempMetadataFile of tempMetadataFiles) {
+ info("Checking temp metadata file");
+
+ let file = getRelativeFile(tempMetadataFile);
+
+ let exists = file.exists();
+ ok(!exists, "Temp metadata file doesn't exist");
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/test_unaccessedOrigins.js b/dom/quota/test/xpcshell/test_unaccessedOrigins.js
new file mode 100644
index 0000000000..93b38501f4
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_unaccessedOrigins.js
@@ -0,0 +1,168 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const SEC_PER_MONTH = 60 * 60 * 24 * 30;
+
+async function testSteps() {
+ function getHostname(index) {
+ return "www.example" + index + ".com";
+ }
+
+ function getOrigin(index) {
+ return "https://" + getHostname(index);
+ }
+
+ function getOriginDir(index) {
+ return getRelativeFile("storage/default/https+++" + getHostname(index));
+ }
+
+ function updateOriginLastAccessTime(index, deltaSec) {
+ let originDir = getOriginDir(index);
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(".metadata-v2");
+
+ let fileRandomAccessStream = Cc[
+ "@mozilla.org/network/file-random-access-stream;1"
+ ].createInstance(Ci.nsIFileRandomAccessStream);
+ fileRandomAccessStream.init(metadataFile, -1, -1, 0);
+
+ let binaryInputStream = Cc[
+ "@mozilla.org/binaryinputstream;1"
+ ].createInstance(Ci.nsIBinaryInputStream);
+ binaryInputStream.setInputStream(fileRandomAccessStream);
+
+ let lastAccessTime = binaryInputStream.read64();
+
+ let seekableStream = fileRandomAccessStream.QueryInterface(
+ Ci.nsISeekableStream
+ );
+ seekableStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+
+ binaryOutputStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
+ Ci.nsIBinaryOutputStream
+ );
+ binaryOutputStream.setOutputStream(fileRandomAccessStream);
+
+ binaryOutputStream.write64(lastAccessTime + deltaSec * PR_USEC_PER_SEC);
+
+ binaryOutputStream.close();
+
+ binaryInputStream.close();
+ }
+
+ function verifyOriginDir(index, shouldExist) {
+ let originDir = getOriginDir(index);
+ let exists = originDir.exists();
+ if (shouldExist) {
+ ok(exists, "Origin directory does exist");
+ } else {
+ ok(!exists, "Origin directory doesn't exist");
+ }
+ }
+
+ info("Setting prefs");
+
+ Services.prefs.setBoolPref("dom.quotaManager.loadQuotaFromCache", false);
+ Services.prefs.setBoolPref("dom.quotaManager.checkQuotaInfoLoadTime", true);
+ Services.prefs.setIntPref(
+ "dom.quotaManager.longQuotaInfoLoadTimeThresholdMs",
+ 0
+ );
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing origins");
+
+ for (let index = 0; index < 30; index++) {
+ request = initTemporaryOrigin("default", getPrincipal(getOrigin(index)));
+ await requestFinished(request);
+ }
+
+ info("Updating last access time of selected origins");
+
+ for (let index = 0; index < 10; index++) {
+ updateOriginLastAccessTime(index, -14 * SEC_PER_MONTH);
+ }
+
+ for (let index = 10; index < 20; index++) {
+ updateOriginLastAccessTime(index, -7 * SEC_PER_MONTH);
+ }
+
+ info("Resetting");
+
+ request = reset();
+ await requestFinished(request);
+
+ info("Setting pref");
+
+ Services.prefs.setIntPref(
+ "dom.quotaManager.unaccessedForLongTimeThresholdSec",
+ 13 * SEC_PER_MONTH
+ );
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Verifying origin directories");
+
+ for (let index = 0; index < 10; index++) {
+ verifyOriginDir(index, false);
+ }
+ for (let index = 10; index < 30; index++) {
+ verifyOriginDir(index, true);
+ }
+
+ info("Resetting");
+
+ request = reset();
+ await requestFinished(request);
+
+ info("Setting pref");
+
+ Services.prefs.setIntPref(
+ "dom.quotaManager.unaccessedForLongTimeThresholdSec",
+ 6 * SEC_PER_MONTH
+ );
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Verifying origin directories");
+
+ for (let index = 0; index < 20; index++) {
+ verifyOriginDir(index, false);
+ }
+ for (let index = 20; index < 30; index++) {
+ verifyOriginDir(index, true);
+ }
+
+ info("Resetting");
+
+ request = reset();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/test_unknownFiles.js b/dom/quota/test/xpcshell/test_unknownFiles.js
new file mode 100644
index 0000000000..57918eb0ad
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_unknownFiles.js
@@ -0,0 +1,106 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that init, initTemporaryStorage,
+ * getUsageForPrincipal and clearStoragesForPrincipal are able to ignore
+ * unknown files and directories in the storage/default directory and its
+ * subdirectories.
+ */
+async function testSteps() {
+ const principal = getPrincipal("http://example.com");
+
+ async function testFunctionality(testFunction) {
+ const modes = [
+ {
+ initializedStorage: false,
+ initializedTemporaryStorage: false,
+ },
+ {
+ initializedStorage: true,
+ initializedTemporaryStorage: false,
+ },
+ {
+ initializedStorage: true,
+ initializedTemporaryStorage: true,
+ },
+ ];
+
+ for (const mode of modes) {
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains unknown files and unknown directories placed
+ // across the repositories, origin directories and client directories.
+ // The file make_unknownFiles.js was run locally, specifically it was
+ // temporarily enabled in xpcshell.ini and then executed:
+ // mach test --interactive dom/quota/test/xpcshell/make_unknownFiles.js
+ installPackage("unknownFiles_profile");
+
+ if (mode.initializedStorage) {
+ info("Initializing storage");
+
+ request = init();
+ await requestFinished(request);
+ }
+
+ if (mode.initializedTemporaryStorage) {
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ }
+
+ info("Verifying initialization status");
+
+ await verifyInitializationStatus(
+ mode.initializedStorage,
+ mode.initializedTemporaryStorage
+ );
+
+ await testFunction(
+ mode.initializedStorage,
+ mode.initializedTemporaryStorage
+ );
+
+ info("Clearing");
+
+ request = clear();
+ await requestFinished(request);
+ }
+ }
+
+ // init and initTemporaryStorage functionality is tested in the
+ // testFunctionality function as part of the multi mode testing
+
+ info("Testing getUsageForPrincipal functionality");
+
+ await testFunctionality(async function () {
+ info("Getting origin usage");
+
+ request = getOriginUsage(principal);
+ await requestFinished(request);
+
+ ok(
+ request.result instanceof Ci.nsIQuotaOriginUsageResult,
+ "The result is nsIQuotaOriginUsageResult instance"
+ );
+ is(request.result.usage, 115025, "Correct total usage");
+ is(request.result.fileUsage, 200, "Correct file usage");
+ });
+
+ info("Testing clearStoragesForPrincipal functionality");
+
+ await testFunctionality(async function () {
+ info("Clearing origin");
+
+ request = clearOrigin(principal, "default");
+ await requestFinished(request);
+ });
+}
diff --git a/dom/quota/test/xpcshell/test_unsetLastAccessTime.js b/dom/quota/test/xpcshell/test_unsetLastAccessTime.js
new file mode 100644
index 0000000000..cff7a5e2f0
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_unsetLastAccessTime.js
@@ -0,0 +1,68 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+async function testSteps() {
+ const metadataFile = getRelativeFile(
+ "storage/default/https+++foo.example.com/.metadata-v2"
+ );
+
+ function getLastAccessTime() {
+ let fileInputStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+
+ fileInputStream.init(metadataFile, -1, -1, 0);
+
+ let binaryInputStream = Cc[
+ "@mozilla.org/binaryinputstream;1"
+ ].createInstance(Ci.nsIBinaryInputStream);
+
+ binaryInputStream.setInputStream(fileInputStream);
+
+ let lastAccessTime = BigInt.asIntN(64, BigInt(binaryInputStream.read64()));
+
+ binaryInputStream.close();
+
+ return lastAccessTime;
+ }
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains one initialized origin directory and the storage
+ // database:
+ // - storage/default/https+++foo.example.com
+ // - storage.sqlite
+ // The file make_unsetLastAccessTime.js was run locally, specifically it was
+ // temporarily enabled in xpcshell.ini and then executed:
+ // mach test --interactive dom/quota/test/xpcshell/make_unsetLastAccessTime.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ // 2. Remove the file "storage/ls-archive.sqlite".
+ installPackage("unsetLastAccessTime_profile");
+
+ info("Verifying last access time");
+
+ Assert.equal(getLastAccessTime(), INT64_MIN, "Correct last access time");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Verifying last access time");
+
+ Assert.notEqual(getLastAccessTime(), INT64_MIN, "Correct last access time");
+}
diff --git a/dom/quota/test/xpcshell/test_validOrigins.js b/dom/quota/test/xpcshell/test_validOrigins.js
new file mode 100644
index 0000000000..74db0ea531
--- /dev/null
+++ b/dom/quota/test/xpcshell/test_validOrigins.js
@@ -0,0 +1,100 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Use initOrigin to test the operation of the origin parser on a list of URLs
+// we should support. If the origin doesn't parse, then initOrigin will throw an
+// exception (and potentially MOZ_ASSERT under debug builds). Handling of
+// obsolete or invalid origins is handled in other test files.
+async function testSteps() {
+ const basePath = "storage/default/";
+ const longExampleOriginSubstring = "a".repeat(
+ 255 - "https://example..com".length
+ );
+ const origins = [
+ // General
+ {
+ dirName: "https+++example.com",
+ url: "https://example.com",
+ },
+ {
+ dirName: "https+++smaug----.github.io",
+ url: "https://smaug----.github.io/",
+ },
+ // About
+ {
+ dirName: "about+home",
+ url: "about:home",
+ },
+ {
+ dirName: "about+reader",
+ url: "about:reader",
+ },
+ // IPv6
+ {
+ dirName: "https+++[++]",
+ url: "https://[::]",
+ },
+ {
+ dirName: "https+++[ffff+ffff+ffff+ffff+ffff+ffff+ffff+ffff]",
+ url: "https://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]",
+ },
+ {
+ dirName: "http+++[2010+836b+4179++836b+4179]",
+ url: "http://[2010:836B:4179::836B:4179]:80",
+ },
+ {
+ dirName: "https+++[++ffff+8190+3426]",
+ url: "https://[::FFFF:129.144.52.38]",
+ },
+ // MAX_PATH on Windows (260); storage/default/https+++example.{a....a}.com
+ // should have already exceeded the MAX_PATH limitation on Windows.
+ // There is a limitation (255) for each component on Windows so that we can
+ // only let the component be 255 chars and expect the wwhole path to be
+ // greater then 260.
+ {
+ dirName: `https+++example.${longExampleOriginSubstring}.com`,
+ url: `https://example.${longExampleOriginSubstring}.com`,
+ },
+ // EndingWithPeriod
+ {
+ dirName: "https+++example.com.",
+ url: "https://example.com.",
+ },
+ ];
+
+ info("Initializing");
+
+ let request = init();
+ await requestFinished(request);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ for (let origin of origins) {
+ info(`Testing ${origin.url}`);
+
+ try {
+ request = initTemporaryOrigin("default", getPrincipal(origin.url));
+ await requestFinished(request);
+
+ ok(true, "Should not have thrown");
+ } catch (ex) {
+ ok(false, "Should not have thrown");
+ }
+
+ let dir = getRelativeFile(basePath + origin.dirName);
+ ok(dir.exists(), "Origin was created");
+ Assert.strictEqual(
+ origin.dirName,
+ dir.leafName,
+ `Origin ${origin.dirName} was created expectedly`
+ );
+ }
+
+ request = clear();
+ await requestFinished(request);
+}
diff --git a/dom/quota/test/xpcshell/unknownFiles_profile.zip b/dom/quota/test/xpcshell/unknownFiles_profile.zip
new file mode 100644
index 0000000000..75bfa09003
--- /dev/null
+++ b/dom/quota/test/xpcshell/unknownFiles_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/unsetLastAccessTime_profile.zip b/dom/quota/test/xpcshell/unsetLastAccessTime_profile.zip
new file mode 100644
index 0000000000..2b14ca7276
--- /dev/null
+++ b/dom/quota/test/xpcshell/unsetLastAccessTime_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.json b/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.json
new file mode 100644
index 0000000000..f04f79a6d7
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.json
@@ -0,0 +1,64 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "https+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ {
+ "name": "sdb",
+ "dir": true,
+ "entries": [{ "name": "data.sdb", "dir": false }]
+ },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "https+++www.mozilla.org^userContextId=1",
+ "dir": true,
+ "entries": [
+ {
+ "name": "sdb",
+ "dir": true,
+ "entries": [{ "name": "data.sdb", "dir": false }]
+ },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.zip b/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.zip
new file mode 100644
index 0000000000..c09b503c18
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/cacheVersion1_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/head.js b/dom/quota/test/xpcshell/upgrades/head.js
new file mode 100644
index 0000000000..5c36d82ca6
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/head.js
@@ -0,0 +1,14 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// The path to the top level directory.
+const depth = "../../../../../";
+
+loadScript("dom/quota/test/xpcshell/common/head.js");
+
+function loadScript(path) {
+ let uri = Services.io.newFileURI(do_get_file(depth + path));
+ Services.scriptloader.loadSubScript(uri.spec);
+}
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.json b/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.json
new file mode 100644
index 0000000000..db66d824eb
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.json
@@ -0,0 +1,63 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "indexedDB",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ },
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.zip b/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.zip
new file mode 100644
index 0000000000..63936ecf9a
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBAndPersistentStorageDirectory_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.json b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.json
new file mode 100644
index 0000000000..7916c25b73
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.json
@@ -0,0 +1,55 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "indexedDB",
+ "dir": true,
+ "entries": [
+ { "name": "1007+f+app+++system.gaiamobile.org", "dir": true },
+ { "name": "http+++www.mozilla.org", "dir": true },
+ { "name": "1007+t+https+++developer.cdn.mozilla.net", "dir": true }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.zip b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.zip
new file mode 100644
index 0000000000..a0a56a77df
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_flatOriginDirectories_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.json b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.json
new file mode 100644
index 0000000000..715c954915
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.json
@@ -0,0 +1,65 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "indexedDB",
+ "dir": true,
+ "entries": [
+ {
+ "name": "1007+f+app+++system.gaiamobile.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.zip b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.zip
new file mode 100644
index 0000000000..589e65ec82
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/indexedDBDirectory_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/localStorageArchive1upgrade_profile.zip b/dom/quota/test/xpcshell/upgrades/localStorageArchive1upgrade_profile.zip
new file mode 100644
index 0000000000..a17b90dedf
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/localStorageArchive1upgrade_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/localStorageArchive4upgrade_profile.zip b/dom/quota/test/xpcshell/upgrades/localStorageArchive4upgrade_profile.zip
new file mode 100644
index 0000000000..cf5b29adae
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/localStorageArchive4upgrade_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/localStorageArchiveDowngrade_profile.zip b/dom/quota/test/xpcshell/upgrades/localStorageArchiveDowngrade_profile.zip
new file mode 100644
index 0000000000..2f11abe858
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/localStorageArchiveDowngrade_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.json b/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.json
new file mode 100644
index 0000000000..a25f257573
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.json
@@ -0,0 +1,63 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ },
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.zip b/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.zip
new file mode 100644
index 0000000000..9ddd9af6a9
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentAndDefaultStorageDirectory_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.json b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.json
new file mode 100644
index 0000000000..c80a3cc283
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.json
@@ -0,0 +1,64 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ { "name": "1007+f+app+++system.gaiamobile.org", "dir": true },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true
+ },
+ { "name": "http+++www.mozilla.org", "dir": true }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.zip b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.zip
new file mode 100644
index 0000000000..436ebf9070
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_flatOriginDirectories_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.json b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.json
new file mode 100644
index 0000000000..98d23af161
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.json
@@ -0,0 +1,92 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "1007+f+app+++system.gaiamobile.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false }
+ ]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false }
+ ]
+ },
+ { "name": "http+++www.mozilla.org+8080", "dir": true }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org+8080",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.zip b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.zip
new file mode 100644
index 0000000000..0c5200adf4
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_originDirectories_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.json b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.json
new file mode 100644
index 0000000000..f5d6a4a749
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.json
@@ -0,0 +1,382 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "persistent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "1007+f+app+++system.gaiamobile.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++Users+joe+c+++index.html",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "moz-safe-about+home",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "https+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++Users+joe+index.html",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++localhost",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++c++",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++c++Users+joe+index.html",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++c++Users+joe+",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++127.0.0.1",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++++index.html",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++www.mozilla.org+8080",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "moz-safe-about+++home",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "resource+++fx-share-addon-at-mozilla-dot-org-fx-share-addon-data",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "indexeddb+++fx-devtools",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "https+++www.mozilla.org+8080",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "file++++Users+joe+",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "1007+f+app+++system.gaiamobile.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++localhost",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++localhost+82",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ { "name": "http+++localhost+81", "dir": true }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "file++++Users+joe+c+++index.html",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "https+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++Users+joe+index.html",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++localhost",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++c++",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++c++Users+joe+index.html",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++c++Users+joe+",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++127.0.0.1",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++++index.html",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org+8080",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "https+++www.mozilla.org+8080",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "file++++Users+joe+",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++localhost",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++localhost+82",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++localhost+81",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-safe-about+home",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "resource+++fx-share-addon-at-mozilla-dot-org-fx-share-addon-data",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "indexeddb+++fx-devtools",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.zip b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.zip
new file mode 100644
index 0000000000..ab8c045be9
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/persistentStorageDirectory_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/test_localStorageArchive1upgrade.js b/dom/quota/test/xpcshell/upgrades/test_localStorageArchive1upgrade.js
new file mode 100644
index 0000000000..f697d9167e
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_localStorageArchive1upgrade.js
@@ -0,0 +1,65 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that local storage directories are removed
+ * during local storage archive upgrade from version 0 to version 1.
+ * See bug 1546305.
+ */
+
+async function testSteps() {
+ const lsDirs = [
+ "storage/default/http+++example.com/ls",
+ "storage/default/http+++localhost/ls",
+ "storage/default/http+++www.mozilla.org/ls",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains three initialized origin directories with local
+ // storage data, local storage archive, a script for origin initialization,
+ // the storage database and the web apps store database:
+ // - storage/default/https+++example.com
+ // - storage/default/https+++localhost
+ // - storage/default/https+++www.mozilla.org
+ // - storage/ls-archive.sqlite
+ // - create_db.js
+ // - storage.sqlite
+ // - webappsstore.sqlite
+ // The file create_db.js in the package was run locally (with a build that
+ // doesn't support local storage archive upgrades), specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/localstorage/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ installPackage("localStorageArchive1upgrade_profile");
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(exists, "ls directory does exist");
+ }
+
+ request = init();
+ request = await requestFinished(request);
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(!exists, "ls directory doesn't exist");
+ }
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_localStorageArchive4upgrade.js b/dom/quota/test/xpcshell/upgrades/test_localStorageArchive4upgrade.js
new file mode 100644
index 0000000000..0b0fcfccd6
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_localStorageArchive4upgrade.js
@@ -0,0 +1,107 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that local storage directories are removed
+ * during local storage archive upgrade from version 3 to version 4.
+ * See bug 1549654.
+ */
+
+async function testSteps() {
+ const lsDirs = [
+ "storage/default/http+++localhost/ls",
+ "storage/default/http+++www.mozilla.org/ls",
+ "storage/default/http+++example.com/ls",
+ ];
+
+ const principalInfos = [
+ "http://localhost",
+ "http://www.mozilla.org",
+ "http://example.com",
+ ];
+
+ const data = [
+ { key: "foo0", value: "bar" },
+ { key: "foo1", value: "A" },
+ { key: "foo2", value: "A".repeat(100) },
+ ];
+
+ function getLocalStorage(principal) {
+ return Services.domStorageManager.createStorage(
+ null,
+ principal,
+ principal,
+ ""
+ );
+ }
+
+ info("Setting pref");
+
+ // xpcshell globals don't have associated clients in the Clients API sense, so
+ // we need to disable client validation so that this xpcshell test is allowed
+ // to use LocalStorage.
+ Services.prefs.setBoolPref("dom.storage.client_validation", false);
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains three initialized origin directories with local
+ // storage data, local storage archive, a script for origin initialization,
+ // the storage database and the web apps store database:
+ // - storage/default/https+++example.com
+ // - storage/default/https+++localhost
+ // - storage/default/https+++www.mozilla.org
+ // - storage/ls-archive.sqlite
+ // - create_db.js
+ // - storage.sqlite
+ // - webappsstore.sqlite
+ // The file create_db.js in the package was run locally (with a build with
+ // local storage archive version 3), specifically it was temporarily added to
+ // xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/localstorage/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ installPackage("localStorageArchive4upgrade_profile");
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(exists, "ls directory does exist");
+ }
+
+ request = init();
+ request = await requestFinished(request);
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(!exists, "ls directory doesn't exist");
+ }
+
+ info("Getting storages");
+
+ let storages = [];
+ for (let i = 0; i < principalInfos.length; i++) {
+ let storage = getLocalStorage(getPrincipal(principalInfos[i]));
+ storages.push(storage);
+ }
+
+ info("Verifying data");
+
+ for (let i = 0; i < storages.length; i++) {
+ is(storages[i].getItem(data[i].key), data[i].value, "Correct value");
+ }
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_localStorageArchiveDowngrade.js b/dom/quota/test/xpcshell/upgrades/test_localStorageArchiveDowngrade.js
new file mode 100644
index 0000000000..8ca46f01d5
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_localStorageArchiveDowngrade.js
@@ -0,0 +1,66 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that local storage directories are removed
+ * during local storage archive downgrade from any future version to current
+ * version. See bug 1546305.
+ */
+
+async function testSteps() {
+ const lsDirs = [
+ "storage/default/http+++example.com/ls",
+ "storage/default/http+++localhost/ls",
+ "storage/default/http+++www.mozilla.org/ls",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Installing package");
+
+ // The profile contains three initialized origin directories with local
+ // storage data, local storage archive, a script for origin initialization,
+ // the storage database and the web apps store database:
+ // - storage/default/https+++example.com
+ // - storage/default/https+++localhost
+ // - storage/default/https+++www.mozilla.org
+ // - storage/ls-archive.sqlite
+ // - create_db.js
+ // - storage.sqlite
+ // - webappsstore.sqlite
+ // The file create_db.js in the package was run locally (with a build that
+ // supports local storage archive upgrades and local storage archive version
+ // set to max integer), specifically it was temporarily added to xpcshell.ini
+ // and then executed:
+ // mach xpcshell-test --interactive dom/localstorage/test/xpcshell/create_db.js
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ installPackage("localStorageArchiveDowngrade_profile");
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(exists, "ls directory does exist");
+ }
+
+ request = init();
+ request = await requestFinished(request);
+
+ info("Checking ls dirs");
+
+ for (let lsDir of lsDirs) {
+ let dir = getRelativeFile(lsDir);
+
+ exists = dir.exists();
+ ok(!exists, "ls directory doesn't exist");
+ }
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeCacheFrom1.js b/dom/quota/test/xpcshell/upgrades/test_upgradeCacheFrom1.js
new file mode 100644
index 0000000000..e9424e20c6
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeCacheFrom1.js
@@ -0,0 +1,79 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify UpgradeCacheFrom1To2 method.
+ */
+
+async function testSteps() {
+ const packages = [
+ // Storage used prior FF 88 (cache version 1).
+ // The profile contains one initialized origin directory with simple
+ // database data, a script for origin initialization and the storage
+ // database:
+ // - storage/default/https+++www.mozilla.org^userContextId=1
+ // - create_db.js
+ // - storage.sqlite
+ // The file create_db.js in the package was run locally, specifically it was
+ // temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test dom/quota/test/xpcshell/upgrades/create_db.js
+ // --interactive
+ // Note: to make it become the profile in the test, additional manual steps
+ // are needed.
+ // 1. Remove the folder "storage/temporary".
+ // 2. Remove the file "storage/ls-archive.sqlite".
+ "cacheVersion1_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+ const principal = getPrincipal("https://www.mozilla.org", {
+ userContextId: 1,
+ });
+ const originUsage = 100;
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing package");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ await requestFinished(request);
+
+ info("Getting origin usage");
+
+ request = getOriginUsage(principal, /* fromMemory */ true);
+ await requestFinished(request);
+
+ info("Verifying origin usage");
+
+ is(request.result.usage, originUsage, "Correct origin usage");
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js
new file mode 100644
index 0000000000..66396fef6c
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js
@@ -0,0 +1,187 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// A flat origin directory is an origin directory with no sub directories for
+// quota clients. The upgrade was initially done lazily and an empty .metadata
+// file was used to indicate a successful upgrade.
+
+function* testSteps() {
+ const setups = [
+ {
+ packages: [
+ // Storage used prior FF 22 (indexedDB/ directory with flat origin
+ // directories).
+ // FF 26 renamed indexedDB/ to storage/persistent and the lazy upgrade
+ // of flat origin directories remained. There's a test for that below.
+ "indexedDBDirectory_flatOriginDirectories_profile",
+ "../indexedDBDirectory_shared",
+ ],
+ origins: [
+ {
+ oldPath: "indexedDB/1007+f+app+++system.gaiamobile.org",
+ },
+
+ {
+ oldPath: "indexedDB/1007+t+https+++developer.cdn.mozilla.net",
+ },
+
+ {
+ oldPath: "indexedDB/http+++www.mozilla.org",
+ newPath: "storage/default/http+++www.mozilla.org",
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ },
+ ],
+ },
+
+ {
+ packages: [
+ // Storage used by FF 26-35 (storage/persistent/ directory with not yet
+ // upgraded flat origin directories).
+ // FF 36 renamed storage/persistent/ to storage/default/ and all not
+ // yet upgraded flat origin directories were upgraded. There's a
+ // separate test for that:
+ // test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.
+ "persistentStorageDirectory_flatOriginDirectories_profile",
+ "../persistentStorageDirectory_shared",
+ ],
+ origins: [
+ {
+ oldPath: "storage/persistent/1007+f+app+++system.gaiamobile.org",
+ },
+
+ {
+ oldPath:
+ "storage/persistent/1007+t+https+++developer.cdn.mozilla.net",
+ },
+
+ {
+ oldPath: "storage/persistent/http+++www.mozilla.org",
+ newPath: "storage/default/http+++www.mozilla.org",
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ },
+ ],
+ },
+ ];
+
+ const metadataFileName = ".metadata";
+
+ for (const setup of setups) {
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(setup.packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(setup.packages);
+
+ info("Verifying storage");
+
+ verifyStorage(setup.packages, "afterInstall");
+
+ info("Checking origin directories");
+
+ for (const origin of setup.origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ let idbDir = originDir.clone();
+ idbDir.append("idb");
+
+ exists = idbDir.exists();
+ ok(!exists, "idb directory doesn't exist");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ ok(!exists, "Metadata file doesn't exist");
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(setup.packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ try {
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+ } catch (ex) {}
+
+ info("Checking origin directories");
+
+ for (const origin of setup.origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ let idbDir = originDir.clone();
+ idbDir.append("idb");
+
+ exists = idbDir.exists();
+ ok(exists, "idb directory does exist");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ ok(exists, "Metadata file does exist");
+ }
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of setup.origins) {
+ if (origin.newPath) {
+ info("Initializing origin");
+
+ let principal = getPrincipal(origin.url);
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+ }
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js
new file mode 100644
index 0000000000..fb6ad9c5a7
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js
@@ -0,0 +1,121 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify
+ * MaybeUpgradeFromIndexedDBDirectoryToPersistentStorageDirectory method.
+ */
+
+function* testSteps() {
+ const origins = [
+ {
+ oldPath: "indexedDB/http+++www.mozilla.org",
+ newPath: "storage/default/http+++www.mozilla.org",
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "indexedDB/1007+f+app+++system.gaiamobile.org",
+ },
+
+ {
+ oldPath: "indexedDB/1007+t+https+++developer.cdn.mozilla.net",
+ },
+ ];
+
+ const packages = [
+ // Storage used prior FF 26 (indexedDB/ directory).
+ "indexedDBDirectory_profile",
+ "../indexedDBDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing package");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+ }
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ if (origin.newPath) {
+ info("Initializing origin");
+
+ let principal = getPrincipal(origin.url);
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory_removeOldDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory_removeOldDirectory.js
new file mode 100644
index 0000000000..a7beced885
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory_removeOldDirectory.js
@@ -0,0 +1,86 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that the old directory is removed in
+ * MaybeUpgradeFromIndexedDBDirectoryToPersistentStorageDirectory method.
+ */
+
+async function testSteps() {
+ const url = "http://www.mozilla.org";
+ const persistence = "default";
+
+ const packages = [
+ // Storage used by FF 26-35 (storage/persistent/ directory and re-created
+ // indexedDB directory by an older FF).
+ "indexedDBAndPersistentStorageDirectory_profile",
+ "../persistentStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking directories");
+
+ let indexedDBDir = getRelativeFile("indexedDB");
+ let exists = indexedDBDir.exists();
+ ok(exists, "IndexedDB directory does exist");
+
+ let persistentStorageDir = getRelativeFile("storage/persistent");
+ exists = persistentStorageDir.exists();
+ ok(exists, "Persistent storage directory does exist");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking directories");
+
+ indexedDBDir = getRelativeFile("indexedDB");
+ exists = indexedDBDir.exists();
+ ok(!exists, "IndexedDB directory doesn't exist");
+
+ // FF 36 renamed storage/persistent/ to storage/default/ so it can't exist
+ // either.
+ persistentStorageDir = getRelativeFile("storage/persistent");
+ exists = persistentStorageDir.exists();
+ ok(!exists, "Persistent storage directory doesn't exist");
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing origin");
+
+ request = initTemporaryOrigin(persistence, getPrincipal(url));
+ await requestFinished(request);
+
+ ok(!request.result, "Origin directory wasn't created");
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js
new file mode 100644
index 0000000000..20c0152921
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js
@@ -0,0 +1,378 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify
+ * MaybeUpgradeFromPersistentStorageDirectoryToDefaultStorageDirectory method.
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+function* testSteps() {
+ const origins = [
+ {
+ oldPath: "storage/persistent/1007+f+app+++system.gaiamobile.org",
+ },
+
+ {
+ oldPath: "storage/persistent/1007+t+https+++developer.cdn.mozilla.net",
+ },
+
+ {
+ oldPath: "storage/persistent/chrome",
+ newPath: "storage/permanent/chrome",
+ chrome: true,
+ persistence: "persistent",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++",
+ newPath: "storage/default/file++++",
+ url: "file:///",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++++index.html",
+ newPath: "storage/default/file++++++index.html",
+ url: "file:///+/index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++++index.html",
+ newPath: "storage/default/file++++++index.html",
+ url: "file://///index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++Users+joe+",
+ newPath: "storage/default/file++++Users+joe+",
+ url: "file:///Users/joe/",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++Users+joe+c+++index.html",
+ newPath: "storage/default/file++++Users+joe+c+++index.html",
+ url: "file:///Users/joe/c++/index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++Users+joe+c+++index.html",
+ newPath: "storage/default/file++++Users+joe+c+++index.html",
+ url: "file:///Users/joe/c///index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++Users+joe+index.html",
+ newPath: "storage/default/file++++Users+joe+index.html",
+ url: "file:///Users/joe/index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++c++",
+ newPath: "storage/default/file++++c++",
+ url: "file:///c:/",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++c++Users+joe+",
+ newPath: "storage/default/file++++c++Users+joe+",
+ url: "file:///c:/Users/joe/",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/file++++c++Users+joe+index.html",
+ newPath: "storage/default/file++++c++Users+joe+index.html",
+ url: "file:///c:/Users/joe/index.html",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/http+++127.0.0.1",
+ newPath: "storage/default/http+++127.0.0.1",
+ url: "http://127.0.0.1",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/http+++localhost",
+ newPath: "storage/default/http+++localhost",
+ url: "http://localhost",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/http+++www.mozilla.org",
+ newPath: "storage/default/http+++www.mozilla.org",
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/http+++www.mozilla.org+8080",
+ newPath: "storage/default/http+++www.mozilla.org+8080",
+ url: "http://www.mozilla.org:8080",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/https+++www.mozilla.org",
+ newPath: "storage/default/https+++www.mozilla.org",
+ url: "https://www.mozilla.org",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/https+++www.mozilla.org+8080",
+ newPath: "storage/default/https+++www.mozilla.org+8080",
+ url: "https://www.mozilla.org:8080",
+ persistence: "default",
+ },
+
+ {
+ oldPath: "storage/persistent/indexeddb+++fx-devtools",
+ newPath: "storage/permanent/indexeddb+++fx-devtools",
+ url: "indexeddb://fx-devtools",
+ persistence: "persistent",
+ },
+
+ {
+ oldPath: "storage/persistent/moz-safe-about+++home",
+ },
+
+ {
+ oldPath: "storage/persistent/moz-safe-about+home",
+ newPath: "storage/permanent/moz-safe-about+home",
+ url: "moz-safe-about:home",
+ persistence: "persistent",
+ },
+
+ {
+ oldPath:
+ "storage/persistent/resource+++fx-share-addon-at-mozilla-dot-org-fx-share-addon-data",
+ newPath:
+ "storage/permanent/resource+++fx-share-addon-at-mozilla-dot-org-fx-share-addon-data",
+ url: "resource://fx-share-addon-at-mozilla-dot-org-fx-share-addon-data",
+ persistence: "persistent",
+ },
+
+ {
+ oldPath: "storage/temporary/1007+f+app+++system.gaiamobile.org",
+ },
+
+ {
+ oldPath: "storage/temporary/1007+t+https+++developer.cdn.mozilla.net",
+ },
+
+ // The .metadata file was intentionally appended for this origin directory
+ // to test recovery from unfinished upgrades (some metadata files can be
+ // already upgraded).
+ {
+ oldPath: "storage/temporary/chrome",
+ newPath: "storage/temporary/chrome",
+ metadataUpgraded: true,
+ chrome: true,
+ persistence: "temporary",
+ },
+
+ {
+ oldPath: "storage/temporary/http+++localhost",
+ newPath: "storage/temporary/http+++localhost",
+ url: "http://localhost",
+ persistence: "temporary",
+ },
+
+ // The .metadata file was intentionally removed for this origin directory
+ // to test restoring during upgrade.
+ {
+ oldPath: "storage/temporary/http+++localhost+81",
+ newPath: "storage/temporary/http+++localhost+81",
+ metadataRemoved: true,
+ url: "http://localhost:81",
+ persistence: "temporary",
+ },
+
+ // The .metadata file was intentionally truncated for this origin directory
+ // to test restoring during upgrade.
+ {
+ oldPath: "storage/temporary/http+++localhost+82",
+ newPath: "storage/temporary/http+++localhost+82",
+ url: "http://localhost:82",
+ persistence: "temporary",
+ },
+ ];
+
+ const metadataFileName = ".metadata";
+
+ const packages = [
+ // Storage used by 26-35 (storage/persistent/ directory, tracked only
+ // timestamp in .metadata for persistent storage and isApp not tracked in
+ // .metadata for temporary storage).
+ "persistentStorageDirectory_profile",
+ "../persistentStorageDirectory_shared",
+ ];
+
+ let metadataBuffers = [];
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ if (origin.newPath) {
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ if (origin.metadataRemoved) {
+ metadataBuffers.push(new ArrayBuffer(0));
+ } else {
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+
+ yield undefined;
+
+ metadataBuffers.push(fileReader.result);
+ }
+
+ if (origin.newPath != origin.oldPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+ }
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ if (!origin.newPath || origin.newPath != origin.oldPath) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+
+ if (origin.newPath) {
+ let originDir = getRelativeFile(origin.newPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+
+ yield undefined;
+
+ let metadataBuffer = fileReader.result;
+
+ info("Verifying blobs differ");
+
+ if (origin.metadataUpgraded) {
+ ok(
+ compareBuffers(metadataBuffer, metadataBuffers.shift()),
+ "Metadata doesn't differ"
+ );
+ } else {
+ ok(
+ !compareBuffers(metadataBuffer, metadataBuffers.shift()),
+ "Metadata differ"
+ );
+ }
+ }
+ }
+
+ info("Initializing");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ if (origin.newPath) {
+ info("Initializing origin");
+
+ let principal;
+ if (origin.chrome) {
+ principal = getCurrentPrincipal();
+ } else {
+ principal = getPrincipal(origin.url);
+ }
+
+ if (origin.persistence == "persistent") {
+ request = initPersistentOrigin(principal, continueToNextStepSync);
+ } else {
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ }
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_removeOldDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_removeOldDirectory.js
new file mode 100644
index 0000000000..524104b776
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_removeOldDirectory.js
@@ -0,0 +1,102 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify that the old directory is removed in
+ * MaybeUpgradeFromPersistentStorageDirectoryToDefaultStorageDirectory method.
+ */
+
+async function testSteps() {
+ const url = "http://www.mozilla.org";
+ const persistence = "default";
+ const lastAccessed = 0x0005330925e07841;
+
+ const packages = [
+ // Storage used by FF 36-48 (storage/default/ directory and re-created
+ // storage/persistent/ directory by an older FF).
+ "persistentAndDefaultStorageDirectory_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking directories");
+
+ let persistentStorageDir = getRelativeFile("storage/persistent");
+ let exists = persistentStorageDir.exists();
+ ok(exists, "Persistent storage directory does exist");
+
+ let defaultStorageDir = getRelativeFile("storage/default");
+ exists = defaultStorageDir.exists();
+ ok(exists, "Default storage directory does exist");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization and getting
+ // usage is able to ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/permanent/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking directories");
+
+ persistentStorageDir = getRelativeFile("storage/persistent");
+ exists = persistentStorageDir.exists();
+ ok(!exists, "Persistent storage directory doesn't exist");
+
+ defaultStorageDir = getRelativeFile("storage/default");
+ exists = defaultStorageDir.exists();
+ ok(exists, "Default storage directory does exist");
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+
+ info("Initializing origin");
+
+ request = initTemporaryOrigin(persistence, getPrincipal(url));
+ await requestFinished(request);
+
+ ok(!request.result, "Origin directory wasn't created");
+
+ info("Getting usage");
+
+ request = getUsage(function () {}, /* getAll */ true);
+ await requestFinished(request);
+
+ info("Verifying result");
+
+ const result = request.result;
+ is(result.length, 1, "Correct number of usage results");
+
+ info("Verifying usage result");
+
+ const usageResult = result[0];
+ Assert.equal(usageResult.origin, url, "Origin equals");
+ Assert.equal(usageResult.lastAccessed, lastAccessed, "LastAccessed equals");
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js
new file mode 100644
index 0000000000..6965a95e69
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js
@@ -0,0 +1,162 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function* testSteps() {
+ const origins = [
+ {
+ oldPath: "storage/persistent/1007+f+app+++system.gaiamobile.org",
+ upgraded: true,
+ },
+
+ {
+ oldPath: "storage/persistent/1007+t+https+++developer.cdn.mozilla.net",
+ upgraded: true,
+ },
+
+ {
+ oldPath: "storage/persistent/http+++www.mozilla.org",
+ newPath: "storage/default/http+++www.mozilla.org",
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ upgraded: true,
+ },
+ {
+ oldPath: "storage/persistent/http+++www.mozilla.org+8080",
+ newPath: "storage/default/http+++www.mozilla.org+8080",
+ url: "http://www.mozilla.org:8080",
+ persistence: "default",
+ },
+ ];
+
+ const metadataFileName = ".metadata";
+
+ const packages = [
+ // Storage used by FF 26-35 (storage/persistent/ directory with already
+ // upgraded origin directories and not yet upgraded flat origin
+ // directories).
+ "persistentStorageDirectory_originDirectories_profile",
+ "../persistentStorageDirectory_shared",
+ ];
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking origin directories");
+
+ for (const origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ let idbDir = originDir.clone();
+ idbDir.append("idb");
+
+ exists = idbDir.exists();
+ if (origin.upgraded) {
+ ok(exists, "idb directory does exist");
+ } else {
+ ok(!exists, "idb directory doesn't exist");
+ }
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ if (origin.upgraded) {
+ ok(exists, "Metadata file does exist");
+ } else {
+ ok(!exists, "Metadata file doesn't exist");
+ }
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization and getting
+ // usage is able to ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking origin directories");
+
+ for (const origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+
+ if (origin.newPath) {
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ let idbDir = originDir.clone();
+ idbDir.append("idb");
+
+ exists = idbDir.exists();
+ ok(exists, "idb directory does exist");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ ok(exists, "Metadata file does exist");
+ }
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ if (origin.newPath) {
+ info("Initializing origin");
+
+ let principal = getPrincipal(origin.url);
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js
new file mode 100644
index 0000000000..f1e97ab043
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js
@@ -0,0 +1,158 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify UpgradeStorageFrom0_0To1_0 method.
+ */
+
+function* testSteps() {
+ const origins = [
+ {
+ path: "storage/default/1007+f+app+++system.gaiamobile.org",
+ obsolete: true,
+ },
+
+ {
+ path: "storage/default/1007+t+https+++developer.cdn.mozilla.net",
+ obsolete: true,
+ },
+
+ {
+ path: "storage/default/http+++www.mozilla.org",
+ obsolete: false,
+ url: "http://www.mozilla.org",
+ persistence: "default",
+ },
+ ];
+
+ const storageFileName = "storage.sqlite";
+ const metadataFileName = ".metadata";
+ const metadata2FileName = ".metadata-v2";
+
+ const packages = [
+ // Storage used by FF 36-48 (storage/default/ directory, but no
+ // storage.sqlite and no .metadata-v2 files).
+ "version0_0_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking storage file");
+
+ let storageFile = getRelativeFile(storageFileName);
+
+ let exists = storageFile.exists();
+ ok(!exists, "Storage file doesn't exist");
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.path);
+
+ exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ ok(exists, "Metadata file does exist");
+
+ let metadata2File = originDir.clone();
+ metadata2File.append(metadata2FileName);
+
+ exists = metadata2File.exists();
+ ok(!exists, "Metadata file doesn't exist");
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ exists = storageFile.exists();
+ ok(exists, "Storage file does exist");
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.path);
+
+ exists = originDir.exists();
+ if (origin.obsolete) {
+ ok(!exists, "Origin directory doesn't exist");
+ } else {
+ ok(exists, "Origin directory does exist");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ exists = metadataFile.exists();
+ ok(exists, "Metadata file does exist");
+
+ let metadata2File = originDir.clone();
+ metadata2File.append(metadata2FileName);
+
+ exists = metadata2File.exists();
+ ok(exists, "Metadata file does exist");
+ }
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ if (!origin.obsolete) {
+ info("Initializing origin");
+
+ let principal = getPrincipal(origin.url);
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_idb.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_idb.js
new file mode 100644
index 0000000000..34508f85e8
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_idb.js
@@ -0,0 +1,43 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify indexedDB::QuotaClient::UpgradeStorageFrom1_0To2_0
+ * method.
+ */
+
+async function testSteps() {
+ const packages = [
+ // Storage used by FF 49-54 (storage version 1.0 with idb directory).
+ "version1_0_idb_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Initializing");
+
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeAppsData.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeAppsData.js
new file mode 100644
index 0000000000..c3589ee686
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeAppsData.js
@@ -0,0 +1,100 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify MaybeRemoveAppsData method.
+ */
+
+function* testSteps() {
+ const origins = [
+ {
+ path: "storage/default/http+++www.mozilla.org",
+ obsolete: false,
+ },
+
+ {
+ path: "storage/default/app+++system.gaiamobile.org^appId=1007",
+ obsolete: true,
+ },
+
+ {
+ path: "storage/default/https+++developer.cdn.mozilla.net^appId=1007&inBrowser=1",
+ obsolete: true,
+ },
+ ];
+
+ const packages = [
+ // Storage used by FF 49-54 (storage version 1.0 with apps data).
+ "version1_0_appsData_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.path);
+
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once getting usage is able to ignore unknown
+ // directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/permanent/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.path);
+
+ let exists = originDir.exists();
+ if (origin.obsolete) {
+ ok(!exists, "Origin directory doesn't exist");
+ } else {
+ ok(exists, "Origin directory does exist");
+ }
+ }
+
+ info("Getting usage");
+
+ getUsage(grabResultAndContinueHandler, /* getAll */ true);
+ let result = yield undefined;
+
+ info("Verifying result");
+
+ is(result.length, 1, "Correct number of usage results");
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeMorgueDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeMorgueDirectory.js
new file mode 100644
index 0000000000..b58f170d52
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_removeMorgueDirectory.js
@@ -0,0 +1,60 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify MaybeRemoveMorgueDirectory method.
+ */
+
+function* testSteps() {
+ const morgueFile = "storage/default/http+++example.com/morgue";
+
+ const packages = [
+ // Storage used by FF 49-54 (storage version 1.0 with morgue directory).
+ "version1_0_morgueDirectory_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking morgue file");
+
+ let file = getRelativeFile(morgueFile);
+
+ let exists = file.exists();
+ ok(exists, "Morgue file does exist");
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ info("Checking morgue file");
+
+ exists = file.exists();
+ ok(!exists, "Morgue file doesn't exist");
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js
new file mode 100644
index 0000000000..b0706d3640
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js
@@ -0,0 +1,179 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify MaybeStripObsoleteOriginAttributes method.
+ */
+
+loadScript("dom/quota/test/common/file.js");
+
+function* testSteps() {
+ const origins = [
+ {
+ oldPath:
+ "storage/permanent/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ newPath:
+ "storage/permanent/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ url: "moz-extension://8ea6d31b-917c-431f-a204-15b95e904d4f",
+ persistence: "persistent",
+ },
+
+ {
+ oldPath:
+ "storage/temporary/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ newPath:
+ "storage/temporary/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ url: "moz-extension://8ea6d31b-917c-431f-a204-15b95e904d4f",
+ persistence: "temporary",
+ },
+
+ {
+ oldPath:
+ "storage/default/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ newPath:
+ "storage/default/moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ url: "moz-extension://8ea6d31b-917c-431f-a204-15b95e904d4f",
+ persistence: "default",
+ },
+ ];
+
+ const metadataFileName = ".metadata-v2";
+
+ const packages = [
+ // Storage used by FF 49-54 (storage version 1.0 with obsolete origin
+ // attributes).
+ "version1_0_obsoleteOriginAttributes_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ let metadataBuffers = [];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+
+ yield undefined;
+
+ metadataBuffers.push(fileReader.result);
+
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+ }
+
+ info("Initializing");
+
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ // TODO: Remove this block once temporary storage initialization is able to
+ // ignore unknown directories.
+ getRelativeFile("storage/default/invalid+++example.com").remove(false);
+ getRelativeFile("storage/temporary/invalid+++example.com").remove(false);
+
+ info("Checking origin directories");
+
+ for (let origin of origins) {
+ let originDir = getRelativeFile(origin.oldPath);
+ let exists = originDir.exists();
+ ok(!exists, "Origin directory doesn't exist");
+
+ originDir = getRelativeFile(origin.newPath);
+ exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+
+ yield undefined;
+
+ let metadataBuffer = fileReader.result;
+
+ info("Verifying blobs differ");
+
+ ok(
+ !compareBuffers(metadataBuffer, metadataBuffers.shift()),
+ "Metadata differ"
+ );
+ }
+
+ info("Initializing temporary storage");
+
+ request = initTemporaryStorage(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Initializing origins");
+
+ for (const origin of origins) {
+ info("Initializing origin");
+
+ let principal = getPrincipal(origin.url);
+ if (origin.persistence == "persistent") {
+ request = initPersistentOrigin(principal, continueToNextStepSync);
+ } else {
+ request = initTemporaryOrigin(
+ origin.persistence,
+ principal,
+ continueToNextStepSync
+ );
+ }
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ ok(!request.result, "Origin directory wasn't created");
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_0.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_0.js
new file mode 100644
index 0000000000..f161820a8f
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_0.js
@@ -0,0 +1,97 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify UpgradeStorageFrom2_0To2_1 method.
+ */
+
+function* testSteps() {
+ const origins = [
+ "storage/default/chrome/",
+ "storage/default/http+++www.mozilla.org/",
+ ];
+ const paddingFilePath = "cache/.padding";
+
+ const packages = [
+ // Storage used by FF 55-56 (storage version 2.0).
+ // The profile contains two cache storages:
+ // - storage/default/chrome/cache,
+ // - storage/default/http+++www.mozilla.org/cache
+ // The file create_cache.js in the package was run locally, specifically it
+ // was temporarily added to xpcshell.ini and then executed:
+ // mach xpcshell-test --interactive dom/quota/test/xpcshell/create_cache.js
+ // Note: it only creates the directory "storage/default/chrome/cache".
+ // To make it become the profile in the test, two more manual steps are
+ // needed.
+ // 1. Remove the folder "storage/temporary".
+ // 2. Copy the content under the "storage/default/chrome" to
+ // "storage/default/http+++www.mozilla.org".
+ // 3. Manually create an asmjs folder under the
+ // "storage/default/http+++www.mozilla.org/".
+ "version2_0_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ clear(continueToNextStepSync);
+ yield undefined;
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking padding files before upgrade (storage version 2.0)");
+
+ for (let origin of origins) {
+ let paddingFile = getRelativeFile(origin + paddingFilePath);
+ let exists = paddingFile.exists();
+ ok(!exists, "Padding file doesn't exist");
+ }
+
+ info("Initializing");
+
+ // Initialize to trigger storage upgrade from version 2.0.
+ let request = init(continueToNextStepSync);
+ yield undefined;
+
+ Assert.equal(request.resultCode, NS_OK, "Initialization succeeded");
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ info("Checking padding files after upgrade");
+
+ for (let origin of origins) {
+ let paddingFile = getRelativeFile(origin + paddingFilePath);
+ let exists = paddingFile.exists();
+ ok(exists, "Padding file does exist");
+
+ info("Reading out contents of padding file");
+
+ File.createFromNsIFile(paddingFile).then(grabArgAndContinueHandler);
+ let domFile = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(domFile);
+ yield undefined;
+
+ let paddingFileInfo = new Float64Array(fileReader.result);
+ Assert.equal(paddingFileInfo.length, 1, "Padding file does take 64 bytes.");
+ Assert.equal(paddingFileInfo[0], 0, "Padding size does default to zero.");
+ }
+
+ finishTest();
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_1.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_1.js
new file mode 100644
index 0000000000..eed7ed21d5
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_1.js
@@ -0,0 +1,85 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify UpgradeStorageFrom2_1To2_2 method (removal of
+ * obsolete origins, deprecated clients and unknown temporary files).
+ */
+
+async function testSteps() {
+ const filePaths = [
+ // Obsolete origins:
+ "storage/default/chrome+++content+browser.xul",
+
+ "storage/default/moz-safe-about+++home",
+
+ // TODO: These three origins don't belong here! They were added one release
+ // later and the origin parser was fixed to handle these origins one
+ // release later as well, so users which already upgraded to 2.2 may
+ // still have issues related to these origins!
+ "storage/default/about+home+1",
+
+ "storage/default/about+home+1+q",
+
+ // about:reader?url=xxx (before bug 1422456)
+ "storage/default/about+reader+url=https%3A%2F%2Fexample.com",
+
+ // Deprecated client:
+ "storage/default/https+++example.com/asmjs",
+
+ // Unknown temporary file:
+ "storage/default/https+++example.com/idb/UUID123.tmp",
+ ];
+
+ const packages = [
+ // Storage used by FF 57-67 (storage version 2.1 with obsolete origins, a
+ // deprecated client and an unknown temporary file).
+ "version2_1_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ info("Checking files and directories before upgrade (storage version 2.1)");
+
+ for (const filePath of filePaths) {
+ let file = getRelativeFile(filePath);
+ let exists = file.exists();
+ ok(exists, "File or directory does exist");
+ }
+
+ info("Initializing");
+
+ // Initialize to trigger storage upgrade from version 2.1
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ info("Checking files and directories after upgrade");
+
+ for (const filePath of filePaths) {
+ let file = getRelativeFile(filePath);
+ let exists = file.exists();
+ ok(!exists, "File or directory does not exist");
+ }
+}
diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_2.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_2.js
new file mode 100644
index 0000000000..8f41c05b49
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom2_2.js
@@ -0,0 +1,64 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This test is mainly to verify UpgradeStorageFrom2_2To2_3 method.
+ */
+
+async function testSteps() {
+ const packages = [
+ // Storage used by FF 68-69 (storage version 2.2).
+ "version2_2_profile",
+ "../defaultStorageDirectory_shared",
+ ];
+
+ function verifyDatabaseTable(shouldExist) {
+ let file = getRelativeFile("storage.sqlite");
+ let conn = Services.storage.openUnsharedDatabase(file);
+
+ let exists = conn.tableExists("database");
+ if (shouldExist) {
+ ok(exists, "Database table does exist");
+ } else {
+ ok(!exists, "Database table does not exist");
+ }
+
+ conn.close();
+ }
+
+ info("Clearing");
+
+ let request = clear();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "beforeInstall");
+
+ info("Installing packages");
+
+ installPackages(packages);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInstall");
+
+ verifyDatabaseTable(/* shouldExist */ false);
+
+ info("Initializing");
+
+ // Initialize to trigger storage upgrade from version 2.2
+ request = init();
+ await requestFinished(request);
+
+ info("Verifying storage");
+
+ verifyStorage(packages, "afterInit");
+
+ request = reset();
+ await requestFinished(request);
+
+ verifyDatabaseTable(/* shouldExist */ true);
+}
diff --git a/dom/quota/test/xpcshell/upgrades/version0_0_profile.json b/dom/quota/test/xpcshell/upgrades/version0_0_profile.json
new file mode 100644
index 0000000000..05c0d27fe3
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version0_0_profile.json
@@ -0,0 +1,88 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "1007+f+app+++system.gaiamobile.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "1007+t+https+++developer.cdn.mozilla.org",
+ "dir": true,
+ "entries": [{ "name": ".metadata", "dir": false }]
+ },
+ {
+ "name": "https+++developer.cdn.mozilla.org",
+ "dir": true
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "todo": "This shouldn't exist, it regressed after accidental changes done in bug 1320404",
+ "name": "https+++developer.cdn.mozilla.net",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "https+++developer.cdn.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version0_0_profile.zip b/dom/quota/test/xpcshell/upgrades/version0_0_profile.zip
new file mode 100644
index 0000000000..5ef577191c
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version0_0_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.json b/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.json
new file mode 100644
index 0000000000..88f5d5dcee
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.json
@@ -0,0 +1,72 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "app+++system.gaiamobile.org^appId=1007",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "https+++developer.cdn.mozilla.net^appId=1007&inBrowser=1",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.zip b/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.zip
new file mode 100644
index 0000000000..582edb43af
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_appsData_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.json b/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.json
new file mode 100644
index 0000000000..9f3b53f57d
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.json
@@ -0,0 +1,73 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-safe-about+home",
+ "dir": true,
+ "entries": [
+ {
+ "name": "idb",
+ "dir": true,
+ "entries": [
+ { "name": "631132235dGb", "dir": true },
+ { "name": "631132235dGb.files", "dir": true },
+ { "name": "631132235dGb.sqlite", "dir": false }
+ ]
+ },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-safe-about+home",
+ "dir": true,
+ "entries": [
+ {
+ "name": "idb",
+ "dir": true,
+ "entries": [
+ { "name": "631132235dGb.files", "dir": true },
+ { "name": "631132235dGb.sqlite", "dir": false }
+ ]
+ },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.zip b/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.zip
new file mode 100644
index 0000000000..8abfae79c2
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_idb_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.json b/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.json
new file mode 100644
index 0000000000..855f7846bc
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.json
@@ -0,0 +1,57 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "morgue", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "http+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.zip b/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.zip
new file mode 100644
index 0000000000..88543784ec
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_morgueDirectory_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.json b/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.json
new file mode 100644
index 0000000000..071c4413f4
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.json
@@ -0,0 +1,112 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "permanent",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "temporary",
+ "dir": true,
+ "entries": [
+ {
+ "name": "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f",
+ "dir": true,
+ "entries": [
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.zip b/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.zip
new file mode 100644
index 0000000000..2b4125edf9
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version1_0_obsoleteOriginAttributes_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version2_0_profile.json b/dom/quota/test/xpcshell/upgrades/version2_0_profile.json
new file mode 100644
index 0000000000..04ad73eae3
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_0_profile.json
@@ -0,0 +1,105 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [
+ { "name": "morgue", "dir": true },
+ { "name": "caches.sqlite", "dir": false }
+ ]
+ },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ { "name": "asmjs", "dir": true },
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [
+ { "name": "morgue", "dir": true },
+ { "name": "caches.sqlite", "dir": false }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "chrome",
+ "dir": true,
+ "entries": [
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [
+ { "name": "morgue", "dir": true },
+ { "name": ".padding", "dir": false },
+ { "name": "caches.sqlite", "dir": false }
+ ]
+ },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ },
+ {
+ "name": "http+++www.mozilla.org",
+ "dir": true,
+ "entries": [
+ {
+ "name": "cache",
+ "dir": true,
+ "entries": [
+ { "name": "morgue", "dir": true },
+ { "name": ".padding", "dir": false },
+ { "name": "caches.sqlite", "dir": false }
+ ]
+ },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version2_0_profile.zip b/dom/quota/test/xpcshell/upgrades/version2_0_profile.zip
new file mode 100644
index 0000000000..c140df56e4
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_0_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version2_1_profile.json b/dom/quota/test/xpcshell/upgrades/version2_1_profile.json
new file mode 100644
index 0000000000..a7866d1123
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_1_profile.json
@@ -0,0 +1,69 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ { "name": "about+home+1", "dir": true },
+ { "name": "about+home+1+q", "dir": true },
+ {
+ "name": "about+reader+url=https%3A%2F%2Fexample.com",
+ "dir": true
+ },
+ { "name": "chrome+++content+browser.xul", "dir": true },
+ {
+ "name": "https+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "asmjs", "dir": true },
+ {
+ "name": "idb",
+ "dir": true,
+ "entries": [{ "name": "UUID123.tmp", "dir": false }]
+ }
+ ]
+ },
+ { "name": "moz-safe-about+++home", "dir": true }
+ ]
+ }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [
+ {
+ "name": "default",
+ "dir": true,
+ "entries": [
+ {
+ "name": "https+++example.com",
+ "dir": true,
+ "entries": [
+ { "name": "idb", "dir": true },
+ { "name": ".metadata", "dir": false },
+ { "name": ".metadata-v2", "dir": false }
+ ]
+ }
+ ]
+ },
+ { "name": "ls-archive.sqlite", "dir": false }
+ ]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version2_1_profile.zip b/dom/quota/test/xpcshell/upgrades/version2_1_profile.zip
new file mode 100644
index 0000000000..908dac7058
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_1_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/version2_2_profile.json b/dom/quota/test/xpcshell/upgrades/version2_2_profile.json
new file mode 100644
index 0000000000..4b7265e3b4
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_2_profile.json
@@ -0,0 +1,18 @@
+[
+ { "key": "beforeInstall", "entries": [] },
+ {
+ "key": "afterInstall",
+ "entries": [{ "name": "storage.sqlite", "dir": false }]
+ },
+ {
+ "key": "afterInit",
+ "entries": [
+ {
+ "name": "storage",
+ "dir": true,
+ "entries": [{ "name": "ls-archive.sqlite", "dir": false }]
+ },
+ { "name": "storage.sqlite", "dir": false }
+ ]
+ }
+]
diff --git a/dom/quota/test/xpcshell/upgrades/version2_2_profile.zip b/dom/quota/test/xpcshell/upgrades/version2_2_profile.zip
new file mode 100644
index 0000000000..b6ae7e7d76
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/version2_2_profile.zip
Binary files differ
diff --git a/dom/quota/test/xpcshell/upgrades/xpcshell.toml b/dom/quota/test/xpcshell/upgrades/xpcshell.toml
new file mode 100644
index 0000000000..2b771f619f
--- /dev/null
+++ b/dom/quota/test/xpcshell/upgrades/xpcshell.toml
@@ -0,0 +1,75 @@
+[DEFAULT]
+head = "head.js"
+support-files = [
+ "cacheVersion1_profile.json",
+ "cacheVersion1_profile.zip",
+ "indexedDBAndPersistentStorageDirectory_profile.json",
+ "indexedDBAndPersistentStorageDirectory_profile.zip",
+ "indexedDBDirectory_flatOriginDirectories_profile.json",
+ "indexedDBDirectory_flatOriginDirectories_profile.zip",
+ "indexedDBDirectory_profile.json",
+ "indexedDBDirectory_profile.zip",
+ "localStorageArchive1upgrade_profile.zip",
+ "localStorageArchive4upgrade_profile.zip",
+ "localStorageArchiveDowngrade_profile.zip",
+ "persistentAndDefaultStorageDirectory_profile.json",
+ "persistentAndDefaultStorageDirectory_profile.zip",
+ "persistentStorageDirectory_flatOriginDirectories_profile.json",
+ "persistentStorageDirectory_flatOriginDirectories_profile.zip",
+ "persistentStorageDirectory_originDirectories_profile.json",
+ "persistentStorageDirectory_originDirectories_profile.zip",
+ "persistentStorageDirectory_profile.json",
+ "persistentStorageDirectory_profile.zip",
+ "version0_0_profile.json",
+ "version0_0_profile.zip",
+ "version1_0_appsData_profile.json",
+ "version1_0_appsData_profile.zip",
+ "version1_0_idb_profile.json",
+ "version1_0_idb_profile.zip",
+ "version1_0_morgueDirectory_profile.json",
+ "version1_0_morgueDirectory_profile.zip",
+ "version1_0_obsoleteOriginAttributes_profile.json",
+ "version1_0_obsoleteOriginAttributes_profile.zip",
+ "version2_0_profile.json",
+ "version2_0_profile.zip",
+ "version2_1_profile.json",
+ "version2_1_profile.zip",
+ "version2_2_profile.json",
+ "version2_2_profile.zip",
+]
+
+["test_localStorageArchive1upgrade.js"]
+
+["test_localStorageArchive4upgrade.js"]
+
+["test_localStorageArchiveDowngrade.js"]
+
+["test_upgradeCacheFrom1.js"]
+
+["test_upgradeFromFlatOriginDirectories.js"]
+
+["test_upgradeFromIndexedDBDirectory.js"]
+
+["test_upgradeFromIndexedDBDirectory_removeOldDirectory.js"]
+
+["test_upgradeFromPersistentStorageDirectory.js"]
+
+["test_upgradeFromPersistentStorageDirectory_removeOldDirectory.js"]
+
+["test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js"]
+
+["test_upgradeStorageFrom0_0.js"]
+
+["test_upgradeStorageFrom1_0_idb.js"]
+
+["test_upgradeStorageFrom1_0_removeAppsData.js"]
+
+["test_upgradeStorageFrom1_0_removeMorgueDirectory.js"]
+
+["test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js"]
+
+["test_upgradeStorageFrom2_0.js"]
+
+["test_upgradeStorageFrom2_1.js"]
+
+["test_upgradeStorageFrom2_2.js"]
diff --git a/dom/quota/test/xpcshell/xpcshell.toml b/dom/quota/test/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..736a2c2e1e
--- /dev/null
+++ b/dom/quota/test/xpcshell/xpcshell.toml
@@ -0,0 +1,100 @@
+[DEFAULT]
+head = "head.js"
+tags = "condprof"
+support-files = [
+ "basics_profile.zip",
+ "clearStoragesForOriginPrefix_profile.json",
+ "clearStoragesForOriginPrefix_profile.zip",
+ "clearStoragesForPrincipal_profile.zip",
+ "clearStoragesForPrivateBrowsing_profile.json",
+ "clearStoragesForPrivateBrowsing_profile.zip",
+ "createLocalStorage_profile.zip",
+ "defaultStorageDirectory_shared.json",
+ "defaultStorageDirectory_shared.zip",
+ "getUsage_profile.zip",
+ "groupMismatch_profile.zip",
+ "indexedDBDirectory_shared.json",
+ "indexedDBDirectory_shared.zip",
+ "originMismatch_profile.json",
+ "originMismatch_profile.zip",
+ "persistentStorageDirectory_shared.json",
+ "persistentStorageDirectory_shared.zip",
+ "removeLocalStorage1_profile.zip",
+ "removeLocalStorage2_profile.zip",
+ "tempMetadataCleanup_profile.zip",
+ "unknownFiles_profile.zip",
+]
+
+["make_unknownFiles.js"]
+skip-if = ["true"] # Only used for recreating unknownFiles_profile.zip
+
+["make_unsetLastAccessTime.js"]
+skip-if = ["true"] # Only used for recreating unsetLastAccessTime_profile.zip
+
+["test_allowListFiles.js"]
+
+["test_bad_origin_directory.js"]
+
+["test_basics.js"]
+
+["test_clearStoragesForOriginAttributesPattern.js"]
+
+["test_clearStoragesForOriginPrefix.js"]
+
+["test_clearStoragesForPrincipal.js"]
+
+["test_clearStoragesForPrivateBrowsing.js"]
+
+["test_createLocalStorage.js"]
+
+["test_estimateOrigin.js"]
+
+["test_getUsage.js"]
+
+["test_groupMismatch.js"]
+skip-if = ["true"] # The group is now always empty, so metadata can't differ anymore.
+
+["test_initTemporaryStorage.js"]
+skip-if = ["condprof"] # frequent perma fail, then goes away.
+
+["test_initializePersistentClient.js"]
+
+["test_initializeTemporaryClient.js"]
+
+["test_listOrigins.js"]
+
+["test_originEndsWithDot.js"]
+
+["test_originMismatch.js"]
+
+["test_originWithCaret.js"]
+
+["test_orpahnedQuotaObject.js"]
+
+["test_persist.js"]
+
+["test_persist_eviction.js"]
+
+["test_persist_globalLimit.js"]
+
+["test_persist_groupLimit.js"]
+
+["test_removeLocalStorage.js"]
+
+["test_simpledb.js"]
+
+["test_specialOrigins.js"]
+
+["test_storagePressure.js"]
+skip-if = ["condprof"]
+
+["test_tempMetadataCleanup.js"]
+
+["test_unaccessedOrigins.js"]
+
+["test_unknownFiles.js"]
+
+["test_unsetLastAccessTime.js"]
+support-files = ["unsetLastAccessTime_profile.zip"]
+
+["test_validOrigins.js"]