2 * Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
6 const NS_OK
= Cr
.NS_OK
;
7 const NS_ERROR_FAILURE
= Cr
.NS_ERROR_FAILURE
;
8 const NS_ERROR_UNEXPECTED
= Cr
.NS_ERROR_UNEXPECTED
;
9 const NS_ERROR_FILE_NO_DEVICE_SPACE
= Cr
.NS_ERROR_FILE_NO_DEVICE_SPACE
;
11 const loggingEnabled
= false;
15 loadScript("dom/quota/test/common/xpcshell.js");
23 function is(a
, b
, msg
) {
24 Assert
.equal(a
, b
, msg
);
27 function ok(cond
, msg
) {
28 Assert
.ok(!!cond
, msg
);
31 function todo(cond
, msg
) {
32 todo_check_true(cond
);
40 this.runTest = function () {
43 enableStorageTesting();
46 // In order to support converting tests to using async functions from using
47 // generator functions, we detect async functions by checking the name of
48 // function's constructor.
50 typeof testSteps
=== "function",
51 "There should be a testSteps function"
53 if (testSteps
.constructor.name
=== "AsyncFunction") {
54 // Do run our existing cleanup function that would normally be called by
55 // the generator's call to finishTest().
56 registerCleanupFunction(function () {
57 resetStorageTesting();
63 // Since we defined run_test, we must invoke run_next_test() to start the
68 testSteps
.constructor.name
=== "GeneratorFunction",
69 "Unsupported function type"
74 testGenerator
= testSteps();
80 function finishTest() {
81 resetStorageTesting();
84 executeSoon(function () {
89 function grabArgAndContinueHandler(arg
) {
90 testGenerator
.next(arg
);
93 function continueToNextStep() {
94 executeSoon(function () {
99 function continueToNextStepSync() {
100 testGenerator
.next();
103 function enableTesting() {
104 SpecialPowers
.setBoolPref(
105 "dom.storage.enable_unsupported_legacy_implementation",
110 function resetTesting() {
111 SpecialPowers
.clearUserPref(
112 "dom.storage.enable_unsupported_legacy_implementation"
116 function setGlobalLimit(globalLimit
) {
117 SpecialPowers
.setIntPref(
118 "dom.quotaManager.temporaryStorage.fixedLimit",
123 function resetGlobalLimit() {
124 SpecialPowers
.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
127 function storageInitialized(callback
) {
128 let request
= SpecialPowers
._getQuotaManager().storageInitialized();
129 request
.callback
= callback
;
134 function persistentStorageInitialized(callback
) {
135 let request
= SpecialPowers
._getQuotaManager().persistentStorageInitialized();
136 request
.callback
= callback
;
141 function temporaryStorageInitialized(callback
) {
142 let request
= SpecialPowers
._getQuotaManager().temporaryStorageInitialized();
143 request
.callback
= callback
;
148 function persistentOriginInitialized(principal
, callback
) {
150 SpecialPowers
._getQuotaManager().persistentOriginInitialized(principal
);
151 request
.callback
= callback
;
156 function temporaryOriginInitialized(persistence
, principal
, callback
) {
157 let request
= SpecialPowers
._getQuotaManager().temporaryOriginInitialized(
161 request
.callback
= callback
;
166 function init(callback
) {
167 let request
= SpecialPowers
._getQuotaManager().init();
168 request
.callback
= callback
;
173 function initializePersistentStorage(callback
) {
174 let request
= SpecialPowers
._getQuotaManager().initializePersistentStorage();
175 request
.callback
= callback
;
180 function initTemporaryStorage(callback
) {
181 let request
= SpecialPowers
._getQuotaManager().initTemporaryStorage();
182 request
.callback
= callback
;
187 function initPersistentOrigin(principal
, callback
) {
189 SpecialPowers
._getQuotaManager().initializePersistentOrigin(principal
);
190 request
.callback
= callback
;
195 function initTemporaryOrigin(
198 createIfNonExistent
= true,
201 let request
= SpecialPowers
._getQuotaManager().initializeTemporaryOrigin(
206 request
.callback
= callback
;
211 function initPersistentClient(principal
, client
, callback
) {
212 let request
= SpecialPowers
._getQuotaManager().initializePersistentClient(
216 request
.callback
= callback
;
221 function initTemporaryClient(persistence
, principal
, client
, callback
) {
222 let request
= SpecialPowers
._getQuotaManager().initializeTemporaryClient(
227 request
.callback
= callback
;
232 function getFullOriginMetadata(persistence
, principal
, callback
) {
233 const request
= SpecialPowers
._getQuotaManager().getFullOriginMetadata(
237 request
.callback
= callback
;
242 function clearClient(principal
, client
, persistence
, callback
) {
243 let request
= SpecialPowers
._getQuotaManager().clearStoragesForClient(
248 request
.callback
= callback
;
253 function clearOrigin(principal
, persistence
, callback
) {
254 let request
= SpecialPowers
._getQuotaManager().clearStoragesForPrincipal(
258 request
.callback
= callback
;
263 function clearOriginsByPrefix(principal
, persistence
, callback
) {
264 let request
= SpecialPowers
._getQuotaManager().clearStoragesForOriginPrefix(
268 request
.callback
= callback
;
273 function clearPrivateBrowsing(callback
) {
275 SpecialPowers
._getQuotaManager().clearStoragesForPrivateBrowsing();
276 request
.callback
= callback
;
281 function resetClient(principal
, client
) {
282 let request
= Services
.qms
.resetStoragesForClient(
291 function persist(principal
, callback
) {
292 let request
= SpecialPowers
._getQuotaManager().persist(principal
);
293 request
.callback
= callback
;
298 function persisted(principal
, callback
) {
299 let request
= SpecialPowers
._getQuotaManager().persisted(principal
);
300 request
.callback
= callback
;
305 function estimateOrigin(principal
, callback
) {
306 let request
= SpecialPowers
._getQuotaManager().estimate(principal
);
307 request
.callback
= callback
;
312 function listOrigins(callback
) {
313 let request
= SpecialPowers
._getQuotaManager().listOrigins(callback
);
314 request
.callback
= callback
;
319 function getPersistedFromMetadata(readBuffer
) {
320 const persistedPosition
= 8; // Persisted state is stored in the 9th byte
322 readBuffer
instanceof Uint8Array
? readBuffer
: new Uint8Array(readBuffer
);
324 return !!view
[persistedPosition
];
327 function grabResultAndContinueHandler(request
) {
328 testGenerator
.next(request
.result
);
331 function grabUsageAndContinueHandler(request
) {
332 testGenerator
.next(request
.result
.usage
);
335 function getUsage(usageHandler
, getAll
) {
336 let request
= SpecialPowers
._getQuotaManager().getUsage(usageHandler
, getAll
);
341 function getOriginUsage(principal
) {
342 let request
= Services
.qms
.getUsageForPrincipal(principal
, function () {});
347 function getCachedOriginUsage(principal
) {
348 let request
= Services
.qms
.getCachedUsageForPrincipal(
356 function getCachedOriginUsage(principal
) {
357 let request
= Services
.qms
.getCachedUsageForPrincipal(principal
);
362 function getCurrentUsage(usageHandler
) {
363 let principal
= Cc
["@mozilla.org/systemprincipal;1"].createInstance(
366 let request
= SpecialPowers
._getQuotaManager().getUsageForPrincipal(
374 function getPrincipal(url
, attr
= {}) {
375 let uri
= Cc
["@mozilla.org/network/io-service;1"]
376 .getService(Ci
.nsIIOService
)
378 let ssm
= Cc
["@mozilla.org/scriptsecuritymanager;1"].getService(
379 Ci
.nsIScriptSecurityManager
381 return ssm
.createContentPrincipal(uri
, attr
);
384 var SpecialPowers
= {
385 getBoolPref(prefName
) {
386 return this._getPrefs().getBoolPref(prefName
);
389 setBoolPref(prefName
, value
) {
390 this._getPrefs().setBoolPref(prefName
, value
);
393 setIntPref(prefName
, value
) {
394 this._getPrefs().setIntPref(prefName
, value
);
397 clearUserPref(prefName
) {
398 this._getPrefs().clearUserPref(prefName
);
402 let prefService
= Cc
["@mozilla.org/preferences-service;1"].getService(
405 return prefService
.getBranch(null);
409 return Cc
["@mozilla.org/dom/quota-manager-service;1"].getService(
410 Ci
.nsIQuotaManagerService
415 function installPackages(packageRelativePaths
) {
416 if (packageRelativePaths
.length
!= 2) {
417 throw new Error("Unsupported number of package relative paths");
420 for (const packageRelativePath
of packageRelativePaths
) {
421 installPackage(packageRelativePath
);
425 // Take current storage structure on disk and compare it with the expected
426 // structure. The expected structure is defined in JSON and consists of a per
427 // test package definition and a shared package definition. The shared package
428 // definition should contain unknown stuff which needs to be properly handled
429 // in all situations.
430 function verifyStorage(packageDefinitionRelativePaths
, key
, sharedKey
) {
431 if (packageDefinitionRelativePaths
.length
!= 2) {
432 throw new Error("Unsupported number of package definition relative paths");
435 function verifyEntries(entries
, name
, indent
= "") {
436 log(`${indent}Verifying ${name} entries`);
440 for (const entry
of entries
) {
441 const maybeName
= entry
.name
;
443 log(`${indent}Verifying entry ${maybeName}`);
447 let hasEntries
= false;
449 for (const property
in entry
) {
468 throw new Error(`Unknown property ${property}`);
473 throw new Error("An entry must have the name property");
477 throw new Error("An entry must have the dir property");
480 if (hasEntries
&& !entry
.dir
) {
481 throw new Error("An entry can't have entries if it's not a directory");
485 verifyEntries(entry
.entries
, entry
.name
, indent
);
490 function getCurrentEntries() {
491 log("Getting current entries");
493 function getEntryForFile(file
) {
496 dir
: file
.isDirectory(),
499 if (file
.isDirectory()) {
500 const enumerator
= file
.directoryEntries
;
502 while ((nextFile
= enumerator
.nextFile
)) {
503 if (!entry
.entries
) {
506 entry
.entries
.push(getEntryForFile(nextFile
));
515 let file
= getRelativeFile("indexedDB");
517 entries
.push(getEntryForFile(file
));
520 file
= getRelativeFile("storage");
522 entries
.push(getEntryForFile(file
));
525 file
= getRelativeFile("storage.sqlite");
527 entries
.push(getEntryForFile(file
));
530 verifyEntries(entries
, "current");
535 function getEntriesFromPackageDefinition(
536 packageDefinitionRelativePath
,
539 log(`Getting ${lookupKey} entries from ${packageDefinitionRelativePath}`);
541 const currentDir
= Services
.dirsvc
.get("CurWorkD", Ci
.nsIFile
);
542 const file
= getRelativeFile(
543 packageDefinitionRelativePath
+ ".json",
547 const fileInputStream
= Cc
[
548 "@mozilla.org/network/file-input-stream;1"
549 ].createInstance(Ci
.nsIFileInputStream
);
550 fileInputStream
.init(file
, -1, -1, 0);
552 const scriptableInputStream
= Cc
[
553 "@mozilla.org/scriptableinputstream;1"
554 ].createInstance(Ci
.nsIScriptableInputStream
);
555 scriptableInputStream
.init(fileInputStream
);
557 const data
= scriptableInputStream
.readBytes(
558 scriptableInputStream
.available()
561 const obj
= JSON
.parse(data
);
563 const result
= obj
.find(({ key
: elementKey
}) => elementKey
== lookupKey
);
566 throw new Error("The file doesn't contain an element for given key");
569 if (!result
.entries
) {
570 throw new Error("The element doesn't have the entries property");
573 verifyEntries(result
.entries
, lookupKey
);
575 return result
.entries
;
578 function addSharedEntries(expectedEntries
, sharedEntries
, name
, indent
= "") {
579 log(`${indent}Checking common ${name} entries`);
583 for (const sharedEntry
of sharedEntries
) {
584 const expectedEntry
= expectedEntries
.find(
585 ({ name
: elementName
}) => elementName
== sharedEntry
.name
589 log(`${indent}Checking common entry ${sharedEntry.name}`);
591 if (!expectedEntry
.dir
|| !sharedEntry
.dir
) {
592 throw new Error("A common entry must be a directory");
595 if (!expectedEntry
.entries
&& !sharedEntry
.entries
) {
596 throw new Error("A common entry must not be a leaf");
599 if (sharedEntry
.entries
) {
600 if (!expectedEntry
.entries
) {
601 expectedEntry
.entries
= [];
605 expectedEntry
.entries
,
612 log(`${indent}Adding entry ${sharedEntry.name}`);
613 expectedEntries
.push(sharedEntry
);
618 function compareEntries(currentEntries
, expectedEntries
, name
, indent
= "") {
619 log(`${indent}Comparing ${name} entries`);
623 if (currentEntries
.length
!= expectedEntries
.length
) {
624 throw new Error("Entries must have the same length");
627 for (const currentEntry
of currentEntries
) {
628 log(`${indent}Comparing entry ${currentEntry.name}`);
630 const expectedEntry
= expectedEntries
.find(
631 ({ name
: elementName
}) => elementName
== currentEntry
.name
634 if (!expectedEntry
) {
635 throw new Error("Cannot find a matching entry");
638 if (expectedEntry
.dir
!= currentEntry
.dir
) {
639 throw new Error("The dir property doesn't match");
643 (expectedEntry
.entries
&& !currentEntry
.entries
) ||
644 (!expectedEntry
.entries
&& currentEntry
.entries
)
646 throw new Error("The entries property doesn't match");
649 if (expectedEntry
.entries
) {
651 currentEntry
.entries
,
652 expectedEntry
.entries
,
660 const currentEntries
= getCurrentEntries();
662 log("Stringified current entries: " + JSON
.stringify(currentEntries
));
664 const expectedEntries
= getEntriesFromPackageDefinition(
665 packageDefinitionRelativePaths
[0],
668 const sharedEntries
= getEntriesFromPackageDefinition(
669 packageDefinitionRelativePaths
[1],
670 sharedKey
? sharedKey
: key
673 addSharedEntries(expectedEntries
, sharedEntries
, key
);
675 log("Stringified expected entries: " + JSON
.stringify(expectedEntries
));
677 compareEntries(currentEntries
, expectedEntries
, key
);
680 async
function verifyInitializationStatus(
681 expectStorageIsInitialized
,
682 expectPersistentStorageIsInitialized
,
683 expectTemporaryStorageIsInitialized
685 if (!expectStorageIsInitialized
&& expectPersistentStorageIsInitialized
) {
686 throw new Error("Invalid expectation");
689 if (!expectStorageIsInitialized
&& expectTemporaryStorageIsInitialized
) {
690 throw new Error("Invalid expectation");
693 let request
= storageInitialized();
694 await
requestFinished(request
);
696 const storageIsInitialized
= request
.result
;
698 request
= persistentStorageInitialized();
699 await
requestFinished(request
);
701 const persistentStorageIsInitialized
= request
.result
;
703 request
= temporaryStorageInitialized();
704 await
requestFinished(request
);
706 const temporaryStorageIsInitialized
= request
.result
;
709 !(!storageIsInitialized
&& persistentStorageIsInitialized
),
710 "Initialization status is consistent"
714 !(!storageIsInitialized
&& temporaryStorageIsInitialized
),
715 "Initialization status is consistent"
718 if (expectStorageIsInitialized
) {
719 ok(storageIsInitialized
, "Storage is initialized");
721 ok(!storageIsInitialized
, "Storage is not initialized");
724 if (expectPersistentStorageIsInitialized
) {
725 ok(persistentStorageIsInitialized
, "Persistent storage is initialized");
728 !persistentStorageIsInitialized
,
729 "Persistent storage is not initialized"
733 if (expectTemporaryStorageIsInitialized
) {
734 ok(temporaryStorageIsInitialized
, "Temporary storage is initialized");
736 ok(!temporaryStorageIsInitialized
, "Temporary storage is not initialized");