storagePermissionsUtils.js (9736B)
1 const BEHAVIOR_ACCEPT = 0; 2 const BEHAVIOR_REJECT_FOREIGN = 1; 3 const BEHAVIOR_REJECT = 2; 4 const BEHAVIOR_LIMIT_FOREIGN = 3; 5 6 const kPrefName = "network.cookie.cookieBehavior"; 7 8 // Check if we are in frame, and declare ok and finishTest appropriately 9 const inFrame = ("" + location).match(/frame/); 10 if (inFrame) { 11 ok = function (a, message) { 12 if (!a) { 13 parent.postMessage("FAILURE: " + message, "http://mochi.test:8888"); 14 } else { 15 parent.postMessage(message, "http://mochi.test:8888"); 16 } 17 }; 18 19 finishTest = function () { 20 parent.postMessage("done", "http://mochi.test:8888"); 21 }; 22 } else { 23 finishTest = function () { 24 SimpleTest.finish(); 25 }; 26 } 27 28 function setCookieBehavior(behavior) { 29 return SpecialPowers.pushPrefEnv({ set: [[kPrefName, behavior]] }); 30 } 31 32 function runIFrame(url) { 33 return new Promise((resolve, reject) => { 34 function onMessage(e) { 35 if (e.data == "done") { 36 resolve(); 37 window.removeEventListener("message", onMessage); 38 return; 39 } 40 41 const isFail = e.data.match(/^FAILURE/); 42 ok(!isFail, e.data + " (IFRAME = " + url + ")"); 43 if (isFail) { 44 reject(e); 45 } 46 } 47 window.addEventListener("message", onMessage); 48 49 document.querySelector("iframe").src = url; 50 }); 51 } 52 53 function runWorker(url) { 54 return new Promise((resolve, reject) => { 55 var worker = new Worker(url); 56 worker.addEventListener("message", function (e) { 57 if (e.data == "done") { 58 resolve(); 59 return; 60 } 61 62 ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = " + url + ")"); 63 }); 64 65 worker.addEventListener("error", function (e) { 66 ok(false, e.data + " (WORKER = " + url + ")"); 67 reject(e); 68 }); 69 }); 70 } 71 72 function chromePower(allowed, blockSessionStorage) { 73 // localStorage is affected by storage policy. 74 try { 75 SpecialPowers.wrap(window).localStorage.getItem("X"); 76 ok(allowed, "getting localStorage from chrome didn't throw"); 77 } catch (e) { 78 ok(!allowed, "getting localStorage from chrome threw"); 79 } 80 81 // sessionStorage is not. See bug 1183968. 82 try { 83 SpecialPowers.wrap(window).sessionStorage.getItem("X"); 84 ok(!blockSessionStorage, "getting sessionStorage from chrome didn't throw"); 85 } catch (e) { 86 ok(blockSessionStorage, "getting sessionStorage from chrome threw"); 87 } 88 89 // indexedDB is affected by storage policy. 90 try { 91 SpecialPowers.wrap(window).indexedDB; 92 ok(allowed, "getting indexedDB from chrome didn't throw"); 93 } catch (e) { 94 ok(!allowed, "getting indexedDB from chrome threw"); 95 } 96 97 // Same with caches, along with the additional https-only requirement. 98 try { 99 var shouldResolve = allowed && location.protocol == "https:"; 100 var promise = SpecialPowers.wrap(window).caches.keys(); 101 ok(true, "getting caches from chrome should never throw"); 102 return new Promise((resolve, reject) => { 103 promise.then( 104 function () { 105 ok(shouldResolve, "The promise was resolved for chrome"); 106 resolve(); 107 }, 108 function (e) { 109 ok(!shouldResolve, "The promise was rejected for chrome: " + e); 110 resolve(); 111 } 112 ); 113 }); 114 } catch (e) { 115 ok(false, "getting caches from chrome threw"); 116 return undefined; 117 } 118 } 119 120 function storageAllowed() { 121 try { 122 localStorage.getItem("X"); 123 ok(true, "getting localStorage didn't throw"); 124 } catch (e) { 125 ok(false, "getting localStorage should not throw"); 126 } 127 128 try { 129 sessionStorage.getItem("X"); 130 ok(true, "getting sessionStorage didn't throw"); 131 } catch (e) { 132 ok(false, "getting sessionStorage should not throw"); 133 } 134 135 try { 136 indexedDB; 137 ok(true, "getting indexedDB didn't throw"); 138 } catch (e) { 139 ok(false, "getting indexedDB should not throw"); 140 } 141 142 const dbName = "db"; 143 144 try { 145 var promise = caches.keys(); 146 ok(true, "getting caches didn't throw"); 147 148 return new Promise((resolve, reject) => { 149 const checkCacheKeys = () => { 150 promise.then( 151 () => { 152 ok(location.protocol == "https:", "The promise was not rejected"); 153 resolve(); 154 }, 155 () => { 156 ok( 157 location.protocol !== "https:", 158 "The promise should not have been rejected" 159 ); 160 resolve(); 161 } 162 ); 163 }; 164 165 const checkDeleteDbAndTheRest = dbs => { 166 ok( 167 dbs.some(elem => elem.name === dbName), 168 "Expected database should be found" 169 ); 170 171 const end = indexedDB.deleteDatabase(dbName); 172 end.onsuccess = checkCacheKeys; 173 end.onerror = err => { 174 ok(false, "querying indexedDB databases should not throw"); 175 reject(err); 176 }; 177 }; 178 179 const checkDatabasesAndTheRest = () => { 180 indexedDB 181 .databases() 182 .then(checkDeleteDbAndTheRest) 183 .catch(err => { 184 ok(false, "deleting an indexedDB database should not throw"); 185 reject(err); 186 }); 187 }; 188 189 const begin = indexedDB.open(dbName); 190 begin.onsuccess = checkDatabasesAndTheRest; 191 begin.onerror = err => { 192 ok(false, "opening an indexedDB database should not throw"); 193 reject(err); 194 }; 195 }); 196 } catch (e) { 197 ok(location.protocol !== "https:", "getting caches should not have thrown"); 198 return Promise.resolve(); 199 } 200 } 201 202 function storagePrevented() { 203 try { 204 localStorage.getItem("X"); 205 ok(false, "getting localStorage should have thrown"); 206 } catch (e) { 207 ok(true, "getting localStorage threw"); 208 } 209 210 if (location.hash == "#thirdparty") { 211 // No matter what the user's preferences are, we don't block 212 // sessionStorage in 3rd-party iframes. We do block them everywhere 213 // else however. 214 try { 215 sessionStorage.getItem("X"); 216 ok(true, "getting sessionStorage didn't throw"); 217 } catch (e) { 218 ok(false, "getting sessionStorage should not have thrown"); 219 } 220 } else { 221 try { 222 sessionStorage.getItem("X"); 223 ok(false, "getting sessionStorage should have thrown"); 224 } catch (e) { 225 ok(true, "getting sessionStorage threw"); 226 } 227 } 228 229 try { 230 indexedDB; 231 ok(true, "getting indexedDB didn't throw"); 232 } catch (e) { 233 ok(false, "getting indexedDB should not have thrown"); 234 } 235 236 const dbName = "album"; 237 238 try { 239 indexedDB.open(dbName); 240 ok(false, "opening an indexedDB database didn't throw"); 241 } catch (e) { 242 ok(true, "opening an indexedDB database threw"); 243 ok( 244 e.name == "SecurityError", 245 "opening indexedDB database emitted a security error" 246 ); 247 } 248 249 // Note: Security error is expected to be thrown synchronously. 250 indexedDB.databases().then( 251 () => { 252 ok(false, "querying indexedDB databases didn't reject"); 253 }, 254 e => { 255 ok(true, "querying indexedDB databases rejected"); 256 ok( 257 e.name == "SecurityError", 258 "querying indexedDB databases emitted a security error" 259 ); 260 } 261 ); 262 263 try { 264 indexedDB.deleteDatabase(dbName); 265 ok(false, "deleting an indexedDB database didn't throw"); 266 } catch (e) { 267 ok(true, "deleting an indexedDB database threw"); 268 ok( 269 e.name == "SecurityError", 270 "deleting indexedDB database emitted a security error" 271 ); 272 } 273 274 try { 275 var promise = caches.keys(); 276 ok(true, "getting caches didn't throw"); 277 278 return new Promise((resolve, reject) => { 279 promise.then( 280 function () { 281 ok(false, "The promise should have rejected"); 282 resolve(); 283 }, 284 function () { 285 ok(true, "The promise was rejected"); 286 resolve(); 287 } 288 ); 289 }); 290 } catch (e) { 291 ok(location.protocol !== "https:", "getting caches should not have thrown"); 292 293 return Promise.resolve(); 294 } 295 } 296 297 function task(fn) { 298 if (!inFrame) { 299 SimpleTest.waitForExplicitFinish(); 300 } 301 302 var gen = fn(); 303 304 function next_step(val, e) { 305 var it; 306 try { 307 if (typeof e !== "undefined") { 308 it = gen.throw(e); 309 } else { 310 it = gen.next(val); 311 } 312 } catch (e) { 313 ok(false, "An error was thrown while stepping: " + e); 314 ok(false, "Stack: " + e.stack); 315 finishTest(); 316 } 317 318 if (it.done) { 319 finishTest(); 320 return; 321 } 322 it.value.then(next_step, e => next_step(null, e)); 323 } 324 325 if (!gen.then) { 326 next_step(); 327 } else { 328 gen.then(finishTest, e => { 329 ok(false, "An error was thrown while stepping: " + e); 330 ok(false, "Stack: " + e.stack); 331 finishTest(); 332 }); 333 } 334 } 335 336 // The test will run on a separate window in order to apply the new cookie jar settings. 337 async function runTestInWindow(test) { 338 let w = window.open("window_storagePermissions.html"); 339 await new Promise(resolve => { 340 w.onload = e => { 341 resolve(); 342 }; 343 }); 344 345 return new Promise((resolve, reject) => { 346 onmessage = e => { 347 if (!e.data.type) { 348 w.postMessage("FAILURE: " + e.data, document.referrer); 349 ok(false, "No error data type"); 350 reject(e); 351 return; 352 } 353 354 if (e.data.type == "finish") { 355 w.close(); 356 resolve(); 357 return; 358 } 359 360 if (e.data.type == "check") { 361 const payload = e.data.msg ? e.data.msg : e.data; 362 ok(e.data.test, payload); 363 const isFail = payload.match(/^FAILURE/) || !e.data.test; 364 if (isFail) { 365 w.postMessage("FAILURE: " + e.data, document.referrer); 366 ok(false, payload); 367 w.close(); 368 reject(e); 369 } 370 return; 371 } 372 373 ok(false, "Unknown message"); 374 }; 375 376 w.postMessage(test.toString(), "*"); 377 }); 378 } 379 380 var thirdparty = "https://example.com/tests/dom/tests/mochitest/general/";