browser/base/content/test/general/browser_sanitizeDialog.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
     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/general/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/general/browser_sanitize-timespans.js.
    18  */
    20 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    22 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
    23                                   "resource://gre/modules/FormHistory.jsm");
    24 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
    25                                   "resource://gre/modules/Downloads.jsm");
    27 let tempScope = {};
    28 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
    29                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
    30 let Sanitizer = tempScope.Sanitizer;
    32 const kMsecPerMin = 60 * 1000;
    33 const kUsecPerMin = 60 * 1000000;
    35 let formEntries, downloadIDs, olderDownloadIDs;
    37 // Add tests here.  Each is a function that's called by doNextTest().
    38 var gAllTests = [
    40   /**
    41    * Initializes the dialog to its default state.
    42    */
    43   function () {
    44     let wh = new WindowHelper();
    45     wh.onload = function () {
    46       // Select "Last Hour"
    47       this.selectDuration(Sanitizer.TIMESPAN_HOUR);
    48       // Hide details
    49       if (!this.getItemList().collapsed)
    50         this.toggleDetails();
    51       this.acceptDialog();
    52     };
    53     wh.open();
    54   },
    56   /**
    57    * Cancels the dialog, makes sure history not cleared.
    58    */
    59   function () {
    60     // Add history (within the past hour)
    61     let uris = [];
    62     let places = [];
    63     let pURI;
    64     for (let i = 0; i < 30; i++) {
    65       pURI = makeURI("http://" + i + "-minutes-ago.com/");
    66       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
    67       uris.push(pURI);
    68     }
    70     addVisits(places, function() {
    71       let wh = new WindowHelper();
    72       wh.onload = function () {
    73         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
    74         this.checkPrefCheckbox("history", false);
    75         this.checkDetails(false);
    77         // Show details
    78         this.toggleDetails();
    79         this.checkDetails(true);
    81         // Hide details
    82         this.toggleDetails();
    83         this.checkDetails(false);
    84         this.cancelDialog();
    85       };
    86       wh.onunload = function () {
    87         yield promiseHistoryClearedState(uris, false);
    88         yield blankSlate();
    89         yield promiseHistoryClearedState(uris, true);
    90       };
    91       wh.open();
    92     });
    93   },
    95   function () {
    96     // Add downloads (within the past hour).
    97     Task.spawn(function () {
    98       downloadIDs = [];
    99       for (let i = 0; i < 5; i++) {
   100         yield addDownloadWithMinutesAgo(downloadIDs, i);
   101       }
   102       // Add downloads (over an hour ago).
   103       olderDownloadIDs = [];
   104       for (let i = 0; i < 5; i++) {
   105         yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
   106       }
   108       doNextTest();
   109     }).then(null, Components.utils.reportError);
   110   },
   112   /**
   113    * Ensures that the combined history-downloads checkbox clears both history
   114    * visits and downloads when checked; the dialog respects simple timespan.
   115    */
   116   function () {
   117     // Add history (within the past hour).
   118     let uris = [];
   119     let places = [];
   120     let pURI;
   121     for (let i = 0; i < 30; i++) {
   122       pURI = makeURI("http://" + i + "-minutes-ago.com/");
   123       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
   124       uris.push(pURI);
   125     }
   126     // Add history (over an hour ago).
   127     let olderURIs = [];
   128     for (let i = 0; i < 5; i++) {
   129       pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/");
   130       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)});
   131       olderURIs.push(pURI);
   132     }
   134     addVisits(places, function() {
   135       let totalHistoryVisits = uris.length + olderURIs.length;
   137       let wh = new WindowHelper();
   138       wh.onload = function () {
   139         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
   140         this.checkPrefCheckbox("history", true);
   141         this.acceptDialog();
   142       };
   143       wh.onunload = function () {
   144         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR,
   145                   "timeSpan pref should be hour after accepting dialog with " +
   146                   "hour selected");
   147         boolPrefIs("cpd.history", true,
   148                    "history pref should be true after accepting dialog with " +
   149                    "history checkbox checked");
   150         boolPrefIs("cpd.downloads", true,
   151                    "downloads pref should be true after accepting dialog with " +
   152                    "history checkbox checked");
   154         // History visits and downloads within one hour should be cleared.
   155         yield promiseHistoryClearedState(uris, true);
   156         yield ensureDownloadsClearedState(downloadIDs, true);
   158         // Visits and downloads > 1 hour should still exist.
   159         yield promiseHistoryClearedState(olderURIs, false);
   160         yield ensureDownloadsClearedState(olderDownloadIDs, false);
   162         // OK, done, cleanup after ourselves.
   163         yield blankSlate();
   164         yield promiseHistoryClearedState(olderURIs, true);
   165         yield ensureDownloadsClearedState(olderDownloadIDs, true);
   166       };
   167       wh.open();
   168     });
   169   },
   171   /**
   172    * Add form history entries for the next test.
   173    */
   174   function () {
   175     formEntries = [];
   177     let iter = function() {
   178       for (let i = 0; i < 5; i++) {
   179         formEntries.push(addFormEntryWithMinutesAgo(iter, i));
   180         yield undefined;
   181       }
   182       doNextTest();
   183     }();
   185     iter.next();
   186   },
   188   function () {
   189     // Add downloads (within the past hour).
   190     Task.spawn(function () {
   191       downloadIDs = [];
   192       for (let i = 0; i < 5; i++) {
   193         yield addDownloadWithMinutesAgo(downloadIDs, i);
   194       }
   196       doNextTest();
   197     }).then(null, Components.utils.reportError);
   198   },
   200   /**
   201    * Ensures that the combined history-downloads checkbox removes neither
   202    * history visits nor downloads when not checked.
   203    */
   204   function () {
   205     // Add history, downloads, form entries (within the past hour).
   206     let uris = [];
   207     let places = [];
   208     let pURI;
   209     for (let i = 0; i < 5; i++) {
   210       pURI = makeURI("http://" + i + "-minutes-ago.com/");
   211       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
   212       uris.push(pURI);
   213     }
   215     addVisits(places, function() {
   216       let wh = new WindowHelper();
   217       wh.onload = function () {
   218         is(this.isWarningPanelVisible(), false,
   219            "Warning panel should be hidden after previously accepting dialog " +
   220            "with a predefined timespan");
   221         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
   223         // Remove only form entries, leave history (including downloads).
   224         this.checkPrefCheckbox("history", false);
   225         this.checkPrefCheckbox("formdata", true);
   226         this.acceptDialog();
   227       };
   228       wh.onunload = function () {
   229         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR,
   230                   "timeSpan pref should be hour after accepting dialog with " +
   231                   "hour selected");
   232         boolPrefIs("cpd.history", false,
   233                    "history pref should be false after accepting dialog with " +
   234                    "history checkbox unchecked");
   235         boolPrefIs("cpd.downloads", false,
   236                    "downloads pref should be false after accepting dialog with " +
   237                    "history checkbox unchecked");
   239         // Of the three only form entries should be cleared.
   240         yield promiseHistoryClearedState(uris, false);
   241         yield ensureDownloadsClearedState(downloadIDs, false);
   243         formEntries.forEach(function (entry) {
   244           let exists = yield formNameExists(entry);
   245           is(exists, false, "form entry " + entry + " should no longer exist");
   246         });
   248         // OK, done, cleanup after ourselves.
   249         yield blankSlate();
   250         yield promiseHistoryClearedState(uris, true);
   251         yield ensureDownloadsClearedState(downloadIDs, true);
   252       };
   253       wh.open();
   254     });
   255   },
   257   /**
   258    * Ensures that the "Everything" duration option works.
   259    */
   260   function () {
   261     // Add history.
   262     let uris = [];
   263     let places = [];
   264     let pURI;
   265     // within past hour, within past two hours, within past four hours and 
   266     // outside past four hours
   267     [10, 70, 130, 250].forEach(function(aValue) {
   268       pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
   269       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
   270       uris.push(pURI);
   271     });
   272     addVisits(places, function() {
   273       let wh = new WindowHelper();
   274       wh.onload = function () {
   275         is(this.isWarningPanelVisible(), false,
   276            "Warning panel should be hidden after previously accepting dialog " +
   277            "with a predefined timespan");
   278         this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
   279         this.checkPrefCheckbox("history", true);
   280         this.checkDetails(true);
   282         // Hide details
   283         this.toggleDetails();
   284         this.checkDetails(false);
   286         // Show details
   287         this.toggleDetails();
   288         this.checkDetails(true);
   290         this.acceptDialog();
   291       };
   292       wh.onunload = function () {
   293         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING,
   294                   "timeSpan pref should be everything after accepting dialog " +
   295                   "with everything selected");
   297         yield promiseHistoryClearedState(uris, true);
   298       };
   299       wh.open();
   300     });
   301   },
   303   /**
   304    * Ensures that the "Everything" warning is visible on dialog open after
   305    * the previous test.
   306    */
   307   function () {
   308     // Add history.
   309     let uris = [];
   310     let places = [];
   311     let pURI;
   312     // within past hour, within past two hours, within past four hours and 
   313     // outside past four hours
   314     [10, 70, 130, 250].forEach(function(aValue) {
   315       pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
   316       places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
   317       uris.push(pURI);
   318     });
   319     addVisits(places, function() {
   320       let wh = new WindowHelper();
   321       wh.onload = function () {
   322         is(this.isWarningPanelVisible(), true,
   323            "Warning panel should be visible after previously accepting dialog " +
   324            "with clearing everything");
   325         this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
   326         this.checkPrefCheckbox("history", true);
   327         this.acceptDialog();
   328       };
   329       wh.onunload = function () {
   330         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING,
   331                   "timeSpan pref should be everything after accepting dialog " +
   332                   "with everything selected");
   334         yield promiseHistoryClearedState(uris, true);
   335       };
   336       wh.open();
   337     });
   338   },
   340   /**
   341    * Add form history entry for the next test.
   342    */
   343   function () {
   344     let iter = function() {
   345       formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
   346       yield undefined;
   347       doNextTest();
   348     }();
   350     iter.next();
   351   },
   353   /**
   354    * The next three tests checks that when a certain history item cannot be
   355    * cleared then the checkbox should be both disabled and unchecked.
   356    * In addition, we ensure that this behavior does not modify the preferences.
   357    */
   358   function () {
   359     // Add history.
   360     let pURI = makeURI("http://" + 10 + "-minutes-ago.com/");
   361     addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}, function() {
   362       let uris = [ pURI ];
   364       let wh = new WindowHelper();
   365       wh.onload = function() {
   366         // Check that the relevant checkboxes are enabled
   367         var cb = this.win.document.querySelectorAll(
   368                    "#itemList > [preference='privacy.cpd.formdata']");
   369         ok(cb.length == 1 && !cb[0].disabled, "There is formdata, checkbox to " +
   370            "clear formdata should be enabled.");
   372         var cb = this.win.document.querySelectorAll(
   373                    "#itemList > [preference='privacy.cpd.history']");
   374         ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " +
   375            "clear history should be enabled.");
   377         this.checkAllCheckboxes();
   378         this.acceptDialog();
   379       };
   380       wh.onunload = function () {
   381         yield promiseHistoryClearedState(uris, true);
   383         let exists = yield formNameExists(formEntries[0]);
   384         is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
   385       };
   386       wh.open();
   387     });
   388   },
   389   function () {
   390     let wh = new WindowHelper();
   391     wh.onload = function() {
   392       boolPrefIs("cpd.history", true,
   393                  "history pref should be true after accepting dialog with " +
   394                  "history checkbox checked");
   395       boolPrefIs("cpd.formdata", true,
   396                  "formdata pref should be true after accepting dialog with " +
   397                  "formdata checkbox checked");
   400       // Even though the formdata pref is true, because there is no history
   401       // left to clear, the checkbox will be disabled.
   402       var cb = this.win.document.querySelectorAll(
   403                  "#itemList > [preference='privacy.cpd.formdata']");
   404       ok(cb.length == 1 && cb[0].disabled && !cb[0].checked,
   405          "There is no formdata history, checkbox should be disabled and be " +
   406          "cleared to reduce user confusion (bug 497664).");
   408       var cb = this.win.document.querySelectorAll(
   409                  "#itemList > [preference='privacy.cpd.history']");
   410       ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
   411          "There is no history, but history checkbox should always be enabled " +
   412          "and will be checked from previous preference.");
   414       this.acceptDialog();
   415     }
   416     wh.open();
   417   },
   419   /**
   420    * Add form history entry for the next test.
   421    */
   422   function () {
   423     let iter = function() {
   424       formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
   425       yield undefined;
   426       doNextTest();
   427     }();
   429     iter.next();
   430   },
   432   function () {
   433     let wh = new WindowHelper();
   434     wh.onload = function() {
   435       boolPrefIs("cpd.formdata", true,
   436                  "formdata pref should persist previous value after accepting " +
   437                  "dialog where you could not clear formdata.");
   439       var cb = this.win.document.querySelectorAll(
   440                  "#itemList > [preference='privacy.cpd.formdata']");
   441       ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
   442          "There exists formEntries so the checkbox should be in sync with " +
   443          "the pref.");
   445       this.acceptDialog();
   446     };
   447     wh.onunload = function () {
   448       let exists = yield formNameExists(formEntries[0]);
   449       is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
   450     };
   451     wh.open();
   452   },
   455   /**
   456    * These next six tests together ensure that toggling details persists
   457    * across dialog openings.
   458    */
   459   function () {
   460     let wh = new WindowHelper();
   461     wh.onload = function () {
   462       // Check all items and select "Everything"
   463       this.checkAllCheckboxes();
   464       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
   466       // Hide details
   467       this.toggleDetails();
   468       this.checkDetails(false);
   469       this.acceptDialog();
   470     };
   471     wh.open();
   472   },
   473   function () {
   474     let wh = new WindowHelper();
   475     wh.onload = function () {
   476       // Details should remain closed because all items are checked.
   477       this.checkDetails(false);
   479       // Uncheck history.
   480       this.checkPrefCheckbox("history", false);
   481       this.acceptDialog();
   482     };
   483     wh.open();
   484   },
   485   function () {
   486     let wh = new WindowHelper();
   487     wh.onload = function () {
   488       // Details should be open because not all items are checked.
   489       this.checkDetails(true);
   491       // Modify the Site Preferences item state (bug 527820)
   492       this.checkAllCheckboxes();
   493       this.checkPrefCheckbox("siteSettings", false);
   494       this.acceptDialog();
   495     };
   496     wh.open();
   497   },
   498   function () {
   499     let wh = new WindowHelper();
   500     wh.onload = function () {
   501       // Details should be open because not all items are checked.
   502       this.checkDetails(true);
   504       // Hide details
   505       this.toggleDetails();
   506       this.checkDetails(false);
   507       this.cancelDialog();
   508     };
   509     wh.open();
   510   },
   511   function () {
   512     let wh = new WindowHelper();
   513     wh.onload = function () {
   514       // Details should be open because not all items are checked.
   515       this.checkDetails(true);
   517       // Select another duration
   518       this.selectDuration(Sanitizer.TIMESPAN_HOUR);
   519       // Hide details
   520       this.toggleDetails();
   521       this.checkDetails(false);
   522       this.acceptDialog();
   523     };
   524     wh.open();
   525   },
   526   function () {
   527     let wh = new WindowHelper();
   528     wh.onload = function () {
   529       // Details should not be open because "Last Hour" is selected
   530       this.checkDetails(false);
   532       this.cancelDialog();
   533     };
   534     wh.open();
   535   },
   536   function () {
   537     let wh = new WindowHelper();
   538     wh.onload = function () {
   539       // Details should have remained closed
   540       this.checkDetails(false);
   542       // Show details
   543       this.toggleDetails();
   544       this.checkDetails(true);
   545       this.cancelDialog();
   546     };
   547     wh.open();
   548   },
   549   function () {
   550     // Test for offline cache deletion
   552     // Prepare stuff, we will work with www.example.com
   553     var URL = "http://www.example.com";
   555     var ios = Cc["@mozilla.org/network/io-service;1"]
   556               .getService(Ci.nsIIOService);
   557     var URI = ios.newURI(URL, null, null);
   559     var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
   560              .getService(Ci.nsIScriptSecurityManager);
   561     var principal = sm.getNoAppCodebasePrincipal(URI);
   563     // Give www.example.com privileges to store offline data
   564     var pm = Cc["@mozilla.org/permissionmanager;1"]
   565              .getService(Ci.nsIPermissionManager);
   566     pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
   567     pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
   569     // Store something to the offline cache
   570     const nsICache = Components.interfaces.nsICache;
   571     var cs = Components.classes["@mozilla.org/network/cache-service;1"]
   572              .getService(Components.interfaces.nsICacheService);
   573     var session = cs.createSession(URL + "/manifest", nsICache.STORE_OFFLINE, nsICache.STREAM_BASED);
   575     // Open the dialog
   576     let wh = new WindowHelper();
   577     wh.onload = function () {
   578       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
   579       // Show details
   580       this.toggleDetails();
   581       // Clear only offlineApps
   582       this.uncheckAllCheckboxes();
   583       this.checkPrefCheckbox("offlineApps", true);
   584       this.acceptDialog();
   585     };
   586     wh.onunload = function () {
   587       // Check if the cache has been deleted
   588       var size = -1;
   589       var visitor = {
   590         visitDevice: function (deviceID, deviceInfo)
   591         {
   592           if (deviceID == "offline")
   593             size = deviceInfo.totalSize;
   595           // Do not enumerate entries
   596           return false;
   597         },
   599         visitEntry: function (deviceID, entryInfo)
   600         {
   601           // Do not enumerate entries.
   602           return false;
   603         }
   604       };
   605       cs.visitEntries(visitor);
   606       is(size, 0, "offline application cache entries evicted");
   607     };
   609     var cacheListener = {
   610       onCacheEntryAvailable: function (entry, access, status) {
   611         is(status, Cr.NS_OK);
   612         var stream = entry.openOutputStream(0);
   613         var content = "content";
   614         stream.write(content, content.length);
   615         stream.close();
   616         entry.close();
   617         wh.open();
   618       }
   619     };
   621     session.asyncOpenCacheEntry(URL, nsICache.ACCESS_READ_WRITE, cacheListener);
   622   },
   623   function () {
   624     // Test for offline apps permission deletion
   626     // Prepare stuff, we will work with www.example.com
   627     var URL = "http://www.example.com";
   629     var ios = Cc["@mozilla.org/network/io-service;1"]
   630               .getService(Ci.nsIIOService);
   631     var URI = ios.newURI(URL, null, null);
   633     var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
   634              .getService(Ci.nsIScriptSecurityManager);
   635     var principal = sm.getNoAppCodebasePrincipal(URI);
   637     // Open the dialog
   638     let wh = new WindowHelper();
   639     wh.onload = function () {
   640       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
   641       // Show details
   642       this.toggleDetails();
   643       // Clear only offlineApps
   644       this.uncheckAllCheckboxes();
   645       this.checkPrefCheckbox("siteSettings", true);
   646       this.acceptDialog();
   647     };
   648     wh.onunload = function () {
   649       // Check all has been deleted (privileges, data, cache)
   650       var pm = Cc["@mozilla.org/permissionmanager;1"]
   651                .getService(Ci.nsIPermissionManager);
   652       is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed");
   653     };
   654     wh.open();
   655   }
   656 ];
   658 // Index in gAllTests of the test currently being run.  Incremented for each
   659 // test run.  See doNextTest().
   660 var gCurrTest = 0;
   662 let now_mSec = Date.now();
   663 let now_uSec = now_mSec * 1000;
   665 ///////////////////////////////////////////////////////////////////////////////
   667 /**
   668  * This wraps the dialog and provides some convenience methods for interacting
   669  * with it.
   670  *
   671  * @param aWin
   672  *        The dialog's nsIDOMWindow
   673  */
   674 function WindowHelper(aWin) {
   675   this.win = aWin;
   676 }
   678 WindowHelper.prototype = {
   679   /**
   680    * "Presses" the dialog's OK button.
   681    */
   682   acceptDialog: function () {
   683     is(this.win.document.documentElement.getButton("accept").disabled, false,
   684        "Dialog's OK button should not be disabled");
   685     this.win.document.documentElement.acceptDialog();
   686   },
   688   /**
   689    * "Presses" the dialog's Cancel button.
   690    */
   691   cancelDialog: function () {
   692     this.win.document.documentElement.cancelDialog();
   693   },
   695   /**
   696    * Ensures that the details progressive disclosure button and the item list
   697    * hidden by it match up.  Also makes sure the height of the dialog is
   698    * sufficient for the item list and warning panel.
   699    *
   700    * @param aShouldBeShown
   701    *        True if you expect the details to be shown and false if hidden
   702    */
   703   checkDetails: function (aShouldBeShown) {
   704     let button = this.getDetailsButton();
   705     let list = this.getItemList();
   706     let hidden = list.hidden || list.collapsed;
   707     is(hidden, !aShouldBeShown,
   708        "Details should be " + (aShouldBeShown ? "shown" : "hidden") +
   709        " but were actually " + (hidden ? "hidden" : "shown"));
   710     let dir = hidden ? "down" : "up";
   711     is(button.className, "expander-" + dir,
   712        "Details button should be " + dir + " because item list is " +
   713        (hidden ? "" : "not ") + "hidden");
   714     let height = 0;
   715     if (!hidden) {
   716       ok(list.boxObject.height > 30, "listbox has sufficient size")
   717       height += list.boxObject.height;
   718     }
   719     if (this.isWarningPanelVisible())
   720       height += this.getWarningPanel().boxObject.height;
   721     ok(height < this.win.innerHeight,
   722        "Window should be tall enough to fit warning panel and item list");
   723   },
   725   /**
   726    * (Un)checks a history scope checkbox (browser & download history,
   727    * form history, etc.).
   728    *
   729    * @param aPrefName
   730    *        The final portion of the checkbox's privacy.cpd.* preference name
   731    * @param aCheckState
   732    *        True if the checkbox should be checked, false otherwise
   733    */
   734   checkPrefCheckbox: function (aPrefName, aCheckState) {
   735     var pref = "privacy.cpd." + aPrefName;
   736     var cb = this.win.document.querySelectorAll(
   737                "#itemList > [preference='" + pref + "']");
   738     is(cb.length, 1, "found checkbox for " + pref + " preference");
   739     if (cb[0].checked != aCheckState)
   740       cb[0].click();
   741   },
   743   /**
   744    * Makes sure all the checkboxes are checked.
   745    */
   746   _checkAllCheckboxesCustom: function (check) {
   747     var cb = this.win.document.querySelectorAll("#itemList > [preference]");
   748     ok(cb.length > 1, "found checkboxes for preferences");
   749     for (var i = 0; i < cb.length; ++i) {
   750       var pref = this.win.document.getElementById(cb[i].getAttribute("preference"));
   751       if (!!pref.value ^ check)
   752         cb[i].click();
   753     }
   754   },
   756   checkAllCheckboxes: function () {
   757     this._checkAllCheckboxesCustom(true);
   758   },
   760   uncheckAllCheckboxes: function () {
   761     this._checkAllCheckboxesCustom(false);
   762   },
   764   /**
   765    * @return The details progressive disclosure button
   766    */
   767   getDetailsButton: function () {
   768     return this.win.document.getElementById("detailsExpander");
   769   },
   771   /**
   772    * @return The dialog's duration dropdown
   773    */
   774   getDurationDropdown: function () {
   775     return this.win.document.getElementById("sanitizeDurationChoice");
   776   },
   778   /**
   779    * @return The item list hidden by the details progressive disclosure button
   780    */
   781   getItemList: function () {
   782     return this.win.document.getElementById("itemList");
   783   },
   785   /**
   786    * @return The clear-everything warning box
   787    */
   788   getWarningPanel: function () {
   789     return this.win.document.getElementById("sanitizeEverythingWarningBox");
   790   },
   792   /**
   793    * @return True if the "Everything" warning panel is visible (as opposed to
   794    *         the tree)
   795    */
   796   isWarningPanelVisible: function () {
   797     return !this.getWarningPanel().hidden;
   798   },
   800   /**
   801    * Opens the clear recent history dialog.  Before calling this, set
   802    * this.onload to a function to execute onload.  It should close the dialog
   803    * when done so that the tests may continue.  Set this.onunload to a function
   804    * to execute onunload.  this.onunload is optional. If it returns true, the
   805    * caller is expected to call waitForAsyncUpdates at some point; if false is
   806    * returned, waitForAsyncUpdates is called automatically.
   807    */
   808   open: function () {
   809     let wh = this;
   811     function windowObserver(aSubject, aTopic, aData) {
   812       if (aTopic != "domwindowopened")
   813         return;
   815       Services.ww.unregisterNotification(windowObserver);
   817       var loaded = false;
   818       let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
   820       win.addEventListener("load", function onload(event) {
   821         win.removeEventListener("load", onload, false);
   823         if (win.name !== "SanitizeDialog")
   824           return;
   826         wh.win = win;
   827         loaded = true;
   829         executeSoon(function () {
   830           // Some exceptions that reach here don't reach the test harness, but
   831           // ok()/is() do...
   832           try {
   833             wh.onload();
   834           }
   835           catch (exc) {
   836             win.close();
   837             ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
   838             finish();
   839           }
   840         });
   841       }, false);
   843       win.addEventListener("unload", function onunload(event) {
   844         if (win.name !== "SanitizeDialog") {
   845           win.removeEventListener("unload", onunload, false);
   846           return;
   847         }
   849         // Why is unload fired before load?
   850         if (!loaded)
   851           return;
   853         win.removeEventListener("unload", onunload, false);
   854         wh.win = win;
   856         executeSoon(function () {
   857           // Some exceptions that reach here don't reach the test harness, but
   858           // ok()/is() do...
   859           try {
   860             if (wh.onunload) {
   861               Task.spawn(wh.onunload).then(function() {
   862                 waitForAsyncUpdates(doNextTest);
   863               }).then(null, Components.utils.reportError);
   864             } else {
   865               waitForAsyncUpdates(doNextTest);
   866             }
   867           }
   868           catch (exc) {
   869             win.close();
   870             ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
   871             finish();
   872           }
   873         });
   874       }, false);
   875     }
   876     Services.ww.registerNotification(windowObserver);
   877     Services.ww.openWindow(null,
   878                            "chrome://browser/content/sanitize.xul",
   879                            "SanitizeDialog",
   880                            "chrome,titlebar,dialog,centerscreen,modal",
   881                            null);
   882   },
   884   /**
   885    * Selects a duration in the duration dropdown.
   886    *
   887    * @param aDurVal
   888    *        One of the Sanitizer.TIMESPAN_* values
   889    */
   890   selectDuration: function (aDurVal) {
   891     this.getDurationDropdown().value = aDurVal;
   892     if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) {
   893       is(this.isWarningPanelVisible(), true,
   894          "Warning panel should be visible for TIMESPAN_EVERYTHING");
   895     }
   896     else {
   897       is(this.isWarningPanelVisible(), false,
   898          "Warning panel should not be visible for non-TIMESPAN_EVERYTHING");
   899     }
   900   },
   902   /**
   903    * Toggles the details progressive disclosure button.
   904    */
   905   toggleDetails: function () {
   906     this.getDetailsButton().click();
   907   }
   908 };
   910 /**
   911  * Adds a download to history.
   912  *
   913  * @param aMinutesAgo
   914  *        The download will be downloaded this many minutes ago
   915  */
   916 function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
   917   let publicList = yield Downloads.getList(Downloads.PUBLIC);
   919   let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
   920   let download = yield Downloads.createDownload({
   921     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
   922     target: name
   923   });
   924   download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin));
   925   download.canceled = true;
   926   publicList.add(download);
   928   ok((yield downloadExists(name)),
   929      "Sanity check: download " + name +
   930      " should exist after creating it");
   932   aExpectedPathList.push(name);
   933 }
   935 /**
   936  * Adds a form entry to history.
   937  *
   938  * @param aMinutesAgo
   939  *        The entry will be added this many minutes ago
   940  */
   941 function addFormEntryWithMinutesAgo(then, aMinutesAgo) {
   942   let name = aMinutesAgo + "-minutes-ago";
   944   // Artifically age the entry to the proper vintage.
   945   let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin);
   947   FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp },
   948                      { handleError: function (error) {
   949                          do_throw("Error occurred updating form history: " + error);
   950                        },
   951                        handleCompletion: function (reason) { then.next(); }
   952                      });
   953   return name;
   954 }
   956 /**
   957  * Checks if a form entry exists.
   958  */
   959 function formNameExists(name)
   960 {
   961   let deferred = Promise.defer();
   963   let count = 0;
   964   FormHistory.count({ fieldname: name },
   965                     { handleResult: function (result) count = result,
   966                       handleError: function (error) {
   967                         do_throw("Error occurred searching form history: " + error);
   968                         deferred.reject(error);
   969                       },
   970                       handleCompletion: function (reason) {
   971                           if (!reason) deferred.resolve(count);
   972                       }
   973                     });
   975   return deferred.promise;
   976 }
   978 /**
   979  * Removes all history visits, downloads, and form entries.
   980  */
   981 function blankSlate() {
   982   PlacesUtils.bhistory.removeAllPages();
   984   // The promise is resolved only when removing both downloads and form history are done.
   985   let deferred = Promise.defer();
   986   let formHistoryDone = false, downloadsDone = false;
   988   Task.spawn(function deleteAllDownloads() {
   989     let publicList = yield Downloads.getList(Downloads.PUBLIC);
   990     let downloads = yield publicList.getAll();
   991     for (let download of downloads) {
   992       yield publicList.remove(download);
   993       yield download.finalize(true);
   994     }
   995     downloadsDone = true;
   996     if (formHistoryDone) {
   997       deferred.resolve();
   998     }
   999   }).then(null, Components.utils.reportError);
  1001   FormHistory.update({ op: "remove" },
  1002                      { handleError: function (error) {
  1003                          do_throw("Error occurred updating form history: " + error);
  1004                          deferred.reject(error);
  1005                        },
  1006                        handleCompletion: function (reason) {
  1007                          if (!reason) {
  1008                            formHistoryDone = true;
  1009                            if (downloadsDone) {
  1010                              deferred.resolve();
  1014                      });
  1015   return deferred.promise;
  1018 /**
  1019  * Ensures that the given pref is the expected value.
  1021  * @param aPrefName
  1022  *        The pref's sub-branch under the privacy branch
  1023  * @param aExpectedVal
  1024  *        The pref's expected value
  1025  * @param aMsg
  1026  *        Passed to is()
  1027  */
  1028 function boolPrefIs(aPrefName, aExpectedVal, aMsg) {
  1029   is(gPrefService.getBoolPref("privacy." + aPrefName), aExpectedVal, aMsg);
  1032 /**
  1033  * Checks to see if the download with the specified path exists.
  1035  * @param  aPath
  1036  *         The path of the download to check
  1037  * @return True if the download exists, false otherwise
  1038  */
  1039 function downloadExists(aPath)
  1041   return Task.spawn(function() {
  1042     let publicList = yield Downloads.getList(Downloads.PUBLIC);
  1043     let listArray = yield publicList.getAll();
  1044     throw new Task.Result(listArray.some(i => i.target.path == aPath));
  1045   });
  1048 /**
  1049  * Runs the next test in the gAllTests array.  If all tests have been run,
  1050  * finishes the entire suite.
  1051  */
  1052 function doNextTest() {
  1053   if (gAllTests.length <= gCurrTest) {
  1054     blankSlate();
  1055     waitForAsyncUpdates(finish);
  1057   else {
  1058     let ct = gCurrTest;
  1059     gCurrTest++;
  1060     gAllTests[ct]();
  1064 /**
  1065  * Ensures that the specified downloads are either cleared or not.
  1067  * @param aDownloadIDs
  1068  *        Array of download database IDs
  1069  * @param aShouldBeCleared
  1070  *        True if each download should be cleared, false otherwise
  1071  */
  1072 function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
  1073   let niceStr = aShouldBeCleared ? "no longer" : "still";
  1074   aDownloadIDs.forEach(function (id) {
  1075     is((yield downloadExists(id)), !aShouldBeCleared,
  1076        "download " + id + " should " + niceStr + " exist");
  1077   });
  1080 /**
  1081  * Ensures that the given pref is the expected value.
  1083  * @param aPrefName
  1084  *        The pref's sub-branch under the privacy branch
  1085  * @param aExpectedVal
  1086  *        The pref's expected value
  1087  * @param aMsg
  1088  *        Passed to is()
  1089  */
  1090 function intPrefIs(aPrefName, aExpectedVal, aMsg) {
  1091   is(gPrefService.getIntPref("privacy." + aPrefName), aExpectedVal, aMsg);
  1094 /**
  1095  * Creates a visit time.
  1097  * @param aMinutesAgo
  1098  *        The visit will be visited this many minutes ago
  1099  */
  1100 function visitTimeForMinutesAgo(aMinutesAgo) {
  1101   return now_uSec - aMinutesAgo * kUsecPerMin;
  1104 ///////////////////////////////////////////////////////////////////////////////
  1106 function test() {
  1107   requestLongerTimeout(2);
  1108   waitForExplicitFinish();
  1109   blankSlate();
  1110   // Kick off all the tests in the gAllTests array.
  1111   waitForAsyncUpdates(doNextTest);

mercurial