Source code
Revision control
Copy as Markdown
Other Tools
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) {
} else {
}
};
finishTest = function () {
};
} 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");
}
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(), "*");
});
}