const BEHAVIOR_ACCEPT = 0; const BEHAVIOR_REJECT_FOREIGN = 1; const BEHAVIOR_REJECT = 2; const BEHAVIOR_LIMIT_FOREIGN = 3; const kPrefName = "network.cookie.cookieBehavior"; // Check if we are in frame, and declare ok and finishTest appropriately const inFrame = ("" + location).match(/frame/); if (inFrame) { ok = function (a, message) { if (!a) { parent.postMessage("FAILURE: " + message, "http://mochi.test:8888"); } else { parent.postMessage(message, "http://mochi.test:8888"); } }; finishTest = function () { parent.postMessage("done", "http://mochi.test:8888"); }; } else { finishTest = function () { SimpleTest.finish(); }; } function setCookieBehavior(behavior) { return SpecialPowers.pushPrefEnv({ set: [[kPrefName, behavior]] }); } function runIFrame(url) { return new Promise((resolve, reject) => { function onMessage(e) { if (e.data == "done") { resolve(); window.removeEventListener("message", onMessage); return; } const isFail = e.data.match(/^FAILURE/); ok(!isFail, e.data + " (IFRAME = " + url + ")"); if (isFail) { reject(e); } } window.addEventListener("message", onMessage); document.querySelector("iframe").src = url; }); } function runWorker(url) { return new Promise((resolve, reject) => { var worker = new Worker(url); worker.addEventListener("message", function (e) { if (e.data == "done") { resolve(); return; } ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = " + url + ")"); }); worker.addEventListener("error", function (e) { ok(false, e.data + " (WORKER = " + url + ")"); reject(e); }); }); } function chromePower(allowed, blockSessionStorage) { // localStorage is affected by storage policy. try { SpecialPowers.wrap(window).localStorage.getItem("X"); ok(allowed, "getting localStorage from chrome didn't throw"); } catch (e) { ok(!allowed, "getting localStorage from chrome threw"); } // sessionStorage is not. See bug 1183968. try { SpecialPowers.wrap(window).sessionStorage.getItem("X"); ok(!blockSessionStorage, "getting sessionStorage from chrome didn't throw"); } catch (e) { ok(blockSessionStorage, "getting sessionStorage from chrome threw"); } // indexedDB is affected by storage policy. try { SpecialPowers.wrap(window).indexedDB; ok(allowed, "getting indexedDB from chrome didn't throw"); } catch (e) { ok(!allowed, "getting indexedDB from chrome threw"); } // Same with caches, along with the additional https-only requirement. try { var shouldResolve = allowed && location.protocol == "https:"; var promise = SpecialPowers.wrap(window).caches.keys(); ok(true, "getting caches from chrome should never throw"); return new Promise((resolve, reject) => { promise.then( function () { ok(shouldResolve, "The promise was resolved for chrome"); resolve(); }, function (e) { ok(!shouldResolve, "The promise was rejected for chrome: " + e); resolve(); } ); }); } catch (e) { ok(false, "getting caches from chrome threw"); return undefined; } } function storageAllowed() { try { localStorage.getItem("X"); ok(true, "getting localStorage didn't throw"); } catch (e) { ok(false, "getting localStorage should not throw"); } try { sessionStorage.getItem("X"); ok(true, "getting sessionStorage didn't throw"); } catch (e) { ok(false, "getting sessionStorage should not throw"); } try { indexedDB; ok(true, "getting indexedDB didn't throw"); } catch (e) { ok(false, "getting indexedDB should not throw"); } const dbName = "db"; try { var promise = caches.keys(); ok(true, "getting caches didn't throw"); return new Promise((resolve, reject) => { const checkCacheKeys = () => { promise.then( () => { ok(location.protocol == "https:", "The promise was not rejected"); resolve(); }, () => { ok( location.protocol !== "https:", "The promise should not have been rejected" ); resolve(); } ); }; const checkDeleteDbAndTheRest = dbs => { ok( dbs.some(elem => elem.name === dbName), "Expected database should be found" ); const end = indexedDB.deleteDatabase(dbName); end.onsuccess = checkCacheKeys; end.onerror = err => { ok(false, "querying indexedDB databases should not throw"); reject(err); }; }; const checkDatabasesAndTheRest = () => { indexedDB .databases() .then(checkDeleteDbAndTheRest) .catch(err => { ok(false, "deleting an indexedDB database should not throw"); reject(err); }); }; const begin = indexedDB.open(dbName); begin.onsuccess = checkDatabasesAndTheRest; begin.onerror = err => { ok(false, "opening an indexedDB database should not throw"); reject(err); }; }); } catch (e) { ok(location.protocol !== "https:", "getting caches should not have thrown"); return Promise.resolve(); } } function storagePrevented() { try { localStorage.getItem("X"); ok(false, "getting localStorage should have thrown"); } catch (e) { ok(true, "getting localStorage threw"); } if (location.hash == "#thirdparty") { // No matter what the user's preferences are, we don't block // sessionStorage in 3rd-party iframes. We do block them everywhere // else however. try { sessionStorage.getItem("X"); ok(true, "getting sessionStorage didn't throw"); } catch (e) { ok(false, "getting sessionStorage should not have thrown"); } } else { try { sessionStorage.getItem("X"); ok(false, "getting sessionStorage should have thrown"); } catch (e) { ok(true, "getting sessionStorage threw"); } } try { indexedDB; ok(true, "getting indexedDB didn't throw"); } catch (e) { ok(false, "getting indexedDB should not have thrown"); } const dbName = "album"; try { indexedDB.open(dbName); ok(false, "opening an indexedDB database didn't throw"); } catch (e) { ok(true, "opening an indexedDB database threw"); ok( e.name == "SecurityError", "opening indexedDB database emitted a security error" ); } // Note: Security error is expected to be thrown synchronously. indexedDB.databases().then( () => { ok(false, "querying indexedDB databases didn't reject"); }, e => { ok(true, "querying indexedDB databases rejected"); ok( e.name == "SecurityError", "querying indexedDB databases emitted a security error" ); } ); try { indexedDB.deleteDatabase(dbName); ok(false, "deleting an indexedDB database didn't throw"); } catch (e) { ok(true, "deleting an indexedDB database threw"); ok( e.name == "SecurityError", "deleting indexedDB database emitted a security error" ); } try { var promise = caches.keys(); ok(true, "getting caches didn't throw"); return new Promise((resolve, reject) => { promise.then( function () { ok(false, "The promise should have rejected"); resolve(); }, function () { ok(true, "The promise was rejected"); resolve(); } ); }); } catch (e) { ok(location.protocol !== "https:", "getting caches should not have thrown"); return Promise.resolve(); } } function task(fn) { if (!inFrame) { SimpleTest.waitForExplicitFinish(); } var gen = fn(); function next_step(val, e) { var it; try { if (typeof e !== "undefined") { it = gen.throw(e); } else { it = gen.next(val); } } catch (e) { ok(false, "An error was thrown while stepping: " + e); ok(false, "Stack: " + e.stack); finishTest(); } if (it.done) { finishTest(); return; } it.value.then(next_step, e => next_step(null, e)); } if (!gen.then) { next_step(); } else { gen.then(finishTest, e => { ok(false, "An error was thrown while stepping: " + e); ok(false, "Stack: " + e.stack); finishTest(); }); } } // The test will run on a separate window in order to apply the new cookie jar settings. async function runTestInWindow(test) { let w = window.open("window_storagePermissions.html"); await new Promise(resolve => { w.onload = e => { resolve(); }; }); return new Promise((resolve, reject) => { onmessage = e => { if (!e.data.type) { w.postMessage("FAILURE: " + e.data, document.referrer); ok(false, "No error data type"); reject(e); return; } if (e.data.type == "finish") { w.close(); resolve(); return; } if (e.data.type == "check") { const payload = e.data.msg ? e.data.msg : e.data; ok(e.data.test, payload); const isFail = payload.match(/^FAILURE/) || !e.data.test; if (isFail) { w.postMessage("FAILURE: " + e.data, document.referrer); ok(false, payload); w.close(); reject(e); } return; } ok(false, "Unknown message"); }; w.postMessage(test.toString(), "*"); }); } var thirdparty = "https://example.com/tests/dom/tests/mochitest/general/";