2 * Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
6 // Tests using testGenerator are expected to define it themselves.
7 // Testing functions are expected to call testSteps and its type should either
8 // be GeneratorFunction or AsyncFunction
9 /* global testGenerator, testSteps:false */
11 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
13 if (!("self" in this)) {
19 function is(a, b, msg) {
20 Assert.equal(a, b, msg);
23 function ok(cond, msg) {
24 Assert.ok(!!cond, msg);
27 function isnot(a, b, msg) {
28 Assert.notEqual(a, b, msg);
31 function todo(condition, name, diag) {
32 todo_check_true(condition);
40 this.runTest = function() {
41 if (SpecialPowers.isMainProcess()) {
42 // XPCShell does not get a profile by default.
49 Cu.importGlobalProperties(["indexedDB"]);
51 // In order to support converting tests to using async functions from using
52 // generator functions, we detect async functions by checking the name of
53 // function's constructor.
55 typeof testSteps === "function",
56 "There should be a testSteps function"
58 if (testSteps.constructor.name === "AsyncFunction") {
59 // Do run our existing cleanup function that would normally be called by
60 // the generator's call to finishTest().
61 registerCleanupFunction(resetTesting);
65 // Since we defined run_test, we must invoke run_next_test() to start the
70 testSteps.constructor.name === "GeneratorFunction",
71 "Unsupported function type"
80 function finishTest() {
81 if (SpecialPowers.isMainProcess()) {
86 SpecialPowers.removeFiles();
88 executeSoon(function() {
93 function grabEventAndContinueHandler(event) {
94 testGenerator.next(event);
97 function continueToNextStep() {
98 executeSoon(function() {
103 function errorHandler(event) {
105 dump("indexedDB error: " + event.target.error.name);
107 dump("indexedDB error: " + e);
113 function unexpectedSuccessHandler() {
118 function expectedErrorHandler(name) {
119 return function(event) {
120 Assert.equal(event.type, "error");
121 Assert.equal(event.target.error.name, name);
122 event.preventDefault();
123 grabEventAndContinueHandler(event);
127 function expectUncaughtException(expecting) {
128 // This is dummy for xpcshell test.
131 function ExpectError(name, preventDefault) {
133 this._preventDefault = preventDefault;
135 ExpectError.prototype = {
137 Assert.equal(event.type, "error");
138 Assert.equal(this._name, event.target.error.name);
139 if (this._preventDefault) {
140 event.preventDefault();
141 event.stopPropagation();
143 grabEventAndContinueHandler(event);
147 function continueToNextStepSync() {
148 testGenerator.next();
151 // TODO compareKeys is duplicated in ../helpers.js, can we import that here?
152 // the same applies to many other functions in this file
153 // this duplication should be avoided (bug 1565986)
154 function compareKeys(k1, k2) {
156 if (t != typeof k2) {
160 if (t !== "object") {
164 if (k1 instanceof Date) {
165 return k2 instanceof Date && k1.getTime() === k2.getTime();
168 if (k1 instanceof Array) {
169 if (!(k2 instanceof Array) || k1.length != k2.length) {
173 for (let i = 0; i < k1.length; ++i) {
174 if (!compareKeys(k1[i], k2[i])) {
182 if (k1 instanceof ArrayBuffer) {
183 if (!(k2 instanceof ArrayBuffer)) {
187 function arrayBuffersAreEqual(a, b) {
188 if (a.byteLength != b.byteLength) {
191 let ui8b = new Uint8Array(b);
192 return new Uint8Array(a).every((val, i) => val === ui8b[i]);
195 return arrayBuffersAreEqual(k1, k2);
201 function addPermission(permission, url) {
202 throw new Error("addPermission");
205 function removePermission(permission, url) {
206 throw new Error("removePermission");
209 function allowIndexedDB(url) {
210 throw new Error("allowIndexedDB");
213 function disallowIndexedDB(url) {
214 throw new Error("disallowIndexedDB");
217 function enableExperimental() {
218 SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
221 function resetExperimental() {
222 SpecialPowers.clearUserPref("dom.indexedDB.experimental");
225 function enableTesting() {
226 SpecialPowers.setBoolPref("dom.indexedDB.testing", true);
229 function resetTesting() {
230 SpecialPowers.clearUserPref("dom.indexedDB.testing");
238 function scheduleGC() {
239 SpecialPowers.exactGC(continueToNextStep);
242 function setTimeout(fun, timeout) {
243 let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
249 timer.initWithCallback(event, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
253 function initStorage() {
254 return Services.qms.init();
257 function initPersistentOrigin(principal) {
258 return Services.qms.initializePersistentOrigin(principal);
261 function resetOrClearAllDatabases(callback, clear) {
262 if (!SpecialPowers.isMainProcess()) {
263 throw new Error("clearAllDatabases not implemented for child processes!");
266 const quotaPref = "dom.quotaManager.testing";
269 if (Services.prefs.prefHasUserValue(quotaPref)) {
270 oldPrefValue = SpecialPowers.getBoolPref(quotaPref);
273 SpecialPowers.setBoolPref(quotaPref, true);
279 request = Services.qms.clear();
281 request = Services.qms.reset();
284 if (oldPrefValue !== undefined) {
285 SpecialPowers.setBoolPref(quotaPref, oldPrefValue);
287 SpecialPowers.clearUserPref(quotaPref);
292 request.callback = callback;
297 function resetAllDatabases(callback) {
298 return resetOrClearAllDatabases(callback, false);
301 function clearAllDatabases(callback) {
302 return resetOrClearAllDatabases(callback, true);
305 function installPackagedProfile(packageName) {
306 let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
308 let currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
310 let packageFile = currentDir.clone();
311 packageFile.append(packageName + ".zip");
313 let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
316 zipReader.open(packageFile);
319 for (let entry of zipReader.findEntries(null)) {
320 if (entry != "create_db.html") {
321 entryNames.push(entry);
326 for (let entryName of entryNames) {
327 let zipentry = zipReader.getEntry(entryName);
329 let file = profileDir.clone();
330 let split = entryName.split("/");
331 for (let i = 0; i < split.length; i++) {
332 file.append(split[i]);
335 if (zipentry.isDirectory) {
336 file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
338 let istream = zipReader.getInputStream(entryName);
341 "@mozilla.org/network/file-output-stream;1"
342 ].createInstance(Ci.nsIFileOutputStream);
343 ostream.init(file, -1, parseInt("0644", 8), 0);
346 "@mozilla.org/network/buffered-output-stream;1"
347 ].createInstance(Ci.nsIBufferedOutputStream);
348 bostream.init(ostream, 32768);
350 bostream.writeFrom(istream, istream.available());
360 function getChromeFilesDir() {
361 let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
363 let idbDir = profileDir.clone();
364 idbDir.append("storage");
365 idbDir.append("permanent");
366 idbDir.append("chrome");
367 idbDir.append("idb");
369 let idbEntries = idbDir.directoryEntries;
370 while (idbEntries.hasMoreElements()) {
371 let file = idbEntries.nextFile;
372 if (file.isDirectory()) {
377 throw new Error("files directory doesn't exist!");
380 function getView(size) {
381 let buffer = new ArrayBuffer(size);
382 let view = new Uint8Array(buffer);
383 is(buffer.byteLength, size, "Correct byte length");
387 function getRandomView(size) {
388 let view = getView(size);
389 for (let i = 0; i < size; i++) {
390 view[i] = parseInt(Math.random() * 255);
395 function getBlob(str) {
396 return new Blob([str], { type: "type/text" });
399 function getFile(name, type, str) {
400 return new File([str], name, { type });
403 function isWasmSupported() {
404 let testingFunctions = Cu.getJSTestingFunctions();
405 return testingFunctions.wasmIsSupported();
408 function getWasmModule(binary) {
409 let module = new WebAssembly.Module(binary);
413 function compareBuffers(buffer1, buffer2) {
414 if (buffer1.byteLength != buffer2.byteLength) {
418 let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1);
419 let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2);
420 for (let i = 0; i < buffer1.byteLength; i++) {
421 if (view1[i] != view2[i]) {
428 function verifyBuffers(buffer1, buffer2) {
429 ok(compareBuffers(buffer1, buffer2), "Correct buffer data");
432 function verifyBlob(blob1, blob2) {
433 is(Blob.isInstance(blob1), true, "Instance of nsIDOMBlob");
434 is(File.isInstance(blob1), File.isInstance(blob2), "Instance of DOM File");
435 is(blob1.size, blob2.size, "Correct size");
436 is(blob1.type, blob2.type, "Correct type");
437 if (File.isInstance(blob2)) {
438 is(blob1.name, blob2.name, "Correct name");
444 for (let i = 0; i < bufferCache.length; i++) {
445 if (bufferCache[i].blob == blob2) {
446 buffer2 = bufferCache[i].buffer;
452 let reader = new FileReader();
453 reader.readAsArrayBuffer(blob2);
454 reader.onload = function(event) {
455 buffer2 = event.target.result;
456 bufferCache.push({ blob: blob2, buffer: buffer2 });
458 verifyBuffers(buffer1, buffer2);
459 testGenerator.next();
464 let reader = new FileReader();
465 reader.readAsArrayBuffer(blob1);
466 reader.onload = function(event) {
467 buffer1 = event.target.result;
469 verifyBuffers(buffer1, buffer2);
470 testGenerator.next();
475 function verifyView(view1, view2) {
476 is(view1.byteLength, view2.byteLength, "Correct byteLength");
477 verifyBuffers(view1, view2);
478 continueToNextStep();
481 function grabFileUsageAndContinueHandler(request) {
482 testGenerator.next(request.result.fileUsage);
485 function getCurrentUsage(usageHandler) {
486 let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(
489 Services.qms.getUsageForPrincipal(principal, usageHandler);
492 function setTemporaryStorageLimit(limit) {
493 const pref = "dom.quotaManager.temporaryStorage.fixedLimit";
495 info("Setting temporary storage limit to " + limit);
496 SpecialPowers.setIntPref(pref, limit);
498 info("Removing temporary storage limit");
499 SpecialPowers.clearUserPref(pref);
503 function setDataThreshold(threshold) {
504 info("Setting data threshold to " + threshold);
505 SpecialPowers.setIntPref("dom.indexedDB.dataThreshold", threshold);
508 function resetDataThreshold() {
509 info("Clearing data threshold pref");
510 SpecialPowers.clearUserPref("dom.indexedDB.dataThreshold");
513 function setMaxSerializedMsgSize(aSize) {
514 info("Setting maximal size of a serialized message to " + aSize);
515 SpecialPowers.setIntPref("dom.indexedDB.maxSerializedMsgSize", aSize);
518 function enablePreprocessing() {
519 info("Setting preprocessing pref");
520 SpecialPowers.setBoolPref("dom.indexedDB.preprocessing", true);
523 function resetPreprocessing() {
524 info("Clearing preprocessing pref");
525 SpecialPowers.clearUserPref("dom.indexedDB.preprocessing");
528 function getSystemPrincipal() {
529 return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
532 function getPrincipal(url) {
533 let uri = Services.io.newURI(url);
534 return Services.scriptSecurityManager.createContentPrincipal(uri, {});
537 class RequestError extends Error {
538 constructor(resultCode, resultName) {
539 super(`Request failed (code: ${resultCode}, name: ${resultName})`);
540 this.name = "RequestError";
541 this.resultCode = resultCode;
542 this.resultName = resultName;
546 async function requestFinished(request) {
547 await new Promise(function(resolve) {
548 request.callback = function() {
553 if (request.resultCode !== Cr.NS_OK) {
554 throw new RequestError(request.resultCode, request.resultName);
557 return request.result;
560 // TODO: Rename to openDBRequestSucceeded ?
561 function expectingSuccess(request) {
562 return new Promise(function(resolve, reject) {
563 request.onerror = function(event) {
564 ok(false, "indexedDB error, '" + event.target.error.name + "'");
567 request.onsuccess = function(event) {
570 request.onupgradeneeded = function(event) {
571 ok(false, "Got upgrade, but did not expect it!");
577 // TODO: Rename to openDBRequestUpgradeNeeded ?
578 function expectingUpgrade(request) {
579 return new Promise(function(resolve, reject) {
580 request.onerror = function(event) {
581 ok(false, "indexedDB error, '" + event.target.error.name + "'");
584 request.onupgradeneeded = function(event) {
587 request.onsuccess = function(event) {
588 ok(false, "Got success, but did not expect it!");
594 function requestSucceeded(request) {
595 return new Promise(function(resolve, reject) {
596 request.onerror = function(event) {
597 ok(false, "indexedDB error, '" + event.target.error.name + "'");
600 request.onsuccess = function(event) {
606 // Given a "/"-delimited path relative to the profile directory,
607 // return an nsIFile representing the path. This does not test
608 // for the existence of the file or parent directories.
609 // It is safe even on Windows where the directory separator is not "/",
610 // but make sure you're not passing in a "\"-delimited path.
611 function getRelativeFile(relativePath) {
612 let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
614 let file = profileDir.clone();
615 relativePath.split("/").forEach(function(component) {
616 file.append(component);
622 var SpecialPowers = {
625 Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
628 notifyObservers(subject, topic, data) {
629 Services.obs.notifyObservers(subject, topic, data);
631 notifyObserversInParentProcess(subject, topic, data) {
633 throw new Error("Can't send subject to another process!");
635 return this.notifyObservers(subject, topic, data);
637 getBoolPref(prefName) {
638 return Services.prefs.getBoolPref(prefName);
640 setBoolPref(prefName, value) {
641 Services.prefs.setBoolPref(prefName, value);
643 setIntPref(prefName, value) {
644 Services.prefs.setIntPref(prefName, value);
646 clearUserPref(prefName) {
647 Services.prefs.clearUserPref(prefName);
649 // Copied (and slightly adjusted) from testing/specialpowers/content/SpecialPowersAPI.jsm
653 function doPreciseGCandCC() {
654 function scheduledGCCallback() {
664 Cu.schedulePreciseGC(scheduledGCCallback);
682 // Based on SpecialPowersObserver.prototype.receiveMessage
683 createFiles(requests, callback) {
685 if (!this._createdFiles) {
686 this._createdFiles = [];
688 let createdFiles = this._createdFiles;
690 requests.forEach(function(request) {
691 const filePerms = 0o666;
692 let testFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
694 testFile.append(request.name);
696 testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms);
699 "@mozilla.org/network/file-output-stream;1"
700 ].createInstance(Ci.nsIFileOutputStream);
703 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
708 outStream.write(request.data, request.data.length);
712 File.createFromFileName(testFile.path, request.options).then(function(
715 filePaths.push(file);
718 createdFiles.push(testFile);
721 Promise.all(promises).then(function() {
722 setTimeout(function() {
729 if (this._createdFiles) {
730 this._createdFiles.forEach(function(testFile) {
732 testFile.remove(false);
735 this._createdFiles = null;