head.js (18025B)
1 /** 2 * Any copyright is dedicated to the Public Domain. 3 * http://creativecommons.org/publicdomain/zero/1.0/ 4 */ 5 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; 10 11 const loggingEnabled = false; 12 13 var testGenerator; 14 15 loadScript("dom/quota/test/common/xpcshell.js"); 16 17 function log(msg) { 18 if (loggingEnabled) { 19 info(msg); 20 } 21 } 22 23 function is(a, b, msg) { 24 Assert.equal(a, b, msg); 25 } 26 27 function ok(cond, msg) { 28 Assert.ok(!!cond, msg); 29 } 30 31 function todo(cond, msg) { 32 todo_check_true(cond); 33 } 34 35 function run_test() { 36 runTest(); 37 } 38 39 if (!this.runTest) { 40 this.runTest = function () { 41 do_get_profile(); 42 43 enableStorageTesting(); 44 enableTesting(); 45 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. 49 Assert.strictEqual( 50 typeof testSteps, 51 "function", 52 "There should be a testSteps function" 53 ); 54 if (testSteps.constructor.name === "AsyncFunction") { 55 // Do run our existing cleanup function that would normally be called by 56 // the generator's call to finishTest(). 57 registerCleanupFunction(function () { 58 resetStorageTesting(); 59 resetTesting(); 60 }); 61 62 add_task(testSteps); 63 64 // Since we defined run_test, we must invoke run_next_test() to start the 65 // async test. 66 run_next_test(); 67 } else { 68 Assert.strictEqual( 69 testSteps.constructor.name, 70 "GeneratorFunction", 71 "Unsupported function type" 72 ); 73 74 do_test_pending(); 75 76 testGenerator = testSteps(); 77 testGenerator.next(); 78 } 79 }; 80 } 81 82 function finishTest() { 83 resetStorageTesting(); 84 resetTesting(); 85 86 executeSoon(function () { 87 do_test_finished(); 88 }); 89 } 90 91 function grabArgAndContinueHandler(arg) { 92 testGenerator.next(arg); 93 } 94 95 function continueToNextStep() { 96 executeSoon(function () { 97 testGenerator.next(); 98 }); 99 } 100 101 function continueToNextStepSync() { 102 testGenerator.next(); 103 } 104 105 function enableTesting() { 106 SpecialPowers.setBoolPref( 107 "dom.storage.enable_unsupported_legacy_implementation", 108 false 109 ); 110 } 111 112 function resetTesting() { 113 SpecialPowers.clearUserPref( 114 "dom.storage.enable_unsupported_legacy_implementation" 115 ); 116 } 117 118 function setGlobalLimit(globalLimit) { 119 SpecialPowers.setIntPref( 120 "dom.quotaManager.temporaryStorage.fixedLimit", 121 globalLimit 122 ); 123 } 124 125 function resetGlobalLimit() { 126 SpecialPowers.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit"); 127 } 128 129 function storageInitialized(callback) { 130 let request = SpecialPowers._getQuotaManager().storageInitialized(); 131 request.callback = callback; 132 133 return request; 134 } 135 136 function persistentStorageInitialized(callback) { 137 let request = SpecialPowers._getQuotaManager().persistentStorageInitialized(); 138 request.callback = callback; 139 140 return request; 141 } 142 143 function temporaryStorageInitialized(callback) { 144 let request = SpecialPowers._getQuotaManager().temporaryStorageInitialized(); 145 request.callback = callback; 146 147 return request; 148 } 149 150 function persistentOriginInitialized(principal, callback) { 151 let request = 152 SpecialPowers._getQuotaManager().persistentOriginInitialized(principal); 153 request.callback = callback; 154 155 return request; 156 } 157 158 function temporaryOriginInitialized(persistence, principal, callback) { 159 let request = SpecialPowers._getQuotaManager().temporaryOriginInitialized( 160 persistence, 161 principal 162 ); 163 request.callback = callback; 164 165 return request; 166 } 167 168 function init(callback) { 169 let request = SpecialPowers._getQuotaManager().init(); 170 request.callback = callback; 171 172 return request; 173 } 174 175 function initializePersistentStorage(callback) { 176 let request = SpecialPowers._getQuotaManager().initializePersistentStorage(); 177 request.callback = callback; 178 179 return request; 180 } 181 182 function initTemporaryStorage(callback) { 183 let request = SpecialPowers._getQuotaManager().initTemporaryStorage(); 184 request.callback = callback; 185 186 return request; 187 } 188 189 function initPersistentOrigin(principal, callback) { 190 let request = 191 SpecialPowers._getQuotaManager().initializePersistentOrigin(principal); 192 request.callback = callback; 193 194 return request; 195 } 196 197 function initTemporaryOrigin( 198 persistence, 199 principal, 200 createIfNonExistent = true, 201 callback 202 ) { 203 let request = SpecialPowers._getQuotaManager().initializeTemporaryOrigin( 204 persistence, 205 principal, 206 createIfNonExistent 207 ); 208 request.callback = callback; 209 210 return request; 211 } 212 213 function initPersistentClient(principal, client, callback) { 214 let request = SpecialPowers._getQuotaManager().initializePersistentClient( 215 principal, 216 client 217 ); 218 request.callback = callback; 219 220 return request; 221 } 222 223 function initTemporaryClient( 224 persistence, 225 principal, 226 client, 227 createIfNonExistent = true, 228 callback 229 ) { 230 let request = SpecialPowers._getQuotaManager().initializeTemporaryClient( 231 persistence, 232 principal, 233 client, 234 createIfNonExistent 235 ); 236 request.callback = callback; 237 238 return request; 239 } 240 241 function getFullOriginMetadata(persistence, principal, callback) { 242 const request = SpecialPowers._getQuotaManager().getFullOriginMetadata( 243 persistence, 244 principal 245 ); 246 request.callback = callback; 247 248 return request; 249 } 250 251 function clearClient(principal, client, persistence, callback) { 252 let request = SpecialPowers._getQuotaManager().clearStoragesForClient( 253 principal, 254 client, 255 persistence 256 ); 257 request.callback = callback; 258 259 return request; 260 } 261 262 function clearOrigin(principal, persistence, callback) { 263 let request = SpecialPowers._getQuotaManager().clearStoragesForPrincipal( 264 principal, 265 persistence 266 ); 267 request.callback = callback; 268 269 return request; 270 } 271 272 function clearOriginsByPrefix(principal, persistence, callback) { 273 let request = SpecialPowers._getQuotaManager().clearStoragesForOriginPrefix( 274 principal, 275 persistence 276 ); 277 request.callback = callback; 278 279 return request; 280 } 281 282 function clearPrivateBrowsing(callback) { 283 let request = 284 SpecialPowers._getQuotaManager().clearStoragesForPrivateBrowsing(); 285 request.callback = callback; 286 287 return request; 288 } 289 290 function resetClient(principal, client) { 291 let request = Services.qms.resetStoragesForClient( 292 principal, 293 client, 294 "default" 295 ); 296 297 return request; 298 } 299 300 function persist(principal, callback) { 301 let request = SpecialPowers._getQuotaManager().persist(principal); 302 request.callback = callback; 303 304 return request; 305 } 306 307 function persisted(principal, callback) { 308 let request = SpecialPowers._getQuotaManager().persisted(principal); 309 request.callback = callback; 310 311 return request; 312 } 313 314 function estimateOrigin(principal, callback) { 315 let request = SpecialPowers._getQuotaManager().estimate(principal); 316 request.callback = callback; 317 318 return request; 319 } 320 321 function listOrigins(callback) { 322 let request = SpecialPowers._getQuotaManager().listOrigins(callback); 323 request.callback = callback; 324 325 return request; 326 } 327 328 function getPersistedFromMetadata(readBuffer) { 329 const persistedPosition = 8; // Persisted state is stored in the 9th byte 330 let view = 331 readBuffer instanceof Uint8Array ? readBuffer : new Uint8Array(readBuffer); 332 333 return !!view[persistedPosition]; 334 } 335 336 function grabResultAndContinueHandler(request) { 337 testGenerator.next(request.result); 338 } 339 340 function grabUsageAndContinueHandler(request) { 341 testGenerator.next(request.result.usage); 342 } 343 344 function getUsage(usageHandler, getAll) { 345 let request = SpecialPowers._getQuotaManager().getUsage(usageHandler, getAll); 346 347 return request; 348 } 349 350 function getOriginUsage(principal) { 351 let request = Services.qms.getUsageForPrincipal(principal, function () {}); 352 353 return request; 354 } 355 356 function getCachedOriginUsage(principal) { 357 let request = Services.qms.getCachedUsageForPrincipal( 358 principal, 359 function () {} 360 ); 361 362 return request; 363 } 364 365 function getCachedOriginUsage(principal) { 366 let request = Services.qms.getCachedUsageForPrincipal(principal); 367 368 return request; 369 } 370 371 function getCurrentUsage(usageHandler) { 372 let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance( 373 Ci.nsIPrincipal 374 ); 375 let request = SpecialPowers._getQuotaManager().getUsageForPrincipal( 376 principal, 377 usageHandler 378 ); 379 380 return request; 381 } 382 383 function getPrincipal(url, attr = {}) { 384 let uri = Cc["@mozilla.org/network/io-service;1"] 385 .getService(Ci.nsIIOService) 386 .newURI(url); 387 let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService( 388 Ci.nsIScriptSecurityManager 389 ); 390 return ssm.createContentPrincipal(uri, attr); 391 } 392 393 var SpecialPowers = { 394 getBoolPref(prefName) { 395 return this._getPrefs().getBoolPref(prefName); 396 }, 397 398 setBoolPref(prefName, value) { 399 this._getPrefs().setBoolPref(prefName, value); 400 }, 401 402 setIntPref(prefName, value) { 403 this._getPrefs().setIntPref(prefName, value); 404 }, 405 406 clearUserPref(prefName) { 407 this._getPrefs().clearUserPref(prefName); 408 }, 409 410 _getPrefs() { 411 let prefService = Cc["@mozilla.org/preferences-service;1"].getService( 412 Ci.nsIPrefService 413 ); 414 return prefService.getBranch(null); 415 }, 416 417 _getQuotaManager() { 418 return Cc["@mozilla.org/dom/quota-manager-service;1"].getService( 419 Ci.nsIQuotaManagerService 420 ); 421 }, 422 }; 423 424 function installPackages(packageRelativePaths) { 425 if (packageRelativePaths.length != 2) { 426 throw new Error("Unsupported number of package relative paths"); 427 } 428 429 for (const packageRelativePath of packageRelativePaths) { 430 installPackage(packageRelativePath); 431 } 432 } 433 434 // Take current storage structure on disk and compare it with the expected 435 // structure. The expected structure is defined in JSON and consists of a per 436 // test package definition and a shared package definition. The shared package 437 // definition should contain unknown stuff which needs to be properly handled 438 // in all situations. 439 function verifyStorage(packageDefinitionRelativePaths, key, sharedKey) { 440 if (packageDefinitionRelativePaths.length != 2) { 441 throw new Error("Unsupported number of package definition relative paths"); 442 } 443 444 function verifyEntries(entries, name, indent = "") { 445 log(`${indent}Verifying ${name} entries`); 446 447 indent += " "; 448 449 for (const entry of entries) { 450 const maybeName = entry.name; 451 452 log(`${indent}Verifying entry ${maybeName}`); 453 454 let hasName = false; 455 let hasDir = false; 456 let hasEntries = false; 457 458 for (const property in entry) { 459 switch (property) { 460 case "note": 461 case "todo": 462 break; 463 464 case "name": 465 hasName = true; 466 break; 467 468 case "dir": 469 hasDir = true; 470 break; 471 472 case "entries": 473 hasEntries = true; 474 break; 475 476 default: 477 throw new Error(`Unknown property ${property}`); 478 } 479 } 480 481 if (!hasName) { 482 throw new Error("An entry must have the name property"); 483 } 484 485 if (!hasDir) { 486 throw new Error("An entry must have the dir property"); 487 } 488 489 if (hasEntries && !entry.dir) { 490 throw new Error("An entry can't have entries if it's not a directory"); 491 } 492 493 if (hasEntries) { 494 verifyEntries(entry.entries, entry.name, indent); 495 } 496 } 497 } 498 499 function getCurrentEntries() { 500 log("Getting current entries"); 501 502 function getEntryForFile(file) { 503 let entry = { 504 name: file.leafName, 505 dir: file.isDirectory(), 506 }; 507 508 if (file.isDirectory()) { 509 const enumerator = file.directoryEntries; 510 let nextFile; 511 while ((nextFile = enumerator.nextFile)) { 512 if (!entry.entries) { 513 entry.entries = []; 514 } 515 entry.entries.push(getEntryForFile(nextFile)); 516 } 517 } 518 519 return entry; 520 } 521 522 let entries = []; 523 524 let file = getRelativeFile("indexedDB"); 525 if (file.exists()) { 526 entries.push(getEntryForFile(file)); 527 } 528 529 file = getRelativeFile("storage"); 530 if (file.exists()) { 531 entries.push(getEntryForFile(file)); 532 } 533 534 file = getRelativeFile("storage.sqlite"); 535 if (file.exists()) { 536 entries.push(getEntryForFile(file)); 537 } 538 539 verifyEntries(entries, "current"); 540 541 return entries; 542 } 543 544 function getEntriesFromPackageDefinition( 545 packageDefinitionRelativePath, 546 lookupKey 547 ) { 548 log(`Getting ${lookupKey} entries from ${packageDefinitionRelativePath}`); 549 550 const currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile); 551 const file = getRelativeFile( 552 packageDefinitionRelativePath + ".json", 553 currentDir 554 ); 555 556 const fileInputStream = Cc[ 557 "@mozilla.org/network/file-input-stream;1" 558 ].createInstance(Ci.nsIFileInputStream); 559 fileInputStream.init(file, -1, -1, 0); 560 561 const scriptableInputStream = Cc[ 562 "@mozilla.org/scriptableinputstream;1" 563 ].createInstance(Ci.nsIScriptableInputStream); 564 scriptableInputStream.init(fileInputStream); 565 566 const data = scriptableInputStream.readBytes( 567 scriptableInputStream.available() 568 ); 569 570 const obj = JSON.parse(data); 571 572 const result = obj.find(({ key: elementKey }) => elementKey == lookupKey); 573 574 if (!result) { 575 throw new Error("The file doesn't contain an element for given key"); 576 } 577 578 if (!result.entries) { 579 throw new Error("The element doesn't have the entries property"); 580 } 581 582 verifyEntries(result.entries, lookupKey); 583 584 return result.entries; 585 } 586 587 function addSharedEntries(expectedEntries, sharedEntries, name, indent = "") { 588 log(`${indent}Checking common ${name} entries`); 589 590 indent += " "; 591 592 for (const sharedEntry of sharedEntries) { 593 const expectedEntry = expectedEntries.find( 594 ({ name: elementName }) => elementName == sharedEntry.name 595 ); 596 597 if (expectedEntry) { 598 log(`${indent}Checking common entry ${sharedEntry.name}`); 599 600 if (!expectedEntry.dir || !sharedEntry.dir) { 601 throw new Error("A common entry must be a directory"); 602 } 603 604 if (!expectedEntry.entries && !sharedEntry.entries) { 605 throw new Error("A common entry must not be a leaf"); 606 } 607 608 if (sharedEntry.entries) { 609 if (!expectedEntry.entries) { 610 expectedEntry.entries = []; 611 } 612 613 addSharedEntries( 614 expectedEntry.entries, 615 sharedEntry.entries, 616 sharedEntry.name, 617 indent 618 ); 619 } 620 } else { 621 log(`${indent}Adding entry ${sharedEntry.name}`); 622 expectedEntries.push(sharedEntry); 623 } 624 } 625 } 626 627 function compareEntries(currentEntries, expectedEntries, name, indent = "") { 628 log(`${indent}Comparing ${name} entries`); 629 630 indent += " "; 631 632 if (currentEntries.length != expectedEntries.length) { 633 throw new Error("Entries must have the same length"); 634 } 635 636 for (const currentEntry of currentEntries) { 637 log(`${indent}Comparing entry ${currentEntry.name}`); 638 639 const expectedEntry = expectedEntries.find( 640 ({ name: elementName }) => elementName == currentEntry.name 641 ); 642 643 if (!expectedEntry) { 644 throw new Error("Cannot find a matching entry"); 645 } 646 647 if (expectedEntry.dir != currentEntry.dir) { 648 throw new Error("The dir property doesn't match"); 649 } 650 651 if ( 652 (expectedEntry.entries && !currentEntry.entries) || 653 (!expectedEntry.entries && currentEntry.entries) 654 ) { 655 throw new Error("The entries property doesn't match"); 656 } 657 658 if (expectedEntry.entries) { 659 compareEntries( 660 currentEntry.entries, 661 expectedEntry.entries, 662 currentEntry.name, 663 indent 664 ); 665 } 666 } 667 } 668 669 const currentEntries = getCurrentEntries(); 670 671 log("Stringified current entries: " + JSON.stringify(currentEntries)); 672 673 const expectedEntries = getEntriesFromPackageDefinition( 674 packageDefinitionRelativePaths[0], 675 key 676 ); 677 const sharedEntries = getEntriesFromPackageDefinition( 678 packageDefinitionRelativePaths[1], 679 sharedKey ? sharedKey : key 680 ); 681 682 addSharedEntries(expectedEntries, sharedEntries, key); 683 684 log("Stringified expected entries: " + JSON.stringify(expectedEntries)); 685 686 compareEntries(currentEntries, expectedEntries, key); 687 } 688 689 async function verifyInitializationStatus( 690 expectStorageIsInitialized, 691 expectPersistentStorageIsInitialized, 692 expectTemporaryStorageIsInitialized 693 ) { 694 if (!expectStorageIsInitialized && expectPersistentStorageIsInitialized) { 695 throw new Error("Invalid expectation"); 696 } 697 698 if (!expectStorageIsInitialized && expectTemporaryStorageIsInitialized) { 699 throw new Error("Invalid expectation"); 700 } 701 702 let request = storageInitialized(); 703 await requestFinished(request); 704 705 const storageIsInitialized = request.result; 706 707 request = persistentStorageInitialized(); 708 await requestFinished(request); 709 710 const persistentStorageIsInitialized = request.result; 711 712 request = temporaryStorageInitialized(); 713 await requestFinished(request); 714 715 const temporaryStorageIsInitialized = request.result; 716 717 ok( 718 !(!storageIsInitialized && persistentStorageIsInitialized), 719 "Initialization status is consistent" 720 ); 721 722 ok( 723 !(!storageIsInitialized && temporaryStorageIsInitialized), 724 "Initialization status is consistent" 725 ); 726 727 if (expectStorageIsInitialized) { 728 ok(storageIsInitialized, "Storage is initialized"); 729 } else { 730 ok(!storageIsInitialized, "Storage is not initialized"); 731 } 732 733 if (expectPersistentStorageIsInitialized) { 734 ok(persistentStorageIsInitialized, "Persistent storage is initialized"); 735 } else { 736 ok( 737 !persistentStorageIsInitialized, 738 "Persistent storage is not initialized" 739 ); 740 } 741 742 if (expectTemporaryStorageIsInitialized) { 743 ok(temporaryStorageIsInitialized, "Temporary storage is initialized"); 744 } else { 745 ok(!temporaryStorageIsInitialized, "Temporary storage is not initialized"); 746 } 747 }