browser_sanitizeDialog.js (23034B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /** 8 * Tests the sanitize dialog (a.k.a. the clear recent history dialog). 9 * See bug 480169. 10 * 11 * The purpose of this test is not to fully flex the sanitize timespan code; 12 * browser/base/content/test/sanitize/browser_sanitize-timespans.js does that. This 13 * test checks the UI of the dialog and makes sure it's correctly connected to 14 * the sanitize timespan code. 15 * 16 * Some of this code, especially the history creation parts, was taken from 17 * browser/base/content/test/sanitize/browser_sanitize-timespans.js. 18 */ 19 20 ChromeUtils.defineESModuleGetters(this, { 21 PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", 22 Timer: "resource://gre/modules/Timer.sys.mjs", 23 }); 24 25 /** 26 * Ensures that the specified URIs are either cleared or not. 27 * 28 * @param aURIs 29 * Array of page URIs 30 * @param aShouldBeCleared 31 * True if each visit to the URI should be cleared, false otherwise 32 */ 33 async function promiseHistoryClearedState(aURIs, aShouldBeCleared) { 34 for (let uri of aURIs) { 35 let visited = await PlacesUtils.history.hasVisits(uri); 36 Assert.equal( 37 visited, 38 !aShouldBeCleared, 39 `history visit ${uri.spec} should ${ 40 aShouldBeCleared ? "no longer" : "still" 41 } exist` 42 ); 43 } 44 } 45 46 add_setup(async function () { 47 requestLongerTimeout(3); 48 await blankSlate(); 49 registerCleanupFunction(async function () { 50 await blankSlate(); 51 await PlacesTestUtils.promiseAsyncUpdates(); 52 }); 53 54 await SpecialPowers.pushPrefEnv({ 55 set: [["privacy.sanitize.useOldClearHistoryDialog", true]], 56 }); 57 }); 58 59 /** 60 * Initializes the dialog to its default state. 61 */ 62 add_task(async function default_state() { 63 let dh = new DialogHelper(); 64 dh.onload = function () { 65 // Select "Last Hour" 66 this.selectDuration(Sanitizer.TIMESPAN_HOUR); 67 this.acceptDialog(); 68 }; 69 dh.open(); 70 await dh.promiseClosed; 71 }); 72 73 /** 74 * Cancels the dialog, makes sure history not cleared. 75 */ 76 add_task(async function test_cancel() { 77 // Add history (within the past hour) 78 let uris = []; 79 let places = []; 80 let pURI; 81 for (let i = 0; i < 30; i++) { 82 pURI = makeURI("https://" + i + "-minutes-ago.com/"); 83 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(i) }); 84 uris.push(pURI); 85 } 86 await PlacesTestUtils.addVisits(places); 87 88 let dh = new DialogHelper(); 89 dh.onload = function () { 90 this.selectDuration(Sanitizer.TIMESPAN_HOUR); 91 this.checkPrefCheckbox("history", false); 92 this.cancelDialog(); 93 }; 94 dh.onunload = async function () { 95 await promiseHistoryClearedState(uris, false); 96 await blankSlate(); 97 await promiseHistoryClearedState(uris, true); 98 }; 99 dh.open(); 100 await dh.promiseClosed; 101 }); 102 103 /** 104 * Ensures that the combined history-downloads checkbox clears both history 105 * visits and downloads when checked; the dialog respects simple timespan. 106 */ 107 add_task(async function test_history_downloads_checked() { 108 // Add downloads (within the past hour). 109 let downloadIDs = []; 110 for (let i = 0; i < 5; i++) { 111 await addDownloadWithMinutesAgo(downloadIDs, i); 112 } 113 // Add downloads (over an hour ago). 114 let olderDownloadIDs = []; 115 for (let i = 0; i < 5; i++) { 116 await addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); 117 } 118 119 // Add history (within the past hour). 120 let uris = []; 121 let places = []; 122 let pURI; 123 for (let i = 0; i < 30; i++) { 124 pURI = makeURI("https://" + i + "-minutes-ago.com/"); 125 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(i) }); 126 uris.push(pURI); 127 } 128 // Add history (over an hour ago). 129 let olderURIs = []; 130 for (let i = 0; i < 5; i++) { 131 pURI = makeURI("https://" + (61 + i) + "-minutes-ago.com/"); 132 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i) }); 133 olderURIs.push(pURI); 134 } 135 let promiseSanitized = promiseSanitizationComplete(); 136 137 await PlacesTestUtils.addVisits(places); 138 139 let dh = new DialogHelper(); 140 dh.onload = function () { 141 this.selectDuration(Sanitizer.TIMESPAN_HOUR); 142 this.checkPrefCheckbox("history", true); 143 this.acceptDialog(); 144 }; 145 dh.onunload = async function () { 146 intPrefIs( 147 "sanitize.timeSpan", 148 Sanitizer.TIMESPAN_HOUR, 149 "timeSpan pref should be hour after accepting dialog with " + 150 "hour selected" 151 ); 152 boolPrefIs( 153 "cpd.history", 154 true, 155 "history pref should be true after accepting dialog with " + 156 "history checkbox checked" 157 ); 158 boolPrefIs( 159 "cpd.downloads", 160 true, 161 "downloads pref should be true after accepting dialog with " + 162 "history checkbox checked" 163 ); 164 165 await promiseSanitized; 166 167 // History visits and downloads within one hour should be cleared. 168 await promiseHistoryClearedState(uris, true); 169 await ensureDownloadsClearedState(downloadIDs, true); 170 171 // Visits and downloads > 1 hour should still exist. 172 await promiseHistoryClearedState(olderURIs, false); 173 await ensureDownloadsClearedState(olderDownloadIDs, false); 174 175 // OK, done, cleanup after ourselves. 176 await blankSlate(); 177 await promiseHistoryClearedState(olderURIs, true); 178 await ensureDownloadsClearedState(olderDownloadIDs, true); 179 }; 180 dh.open(); 181 await dh.promiseClosed; 182 }); 183 184 /** 185 * Ensures that the combined history-downloads checkbox removes neither 186 * history visits nor downloads when not checked. 187 */ 188 add_task(async function test_history_downloads_unchecked() { 189 // Add form entries 190 let formEntries = []; 191 192 for (let i = 0; i < 5; i++) { 193 formEntries.push(await promiseAddFormEntryWithMinutesAgo(i)); 194 } 195 196 // Add downloads (within the past hour). 197 let downloadIDs = []; 198 for (let i = 0; i < 5; i++) { 199 await addDownloadWithMinutesAgo(downloadIDs, i); 200 } 201 202 // Add history, downloads, form entries (within the past hour). 203 let uris = []; 204 let places = []; 205 let pURI; 206 for (let i = 0; i < 5; i++) { 207 pURI = makeURI("https://" + i + "-minutes-ago.com/"); 208 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(i) }); 209 uris.push(pURI); 210 } 211 212 await PlacesTestUtils.addVisits(places); 213 let dh = new DialogHelper(); 214 dh.onload = function () { 215 is( 216 this.isWarningPanelVisible(), 217 false, 218 "Warning panel should be hidden after previously accepting dialog " + 219 "with a predefined timespan" 220 ); 221 this.selectDuration(Sanitizer.TIMESPAN_HOUR); 222 223 // Remove only form entries, leave history (including downloads). 224 this.checkPrefCheckbox("history", false); 225 this.checkPrefCheckbox("formdata", true); 226 this.acceptDialog(); 227 }; 228 dh.onunload = async function () { 229 intPrefIs( 230 "sanitize.timeSpan", 231 Sanitizer.TIMESPAN_HOUR, 232 "timeSpan pref should be hour after accepting dialog with " + 233 "hour selected" 234 ); 235 boolPrefIs( 236 "cpd.history", 237 false, 238 "history pref should be false after accepting dialog with " + 239 "history checkbox unchecked" 240 ); 241 boolPrefIs( 242 "cpd.downloads", 243 false, 244 "downloads pref should be false after accepting dialog with " + 245 "history checkbox unchecked" 246 ); 247 248 // Of the three only form entries should be cleared. 249 await promiseHistoryClearedState(uris, false); 250 await ensureDownloadsClearedState(downloadIDs, false); 251 252 for (let entry of formEntries) { 253 let exists = await formNameExists(entry); 254 ok(!exists, "form entry " + entry + " should no longer exist"); 255 } 256 257 // OK, done, cleanup after ourselves. 258 await blankSlate(); 259 await promiseHistoryClearedState(uris, true); 260 await ensureDownloadsClearedState(downloadIDs, true); 261 }; 262 dh.open(); 263 await dh.promiseClosed; 264 }); 265 266 /** 267 * Ensures that the "Everything" duration option works. 268 */ 269 add_task(async function test_everything() { 270 // Add history. 271 let uris = []; 272 let places = []; 273 let pURI; 274 // within past hour, within past two hours, within past four hours and 275 // outside past four hours 276 [10, 70, 130, 250].forEach(function (aValue) { 277 pURI = makeURI("https://" + aValue + "-minutes-ago.com/"); 278 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(aValue) }); 279 uris.push(pURI); 280 }); 281 282 let promiseSanitized = promiseSanitizationComplete(); 283 284 await PlacesTestUtils.addVisits(places); 285 let dh = new DialogHelper(); 286 dh.onload = function () { 287 is( 288 this.isWarningPanelVisible(), 289 false, 290 "Warning panel should be hidden after previously accepting dialog " + 291 "with a predefined timespan" 292 ); 293 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); 294 this.checkPrefCheckbox("history", true); 295 this.acceptDialog(); 296 }; 297 dh.onunload = async function () { 298 await promiseSanitized; 299 intPrefIs( 300 "sanitize.timeSpan", 301 Sanitizer.TIMESPAN_EVERYTHING, 302 "timeSpan pref should be everything after accepting dialog " + 303 "with everything selected" 304 ); 305 306 await promiseHistoryClearedState(uris, true); 307 }; 308 dh.open(); 309 await dh.promiseClosed; 310 }); 311 312 /** 313 * Ensures that the "Everything" warning is visible on dialog open after 314 * the previous test. 315 */ 316 add_task(async function test_everything_warning() { 317 // Add history. 318 let uris = []; 319 let places = []; 320 let pURI; 321 // within past hour, within past two hours, within past four hours and 322 // outside past four hours 323 [10, 70, 130, 250].forEach(function (aValue) { 324 pURI = makeURI("https://" + aValue + "-minutes-ago.com/"); 325 places.push({ uri: pURI, visitDate: visitTimeForMinutesAgo(aValue) }); 326 uris.push(pURI); 327 }); 328 329 let promiseSanitized = promiseSanitizationComplete(); 330 331 await PlacesTestUtils.addVisits(places); 332 let dh = new DialogHelper(); 333 dh.onload = function () { 334 is( 335 this.isWarningPanelVisible(), 336 true, 337 "Warning panel should be visible after previously accepting dialog " + 338 "with clearing everything" 339 ); 340 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); 341 this.checkPrefCheckbox("history", true); 342 this.acceptDialog(); 343 }; 344 dh.onunload = async function () { 345 intPrefIs( 346 "sanitize.timeSpan", 347 Sanitizer.TIMESPAN_EVERYTHING, 348 "timeSpan pref should be everything after accepting dialog " + 349 "with everything selected" 350 ); 351 352 await promiseSanitized; 353 354 await promiseHistoryClearedState(uris, true); 355 }; 356 dh.open(); 357 await dh.promiseClosed; 358 }); 359 360 /** 361 * The next three tests checks that when a certain history item cannot be 362 * cleared then the checkbox should be both disabled and unchecked. 363 * In addition, we ensure that this behavior does not modify the preferences. 364 */ 365 add_task(async function test_cannot_clear_history() { 366 // Add form entries 367 let formEntries = [await promiseAddFormEntryWithMinutesAgo(10)]; 368 369 let promiseSanitized = promiseSanitizationComplete(); 370 371 // Add history. 372 let pURI = makeURI("https://" + 10 + "-minutes-ago.com/"); 373 await PlacesTestUtils.addVisits({ 374 uri: pURI, 375 visitDate: visitTimeForMinutesAgo(10), 376 }); 377 let uris = [pURI]; 378 379 let dh = new DialogHelper(); 380 dh.onload = function () { 381 // Check that the relevant checkboxes are enabled 382 var cb = this.win.document.querySelectorAll( 383 "checkbox[preference='privacy.cpd.formdata']" 384 ); 385 ok( 386 cb.length == 1 && !cb[0].disabled, 387 "There is formdata, checkbox to clear formdata should be enabled." 388 ); 389 390 cb = this.win.document.querySelectorAll( 391 "checkbox[preference='privacy.cpd.history']" 392 ); 393 ok( 394 cb.length == 1 && !cb[0].disabled, 395 "There is history, checkbox to clear history should be enabled." 396 ); 397 398 this.checkAllCheckboxes(); 399 this.acceptDialog(); 400 }; 401 dh.onunload = async function () { 402 await promiseSanitized; 403 404 await promiseHistoryClearedState(uris, true); 405 406 let exists = await formNameExists(formEntries[0]); 407 ok(!exists, "form entry " + formEntries[0] + " should no longer exist"); 408 }; 409 dh.open(); 410 await dh.promiseClosed; 411 }); 412 413 add_task(async function test_no_formdata_history_to_clear() { 414 let promiseSanitized = promiseSanitizationComplete(); 415 let dh = new DialogHelper(); 416 dh.onload = function () { 417 boolPrefIs( 418 "cpd.history", 419 true, 420 "history pref should be true after accepting dialog with " + 421 "history checkbox checked" 422 ); 423 boolPrefIs( 424 "cpd.formdata", 425 true, 426 "formdata pref should be true after accepting dialog with " + 427 "formdata checkbox checked" 428 ); 429 430 var cb = this.win.document.querySelectorAll( 431 "checkbox[preference='privacy.cpd.history']" 432 ); 433 ok( 434 cb.length == 1 && !cb[0].disabled && cb[0].checked, 435 "There is no history, but history checkbox should always be enabled " + 436 "and will be checked from previous preference." 437 ); 438 439 this.acceptDialog(); 440 }; 441 dh.open(); 442 await dh.promiseClosed; 443 await promiseSanitized; 444 }); 445 446 add_task(async function test_form_entries() { 447 let formEntry = await promiseAddFormEntryWithMinutesAgo(10); 448 449 let promiseSanitized = promiseSanitizationComplete(); 450 451 let dh = new DialogHelper(); 452 dh.onload = function () { 453 boolPrefIs( 454 "cpd.formdata", 455 true, 456 "formdata pref should persist previous value after accepting " + 457 "dialog where you could not clear formdata." 458 ); 459 460 var cb = this.win.document.querySelectorAll( 461 "checkbox[preference='privacy.cpd.formdata']" 462 ); 463 464 info( 465 "There exists formEntries so the checkbox should be in sync with the pref." 466 ); 467 is(cb.length, 1, "There is only one checkbox for form data"); 468 ok(!cb[0].disabled, "The checkbox is enabled"); 469 ok(cb[0].checked, "The checkbox is checked"); 470 471 this.acceptDialog(); 472 }; 473 dh.onunload = async function () { 474 await promiseSanitized; 475 let exists = await formNameExists(formEntry); 476 ok(!exists, "form entry " + formEntry + " should no longer exist"); 477 }; 478 dh.open(); 479 await dh.promiseClosed; 480 }); 481 482 // Test for offline apps permission deletion 483 add_task(async function test_offline_apps_permissions() { 484 // Prepare stuff, we will work with www.example.com 485 var URL = "https://www.example.com"; 486 var URI = makeURI(URL); 487 var principal = Services.scriptSecurityManager.createContentPrincipal( 488 URI, 489 {} 490 ); 491 492 let promiseSanitized = promiseSanitizationComplete(); 493 494 // Open the dialog 495 let dh = new DialogHelper(); 496 dh.onload = function () { 497 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); 498 // Clear only offlineApps 499 this.uncheckAllCheckboxes(); 500 this.checkPrefCheckbox("siteSettings", true); 501 this.acceptDialog(); 502 }; 503 dh.onunload = async function () { 504 await promiseSanitized; 505 506 // Check all has been deleted (privileges, data, cache) 507 is( 508 Services.perms.testPermissionFromPrincipal(principal, "offline-app"), 509 0, 510 "offline-app permissions removed" 511 ); 512 }; 513 dh.open(); 514 await dh.promiseClosed; 515 }); 516 517 var now_mSec = Date.now(); 518 var now_uSec = now_mSec * 1000; 519 520 /** 521 * This wraps the dialog and provides some convenience methods for interacting 522 * with it. 523 * 524 * @param browserWin (optional) 525 * The browser window that the dialog is expected to open in. If not 526 * supplied, the initial browser window of the test run is used. 527 */ 528 function DialogHelper(browserWin = window) { 529 this._browserWin = browserWin; 530 this.win = null; 531 this.promiseClosed = new Promise(resolve => { 532 this._resolveClosed = resolve; 533 }); 534 } 535 536 DialogHelper.prototype = { 537 /** 538 * "Presses" the dialog's OK button. 539 */ 540 acceptDialog() { 541 let dialogEl = this.win.document.querySelector("dialog"); 542 is( 543 dialogEl.getButton("accept").disabled, 544 false, 545 "Dialog's OK button should not be disabled" 546 ); 547 dialogEl.acceptDialog(); 548 }, 549 550 /** 551 * "Presses" the dialog's Cancel button. 552 */ 553 cancelDialog() { 554 this.win.document.querySelector("dialog").cancelDialog(); 555 }, 556 557 /** 558 * (Un)checks a history scope checkbox (browser & download history, 559 * form history, etc.). 560 * 561 * @param aPrefName 562 * The final portion of the checkbox's privacy.cpd.* preference name 563 * @param aCheckState 564 * True if the checkbox should be checked, false otherwise 565 */ 566 checkPrefCheckbox(aPrefName, aCheckState) { 567 var pref = "privacy.cpd." + aPrefName; 568 var cb = this.win.document.querySelectorAll( 569 "checkbox[preference='" + pref + "']" 570 ); 571 is(cb.length, 1, "found checkbox for " + pref + " preference"); 572 if (cb[0].checked != aCheckState) { 573 cb[0].click(); 574 } 575 }, 576 577 /** 578 * Makes sure all the checkboxes are checked. 579 */ 580 _checkAllCheckboxesCustom(check) { 581 var cb = this.win.document.querySelectorAll("checkbox[preference]"); 582 Assert.greater(cb.length, 1, "found checkboxes for preferences"); 583 for (var i = 0; i < cb.length; ++i) { 584 var pref = this.win.Preferences.get(cb[i].getAttribute("preference")); 585 if (!!pref.value ^ check) { 586 cb[i].click(); 587 } 588 } 589 }, 590 591 checkAllCheckboxes() { 592 this._checkAllCheckboxesCustom(true); 593 }, 594 595 uncheckAllCheckboxes() { 596 this._checkAllCheckboxesCustom(false); 597 }, 598 599 /** 600 * @return The dialog's duration dropdown 601 */ 602 getDurationDropdown() { 603 return this.win.document.getElementById("sanitizeDurationChoice"); 604 }, 605 606 /** 607 * @return The clear-everything warning box 608 */ 609 getWarningPanel() { 610 return this.win.document.getElementById("sanitizeEverythingWarningBox"); 611 }, 612 613 /** 614 * @return True if the "Everything" warning panel is visible (as opposed to 615 * the tree) 616 */ 617 isWarningPanelVisible() { 618 return !this.getWarningPanel().hidden; 619 }, 620 621 /** 622 * Opens the clear recent history dialog. Before calling this, set 623 * this.onload to a function to execute onload. It should close the dialog 624 * when done so that the tests may continue. Set this.onunload to a function 625 * to execute onunload. this.onunload is optional. If it returns true, the 626 * caller is expected to call promiseAsyncUpdates at some point; if false is 627 * returned, promiseAsyncUpdates is called automatically. 628 */ 629 async open() { 630 let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen( 631 null, 632 "chrome://browser/content/sanitize.xhtml", 633 { 634 isSubDialog: true, 635 } 636 ); 637 638 executeSoon(() => { 639 Sanitizer.showUI(this._browserWin); 640 }); 641 642 this.win = await dialogPromise; 643 this.win.addEventListener( 644 "load", 645 () => { 646 // Run onload on next tick so that gSanitizePromptDialog.init can run first. 647 executeSoon(() => this.onload()); 648 }, 649 { once: true } 650 ); 651 652 this.win.addEventListener( 653 "unload", 654 () => { 655 // Some exceptions that reach here don't reach the test harness, but 656 // ok()/is() do... 657 (async () => { 658 if (this.onunload) { 659 await this.onunload(); 660 } 661 await PlacesTestUtils.promiseAsyncUpdates(); 662 this._resolveClosed(); 663 this.win = null; 664 })(); 665 }, 666 { once: true } 667 ); 668 }, 669 670 /** 671 * Selects a duration in the duration dropdown. 672 * 673 * @param aDurVal 674 * One of the Sanitizer.TIMESPAN_* values 675 */ 676 selectDuration(aDurVal) { 677 this.getDurationDropdown().value = aDurVal; 678 if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { 679 is( 680 this.isWarningPanelVisible(), 681 true, 682 "Warning panel should be visible for TIMESPAN_EVERYTHING" 683 ); 684 } else { 685 is( 686 this.isWarningPanelVisible(), 687 false, 688 "Warning panel should not be visible for non-TIMESPAN_EVERYTHING" 689 ); 690 } 691 }, 692 }; 693 694 /** 695 * Adds a download to history. 696 * 697 * @param aMinutesAgo 698 * The download will be downloaded this many minutes ago 699 */ 700 async function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { 701 let publicList = await Downloads.getList(Downloads.PUBLIC); 702 703 let name = "fakefile-" + aMinutesAgo + "-minutes-ago"; 704 let download = await Downloads.createDownload({ 705 source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", 706 target: name, 707 }); 708 download.startTime = new Date(now_mSec - aMinutesAgo * kMsecPerMin); 709 download.canceled = true; 710 publicList.add(download); 711 712 ok( 713 await downloadExists(name), 714 "Sanity check: download " + name + " should exist after creating it" 715 ); 716 717 aExpectedPathList.push(name); 718 } 719 720 /** 721 * Adds a form entry to history. 722 * 723 * @param aMinutesAgo 724 * The entry will be added this many minutes ago 725 */ 726 function promiseAddFormEntryWithMinutesAgo(aMinutesAgo) { 727 let name = aMinutesAgo + "-minutes-ago"; 728 729 // Artifically age the entry to the proper vintage. 730 let timestamp = now_uSec - aMinutesAgo * kUsecPerMin; 731 732 return FormHistory.update({ 733 op: "add", 734 fieldname: name, 735 value: "dummy", 736 firstUsed: timestamp, 737 }); 738 } 739 740 /** 741 * Checks if a form entry exists. 742 */ 743 async function formNameExists(name) { 744 return !!(await FormHistory.count({ fieldname: name })); 745 } 746 747 /** 748 * Ensures that the given pref is the expected value. 749 * 750 * @param aPrefName 751 * The pref's sub-branch under the privacy branch 752 * @param aExpectedVal 753 * The pref's expected value 754 * @param aMsg 755 * Passed to is() 756 */ 757 function boolPrefIs(aPrefName, aExpectedVal, aMsg) { 758 is(Services.prefs.getBoolPref("privacy." + aPrefName), aExpectedVal, aMsg); 759 } 760 761 /** 762 * Checks to see if the download with the specified path exists. 763 * 764 * @param aPath 765 * The path of the download to check 766 * @return True if the download exists, false otherwise 767 */ 768 async function downloadExists(aPath) { 769 let publicList = await Downloads.getList(Downloads.PUBLIC); 770 let listArray = await publicList.getAll(); 771 return listArray.some(i => i.target.path == aPath); 772 } 773 774 /** 775 * Ensures that the specified downloads are either cleared or not. 776 * 777 * @param aDownloadIDs 778 * Array of download database IDs 779 * @param aShouldBeCleared 780 * True if each download should be cleared, false otherwise 781 */ 782 async function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) { 783 let niceStr = aShouldBeCleared ? "no longer" : "still"; 784 for (let id of aDownloadIDs) { 785 is( 786 await downloadExists(id), 787 !aShouldBeCleared, 788 "download " + id + " should " + niceStr + " exist" 789 ); 790 } 791 } 792 793 /** 794 * Ensures that the given pref is the expected value. 795 * 796 * @param aPrefName 797 * The pref's sub-branch under the privacy branch 798 * @param aExpectedVal 799 * The pref's expected value 800 * @param aMsg 801 * Passed to is() 802 */ 803 function intPrefIs(aPrefName, aExpectedVal, aMsg) { 804 is(Services.prefs.getIntPref("privacy." + aPrefName), aExpectedVal, aMsg); 805 } 806 807 /** 808 * Creates a visit time. 809 * 810 * @param aMinutesAgo 811 * The visit will be visited this many minutes ago 812 */ 813 function visitTimeForMinutesAgo(aMinutesAgo) { 814 return now_uSec - aMinutesAgo * kUsecPerMin; 815 }