michael@0: /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Tests the sanitize dialog (a.k.a. the clear recent history dialog). michael@0: * See bug 480169. michael@0: * michael@0: * The purpose of this test is not to fully flex the sanitize timespan code; michael@0: * browser/base/content/test/general/browser_sanitize-timespans.js does that. This michael@0: * test checks the UI of the dialog and makes sure it's correctly connected to michael@0: * the sanitize timespan code. michael@0: * michael@0: * Some of this code, especially the history creation parts, was taken from michael@0: * browser/base/content/test/general/browser_sanitize-timespans.js. michael@0: */ michael@0: michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", michael@0: "resource://gre/modules/FormHistory.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Downloads", michael@0: "resource://gre/modules/Downloads.jsm"); michael@0: michael@0: let tempScope = {}; michael@0: Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) michael@0: .loadSubScript("chrome://browser/content/sanitize.js", tempScope); michael@0: let Sanitizer = tempScope.Sanitizer; michael@0: michael@0: const kMsecPerMin = 60 * 1000; michael@0: const kUsecPerMin = 60 * 1000000; michael@0: michael@0: let formEntries, downloadIDs, olderDownloadIDs; michael@0: michael@0: // Add tests here. Each is a function that's called by doNextTest(). michael@0: var gAllTests = [ michael@0: michael@0: /** michael@0: * Initializes the dialog to its default state. michael@0: */ michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Select "Last Hour" michael@0: this.selectDuration(Sanitizer.TIMESPAN_HOUR); michael@0: // Hide details michael@0: if (!this.getItemList().collapsed) michael@0: this.toggleDetails(); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: michael@0: /** michael@0: * Cancels the dialog, makes sure history not cleared. michael@0: */ michael@0: function () { michael@0: // Add history (within the past hour) michael@0: let uris = []; michael@0: let places = []; michael@0: let pURI; michael@0: for (let i = 0; i < 30; i++) { michael@0: pURI = makeURI("http://" + i + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); michael@0: uris.push(pURI); michael@0: } michael@0: michael@0: addVisits(places, function() { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: this.selectDuration(Sanitizer.TIMESPAN_HOUR); michael@0: this.checkPrefCheckbox("history", false); michael@0: this.checkDetails(false); michael@0: michael@0: // Show details michael@0: this.toggleDetails(); michael@0: this.checkDetails(true); michael@0: michael@0: // Hide details michael@0: this.toggleDetails(); michael@0: this.checkDetails(false); michael@0: this.cancelDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: yield promiseHistoryClearedState(uris, false); michael@0: yield blankSlate(); michael@0: yield promiseHistoryClearedState(uris, true); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: michael@0: function () { michael@0: // Add downloads (within the past hour). michael@0: Task.spawn(function () { michael@0: downloadIDs = []; michael@0: for (let i = 0; i < 5; i++) { michael@0: yield addDownloadWithMinutesAgo(downloadIDs, i); michael@0: } michael@0: // Add downloads (over an hour ago). michael@0: olderDownloadIDs = []; michael@0: for (let i = 0; i < 5; i++) { michael@0: yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); michael@0: } michael@0: michael@0: doNextTest(); michael@0: }).then(null, Components.utils.reportError); michael@0: }, michael@0: michael@0: /** michael@0: * Ensures that the combined history-downloads checkbox clears both history michael@0: * visits and downloads when checked; the dialog respects simple timespan. michael@0: */ michael@0: function () { michael@0: // Add history (within the past hour). michael@0: let uris = []; michael@0: let places = []; michael@0: let pURI; michael@0: for (let i = 0; i < 30; i++) { michael@0: pURI = makeURI("http://" + i + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); michael@0: uris.push(pURI); michael@0: } michael@0: // Add history (over an hour ago). michael@0: let olderURIs = []; michael@0: for (let i = 0; i < 5; i++) { michael@0: pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)}); michael@0: olderURIs.push(pURI); michael@0: } michael@0: michael@0: addVisits(places, function() { michael@0: let totalHistoryVisits = uris.length + olderURIs.length; michael@0: michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: this.selectDuration(Sanitizer.TIMESPAN_HOUR); michael@0: this.checkPrefCheckbox("history", true); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, michael@0: "timeSpan pref should be hour after accepting dialog with " + michael@0: "hour selected"); michael@0: boolPrefIs("cpd.history", true, michael@0: "history pref should be true after accepting dialog with " + michael@0: "history checkbox checked"); michael@0: boolPrefIs("cpd.downloads", true, michael@0: "downloads pref should be true after accepting dialog with " + michael@0: "history checkbox checked"); michael@0: michael@0: // History visits and downloads within one hour should be cleared. michael@0: yield promiseHistoryClearedState(uris, true); michael@0: yield ensureDownloadsClearedState(downloadIDs, true); michael@0: michael@0: // Visits and downloads > 1 hour should still exist. michael@0: yield promiseHistoryClearedState(olderURIs, false); michael@0: yield ensureDownloadsClearedState(olderDownloadIDs, false); michael@0: michael@0: // OK, done, cleanup after ourselves. michael@0: yield blankSlate(); michael@0: yield promiseHistoryClearedState(olderURIs, true); michael@0: yield ensureDownloadsClearedState(olderDownloadIDs, true); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Add form history entries for the next test. michael@0: */ michael@0: function () { michael@0: formEntries = []; michael@0: michael@0: let iter = function() { michael@0: for (let i = 0; i < 5; i++) { michael@0: formEntries.push(addFormEntryWithMinutesAgo(iter, i)); michael@0: yield undefined; michael@0: } michael@0: doNextTest(); michael@0: }(); michael@0: michael@0: iter.next(); michael@0: }, michael@0: michael@0: function () { michael@0: // Add downloads (within the past hour). michael@0: Task.spawn(function () { michael@0: downloadIDs = []; michael@0: for (let i = 0; i < 5; i++) { michael@0: yield addDownloadWithMinutesAgo(downloadIDs, i); michael@0: } michael@0: michael@0: doNextTest(); michael@0: }).then(null, Components.utils.reportError); michael@0: }, michael@0: michael@0: /** michael@0: * Ensures that the combined history-downloads checkbox removes neither michael@0: * history visits nor downloads when not checked. michael@0: */ michael@0: function () { michael@0: // Add history, downloads, form entries (within the past hour). michael@0: let uris = []; michael@0: let places = []; michael@0: let pURI; michael@0: for (let i = 0; i < 5; i++) { michael@0: pURI = makeURI("http://" + i + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); michael@0: uris.push(pURI); michael@0: } michael@0: michael@0: addVisits(places, function() { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: is(this.isWarningPanelVisible(), false, michael@0: "Warning panel should be hidden after previously accepting dialog " + michael@0: "with a predefined timespan"); michael@0: this.selectDuration(Sanitizer.TIMESPAN_HOUR); michael@0: michael@0: // Remove only form entries, leave history (including downloads). michael@0: this.checkPrefCheckbox("history", false); michael@0: this.checkPrefCheckbox("formdata", true); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, michael@0: "timeSpan pref should be hour after accepting dialog with " + michael@0: "hour selected"); michael@0: boolPrefIs("cpd.history", false, michael@0: "history pref should be false after accepting dialog with " + michael@0: "history checkbox unchecked"); michael@0: boolPrefIs("cpd.downloads", false, michael@0: "downloads pref should be false after accepting dialog with " + michael@0: "history checkbox unchecked"); michael@0: michael@0: // Of the three only form entries should be cleared. michael@0: yield promiseHistoryClearedState(uris, false); michael@0: yield ensureDownloadsClearedState(downloadIDs, false); michael@0: michael@0: formEntries.forEach(function (entry) { michael@0: let exists = yield formNameExists(entry); michael@0: is(exists, false, "form entry " + entry + " should no longer exist"); michael@0: }); michael@0: michael@0: // OK, done, cleanup after ourselves. michael@0: yield blankSlate(); michael@0: yield promiseHistoryClearedState(uris, true); michael@0: yield ensureDownloadsClearedState(downloadIDs, true); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Ensures that the "Everything" duration option works. michael@0: */ michael@0: function () { michael@0: // Add history. michael@0: let uris = []; michael@0: let places = []; michael@0: let pURI; michael@0: // within past hour, within past two hours, within past four hours and michael@0: // outside past four hours michael@0: [10, 70, 130, 250].forEach(function(aValue) { michael@0: pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); michael@0: uris.push(pURI); michael@0: }); michael@0: addVisits(places, function() { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: is(this.isWarningPanelVisible(), false, michael@0: "Warning panel should be hidden after previously accepting dialog " + michael@0: "with a predefined timespan"); michael@0: this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); michael@0: this.checkPrefCheckbox("history", true); michael@0: this.checkDetails(true); michael@0: michael@0: // Hide details michael@0: this.toggleDetails(); michael@0: this.checkDetails(false); michael@0: michael@0: // Show details michael@0: this.toggleDetails(); michael@0: this.checkDetails(true); michael@0: michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING, michael@0: "timeSpan pref should be everything after accepting dialog " + michael@0: "with everything selected"); michael@0: michael@0: yield promiseHistoryClearedState(uris, true); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Ensures that the "Everything" warning is visible on dialog open after michael@0: * the previous test. michael@0: */ michael@0: function () { michael@0: // Add history. michael@0: let uris = []; michael@0: let places = []; michael@0: let pURI; michael@0: // within past hour, within past two hours, within past four hours and michael@0: // outside past four hours michael@0: [10, 70, 130, 250].forEach(function(aValue) { michael@0: pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); michael@0: places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); michael@0: uris.push(pURI); michael@0: }); michael@0: addVisits(places, function() { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: is(this.isWarningPanelVisible(), true, michael@0: "Warning panel should be visible after previously accepting dialog " + michael@0: "with clearing everything"); michael@0: this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); michael@0: this.checkPrefCheckbox("history", true); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING, michael@0: "timeSpan pref should be everything after accepting dialog " + michael@0: "with everything selected"); michael@0: michael@0: yield promiseHistoryClearedState(uris, true); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Add form history entry for the next test. michael@0: */ michael@0: function () { michael@0: let iter = function() { michael@0: formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; michael@0: yield undefined; michael@0: doNextTest(); michael@0: }(); michael@0: michael@0: iter.next(); michael@0: }, michael@0: michael@0: /** michael@0: * The next three tests checks that when a certain history item cannot be michael@0: * cleared then the checkbox should be both disabled and unchecked. michael@0: * In addition, we ensure that this behavior does not modify the preferences. michael@0: */ michael@0: function () { michael@0: // Add history. michael@0: let pURI = makeURI("http://" + 10 + "-minutes-ago.com/"); michael@0: addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}, function() { michael@0: let uris = [ pURI ]; michael@0: michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function() { michael@0: // Check that the relevant checkboxes are enabled michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='privacy.cpd.formdata']"); michael@0: ok(cb.length == 1 && !cb[0].disabled, "There is formdata, checkbox to " + michael@0: "clear formdata should be enabled."); michael@0: michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='privacy.cpd.history']"); michael@0: ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " + michael@0: "clear history should be enabled."); michael@0: michael@0: this.checkAllCheckboxes(); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: yield promiseHistoryClearedState(uris, true); michael@0: michael@0: let exists = yield formNameExists(formEntries[0]); michael@0: is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); michael@0: }; michael@0: wh.open(); michael@0: }); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function() { michael@0: boolPrefIs("cpd.history", true, michael@0: "history pref should be true after accepting dialog with " + michael@0: "history checkbox checked"); michael@0: boolPrefIs("cpd.formdata", true, michael@0: "formdata pref should be true after accepting dialog with " + michael@0: "formdata checkbox checked"); michael@0: michael@0: michael@0: // Even though the formdata pref is true, because there is no history michael@0: // left to clear, the checkbox will be disabled. michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='privacy.cpd.formdata']"); michael@0: ok(cb.length == 1 && cb[0].disabled && !cb[0].checked, michael@0: "There is no formdata history, checkbox should be disabled and be " + michael@0: "cleared to reduce user confusion (bug 497664)."); michael@0: michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='privacy.cpd.history']"); michael@0: ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, michael@0: "There is no history, but history checkbox should always be enabled " + michael@0: "and will be checked from previous preference."); michael@0: michael@0: this.acceptDialog(); michael@0: } michael@0: wh.open(); michael@0: }, michael@0: michael@0: /** michael@0: * Add form history entry for the next test. michael@0: */ michael@0: function () { michael@0: let iter = function() { michael@0: formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; michael@0: yield undefined; michael@0: doNextTest(); michael@0: }(); michael@0: michael@0: iter.next(); michael@0: }, michael@0: michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function() { michael@0: boolPrefIs("cpd.formdata", true, michael@0: "formdata pref should persist previous value after accepting " + michael@0: "dialog where you could not clear formdata."); michael@0: michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='privacy.cpd.formdata']"); michael@0: ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, michael@0: "There exists formEntries so the checkbox should be in sync with " + michael@0: "the pref."); michael@0: michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: let exists = yield formNameExists(formEntries[0]); michael@0: is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: michael@0: michael@0: /** michael@0: * These next six tests together ensure that toggling details persists michael@0: * across dialog openings. michael@0: */ michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Check all items and select "Everything" michael@0: this.checkAllCheckboxes(); michael@0: this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); michael@0: michael@0: // Hide details michael@0: this.toggleDetails(); michael@0: this.checkDetails(false); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should remain closed because all items are checked. michael@0: this.checkDetails(false); michael@0: michael@0: // Uncheck history. michael@0: this.checkPrefCheckbox("history", false); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should be open because not all items are checked. michael@0: this.checkDetails(true); michael@0: michael@0: // Modify the Site Preferences item state (bug 527820) michael@0: this.checkAllCheckboxes(); michael@0: this.checkPrefCheckbox("siteSettings", false); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should be open because not all items are checked. michael@0: this.checkDetails(true); michael@0: michael@0: // Hide details michael@0: this.toggleDetails(); michael@0: this.checkDetails(false); michael@0: this.cancelDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should be open because not all items are checked. michael@0: this.checkDetails(true); michael@0: michael@0: // Select another duration michael@0: this.selectDuration(Sanitizer.TIMESPAN_HOUR); michael@0: // Hide details michael@0: this.toggleDetails(); michael@0: this.checkDetails(false); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should not be open because "Last Hour" is selected michael@0: this.checkDetails(false); michael@0: michael@0: this.cancelDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: // Details should have remained closed michael@0: this.checkDetails(false); michael@0: michael@0: // Show details michael@0: this.toggleDetails(); michael@0: this.checkDetails(true); michael@0: this.cancelDialog(); michael@0: }; michael@0: wh.open(); michael@0: }, michael@0: function () { michael@0: // Test for offline cache deletion michael@0: michael@0: // Prepare stuff, we will work with www.example.com michael@0: var URL = "http://www.example.com"; michael@0: michael@0: var ios = Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Ci.nsIIOService); michael@0: var URI = ios.newURI(URL, null, null); michael@0: michael@0: var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] michael@0: .getService(Ci.nsIScriptSecurityManager); michael@0: var principal = sm.getNoAppCodebasePrincipal(URI); michael@0: michael@0: // Give www.example.com privileges to store offline data michael@0: var pm = Cc["@mozilla.org/permissionmanager;1"] michael@0: .getService(Ci.nsIPermissionManager); michael@0: pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION); michael@0: pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN); michael@0: michael@0: // Store something to the offline cache michael@0: const nsICache = Components.interfaces.nsICache; michael@0: var cs = Components.classes["@mozilla.org/network/cache-service;1"] michael@0: .getService(Components.interfaces.nsICacheService); michael@0: var session = cs.createSession(URL + "/manifest", nsICache.STORE_OFFLINE, nsICache.STREAM_BASED); michael@0: michael@0: // Open the dialog michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); michael@0: // Show details michael@0: this.toggleDetails(); michael@0: // Clear only offlineApps michael@0: this.uncheckAllCheckboxes(); michael@0: this.checkPrefCheckbox("offlineApps", true); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: // Check if the cache has been deleted michael@0: var size = -1; michael@0: var visitor = { michael@0: visitDevice: function (deviceID, deviceInfo) michael@0: { michael@0: if (deviceID == "offline") michael@0: size = deviceInfo.totalSize; michael@0: michael@0: // Do not enumerate entries michael@0: return false; michael@0: }, michael@0: michael@0: visitEntry: function (deviceID, entryInfo) michael@0: { michael@0: // Do not enumerate entries. michael@0: return false; michael@0: } michael@0: }; michael@0: cs.visitEntries(visitor); michael@0: is(size, 0, "offline application cache entries evicted"); michael@0: }; michael@0: michael@0: var cacheListener = { michael@0: onCacheEntryAvailable: function (entry, access, status) { michael@0: is(status, Cr.NS_OK); michael@0: var stream = entry.openOutputStream(0); michael@0: var content = "content"; michael@0: stream.write(content, content.length); michael@0: stream.close(); michael@0: entry.close(); michael@0: wh.open(); michael@0: } michael@0: }; michael@0: michael@0: session.asyncOpenCacheEntry(URL, nsICache.ACCESS_READ_WRITE, cacheListener); michael@0: }, michael@0: function () { michael@0: // Test for offline apps permission deletion michael@0: michael@0: // Prepare stuff, we will work with www.example.com michael@0: var URL = "http://www.example.com"; michael@0: michael@0: var ios = Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Ci.nsIIOService); michael@0: var URI = ios.newURI(URL, null, null); michael@0: michael@0: var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] michael@0: .getService(Ci.nsIScriptSecurityManager); michael@0: var principal = sm.getNoAppCodebasePrincipal(URI); michael@0: michael@0: // Open the dialog michael@0: let wh = new WindowHelper(); michael@0: wh.onload = function () { michael@0: this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); michael@0: // Show details michael@0: this.toggleDetails(); michael@0: // Clear only offlineApps michael@0: this.uncheckAllCheckboxes(); michael@0: this.checkPrefCheckbox("siteSettings", true); michael@0: this.acceptDialog(); michael@0: }; michael@0: wh.onunload = function () { michael@0: // Check all has been deleted (privileges, data, cache) michael@0: var pm = Cc["@mozilla.org/permissionmanager;1"] michael@0: .getService(Ci.nsIPermissionManager); michael@0: is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed"); michael@0: }; michael@0: wh.open(); michael@0: } michael@0: ]; michael@0: michael@0: // Index in gAllTests of the test currently being run. Incremented for each michael@0: // test run. See doNextTest(). michael@0: var gCurrTest = 0; michael@0: michael@0: let now_mSec = Date.now(); michael@0: let now_uSec = now_mSec * 1000; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: /** michael@0: * This wraps the dialog and provides some convenience methods for interacting michael@0: * with it. michael@0: * michael@0: * @param aWin michael@0: * The dialog's nsIDOMWindow michael@0: */ michael@0: function WindowHelper(aWin) { michael@0: this.win = aWin; michael@0: } michael@0: michael@0: WindowHelper.prototype = { michael@0: /** michael@0: * "Presses" the dialog's OK button. michael@0: */ michael@0: acceptDialog: function () { michael@0: is(this.win.document.documentElement.getButton("accept").disabled, false, michael@0: "Dialog's OK button should not be disabled"); michael@0: this.win.document.documentElement.acceptDialog(); michael@0: }, michael@0: michael@0: /** michael@0: * "Presses" the dialog's Cancel button. michael@0: */ michael@0: cancelDialog: function () { michael@0: this.win.document.documentElement.cancelDialog(); michael@0: }, michael@0: michael@0: /** michael@0: * Ensures that the details progressive disclosure button and the item list michael@0: * hidden by it match up. Also makes sure the height of the dialog is michael@0: * sufficient for the item list and warning panel. michael@0: * michael@0: * @param aShouldBeShown michael@0: * True if you expect the details to be shown and false if hidden michael@0: */ michael@0: checkDetails: function (aShouldBeShown) { michael@0: let button = this.getDetailsButton(); michael@0: let list = this.getItemList(); michael@0: let hidden = list.hidden || list.collapsed; michael@0: is(hidden, !aShouldBeShown, michael@0: "Details should be " + (aShouldBeShown ? "shown" : "hidden") + michael@0: " but were actually " + (hidden ? "hidden" : "shown")); michael@0: let dir = hidden ? "down" : "up"; michael@0: is(button.className, "expander-" + dir, michael@0: "Details button should be " + dir + " because item list is " + michael@0: (hidden ? "" : "not ") + "hidden"); michael@0: let height = 0; michael@0: if (!hidden) { michael@0: ok(list.boxObject.height > 30, "listbox has sufficient size") michael@0: height += list.boxObject.height; michael@0: } michael@0: if (this.isWarningPanelVisible()) michael@0: height += this.getWarningPanel().boxObject.height; michael@0: ok(height < this.win.innerHeight, michael@0: "Window should be tall enough to fit warning panel and item list"); michael@0: }, michael@0: michael@0: /** michael@0: * (Un)checks a history scope checkbox (browser & download history, michael@0: * form history, etc.). michael@0: * michael@0: * @param aPrefName michael@0: * The final portion of the checkbox's privacy.cpd.* preference name michael@0: * @param aCheckState michael@0: * True if the checkbox should be checked, false otherwise michael@0: */ michael@0: checkPrefCheckbox: function (aPrefName, aCheckState) { michael@0: var pref = "privacy.cpd." + aPrefName; michael@0: var cb = this.win.document.querySelectorAll( michael@0: "#itemList > [preference='" + pref + "']"); michael@0: is(cb.length, 1, "found checkbox for " + pref + " preference"); michael@0: if (cb[0].checked != aCheckState) michael@0: cb[0].click(); michael@0: }, michael@0: michael@0: /** michael@0: * Makes sure all the checkboxes are checked. michael@0: */ michael@0: _checkAllCheckboxesCustom: function (check) { michael@0: var cb = this.win.document.querySelectorAll("#itemList > [preference]"); michael@0: ok(cb.length > 1, "found checkboxes for preferences"); michael@0: for (var i = 0; i < cb.length; ++i) { michael@0: var pref = this.win.document.getElementById(cb[i].getAttribute("preference")); michael@0: if (!!pref.value ^ check) michael@0: cb[i].click(); michael@0: } michael@0: }, michael@0: michael@0: checkAllCheckboxes: function () { michael@0: this._checkAllCheckboxesCustom(true); michael@0: }, michael@0: michael@0: uncheckAllCheckboxes: function () { michael@0: this._checkAllCheckboxesCustom(false); michael@0: }, michael@0: michael@0: /** michael@0: * @return The details progressive disclosure button michael@0: */ michael@0: getDetailsButton: function () { michael@0: return this.win.document.getElementById("detailsExpander"); michael@0: }, michael@0: michael@0: /** michael@0: * @return The dialog's duration dropdown michael@0: */ michael@0: getDurationDropdown: function () { michael@0: return this.win.document.getElementById("sanitizeDurationChoice"); michael@0: }, michael@0: michael@0: /** michael@0: * @return The item list hidden by the details progressive disclosure button michael@0: */ michael@0: getItemList: function () { michael@0: return this.win.document.getElementById("itemList"); michael@0: }, michael@0: michael@0: /** michael@0: * @return The clear-everything warning box michael@0: */ michael@0: getWarningPanel: function () { michael@0: return this.win.document.getElementById("sanitizeEverythingWarningBox"); michael@0: }, michael@0: michael@0: /** michael@0: * @return True if the "Everything" warning panel is visible (as opposed to michael@0: * the tree) michael@0: */ michael@0: isWarningPanelVisible: function () { michael@0: return !this.getWarningPanel().hidden; michael@0: }, michael@0: michael@0: /** michael@0: * Opens the clear recent history dialog. Before calling this, set michael@0: * this.onload to a function to execute onload. It should close the dialog michael@0: * when done so that the tests may continue. Set this.onunload to a function michael@0: * to execute onunload. this.onunload is optional. If it returns true, the michael@0: * caller is expected to call waitForAsyncUpdates at some point; if false is michael@0: * returned, waitForAsyncUpdates is called automatically. michael@0: */ michael@0: open: function () { michael@0: let wh = this; michael@0: michael@0: function windowObserver(aSubject, aTopic, aData) { michael@0: if (aTopic != "domwindowopened") michael@0: return; michael@0: michael@0: Services.ww.unregisterNotification(windowObserver); michael@0: michael@0: var loaded = false; michael@0: let win = aSubject.QueryInterface(Ci.nsIDOMWindow); michael@0: michael@0: win.addEventListener("load", function onload(event) { michael@0: win.removeEventListener("load", onload, false); michael@0: michael@0: if (win.name !== "SanitizeDialog") michael@0: return; michael@0: michael@0: wh.win = win; michael@0: loaded = true; michael@0: michael@0: executeSoon(function () { michael@0: // Some exceptions that reach here don't reach the test harness, but michael@0: // ok()/is() do... michael@0: try { michael@0: wh.onload(); michael@0: } michael@0: catch (exc) { michael@0: win.close(); michael@0: ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); michael@0: finish(); michael@0: } michael@0: }); michael@0: }, false); michael@0: michael@0: win.addEventListener("unload", function onunload(event) { michael@0: if (win.name !== "SanitizeDialog") { michael@0: win.removeEventListener("unload", onunload, false); michael@0: return; michael@0: } michael@0: michael@0: // Why is unload fired before load? michael@0: if (!loaded) michael@0: return; michael@0: michael@0: win.removeEventListener("unload", onunload, false); michael@0: wh.win = win; michael@0: michael@0: executeSoon(function () { michael@0: // Some exceptions that reach here don't reach the test harness, but michael@0: // ok()/is() do... michael@0: try { michael@0: if (wh.onunload) { michael@0: Task.spawn(wh.onunload).then(function() { michael@0: waitForAsyncUpdates(doNextTest); michael@0: }).then(null, Components.utils.reportError); michael@0: } else { michael@0: waitForAsyncUpdates(doNextTest); michael@0: } michael@0: } michael@0: catch (exc) { michael@0: win.close(); michael@0: ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); michael@0: finish(); michael@0: } michael@0: }); michael@0: }, false); michael@0: } michael@0: Services.ww.registerNotification(windowObserver); michael@0: Services.ww.openWindow(null, michael@0: "chrome://browser/content/sanitize.xul", michael@0: "SanitizeDialog", michael@0: "chrome,titlebar,dialog,centerscreen,modal", michael@0: null); michael@0: }, michael@0: michael@0: /** michael@0: * Selects a duration in the duration dropdown. michael@0: * michael@0: * @param aDurVal michael@0: * One of the Sanitizer.TIMESPAN_* values michael@0: */ michael@0: selectDuration: function (aDurVal) { michael@0: this.getDurationDropdown().value = aDurVal; michael@0: if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { michael@0: is(this.isWarningPanelVisible(), true, michael@0: "Warning panel should be visible for TIMESPAN_EVERYTHING"); michael@0: } michael@0: else { michael@0: is(this.isWarningPanelVisible(), false, michael@0: "Warning panel should not be visible for non-TIMESPAN_EVERYTHING"); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Toggles the details progressive disclosure button. michael@0: */ michael@0: toggleDetails: function () { michael@0: this.getDetailsButton().click(); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Adds a download to history. michael@0: * michael@0: * @param aMinutesAgo michael@0: * The download will be downloaded this many minutes ago michael@0: */ michael@0: function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { michael@0: let publicList = yield Downloads.getList(Downloads.PUBLIC); michael@0: michael@0: let name = "fakefile-" + aMinutesAgo + "-minutes-ago"; michael@0: let download = yield Downloads.createDownload({ michael@0: source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", michael@0: target: name michael@0: }); michael@0: download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin)); michael@0: download.canceled = true; michael@0: publicList.add(download); michael@0: michael@0: ok((yield downloadExists(name)), michael@0: "Sanity check: download " + name + michael@0: " should exist after creating it"); michael@0: michael@0: aExpectedPathList.push(name); michael@0: } michael@0: michael@0: /** michael@0: * Adds a form entry to history. michael@0: * michael@0: * @param aMinutesAgo michael@0: * The entry will be added this many minutes ago michael@0: */ michael@0: function addFormEntryWithMinutesAgo(then, aMinutesAgo) { michael@0: let name = aMinutesAgo + "-minutes-ago"; michael@0: michael@0: // Artifically age the entry to the proper vintage. michael@0: let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin); michael@0: michael@0: FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp }, michael@0: { handleError: function (error) { michael@0: do_throw("Error occurred updating form history: " + error); michael@0: }, michael@0: handleCompletion: function (reason) { then.next(); } michael@0: }); michael@0: return name; michael@0: } michael@0: michael@0: /** michael@0: * Checks if a form entry exists. michael@0: */ michael@0: function formNameExists(name) michael@0: { michael@0: let deferred = Promise.defer(); michael@0: michael@0: let count = 0; michael@0: FormHistory.count({ fieldname: name }, michael@0: { handleResult: function (result) count = result, michael@0: handleError: function (error) { michael@0: do_throw("Error occurred searching form history: " + error); michael@0: deferred.reject(error); michael@0: }, michael@0: handleCompletion: function (reason) { michael@0: if (!reason) deferred.resolve(count); michael@0: } michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Removes all history visits, downloads, and form entries. michael@0: */ michael@0: function blankSlate() { michael@0: PlacesUtils.bhistory.removeAllPages(); michael@0: michael@0: // The promise is resolved only when removing both downloads and form history are done. michael@0: let deferred = Promise.defer(); michael@0: let formHistoryDone = false, downloadsDone = false; michael@0: michael@0: Task.spawn(function deleteAllDownloads() { michael@0: let publicList = yield Downloads.getList(Downloads.PUBLIC); michael@0: let downloads = yield publicList.getAll(); michael@0: for (let download of downloads) { michael@0: yield publicList.remove(download); michael@0: yield download.finalize(true); michael@0: } michael@0: downloadsDone = true; michael@0: if (formHistoryDone) { michael@0: deferred.resolve(); michael@0: } michael@0: }).then(null, Components.utils.reportError); michael@0: michael@0: FormHistory.update({ op: "remove" }, michael@0: { handleError: function (error) { michael@0: do_throw("Error occurred updating form history: " + error); michael@0: deferred.reject(error); michael@0: }, michael@0: handleCompletion: function (reason) { michael@0: if (!reason) { michael@0: formHistoryDone = true; michael@0: if (downloadsDone) { michael@0: deferred.resolve(); michael@0: } michael@0: } michael@0: } michael@0: }); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Ensures that the given pref is the expected value. michael@0: * michael@0: * @param aPrefName michael@0: * The pref's sub-branch under the privacy branch michael@0: * @param aExpectedVal michael@0: * The pref's expected value michael@0: * @param aMsg michael@0: * Passed to is() michael@0: */ michael@0: function boolPrefIs(aPrefName, aExpectedVal, aMsg) { michael@0: is(gPrefService.getBoolPref("privacy." + aPrefName), aExpectedVal, aMsg); michael@0: } michael@0: michael@0: /** michael@0: * Checks to see if the download with the specified path exists. michael@0: * michael@0: * @param aPath michael@0: * The path of the download to check michael@0: * @return True if the download exists, false otherwise michael@0: */ michael@0: function downloadExists(aPath) michael@0: { michael@0: return Task.spawn(function() { michael@0: let publicList = yield Downloads.getList(Downloads.PUBLIC); michael@0: let listArray = yield publicList.getAll(); michael@0: throw new Task.Result(listArray.some(i => i.target.path == aPath)); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Runs the next test in the gAllTests array. If all tests have been run, michael@0: * finishes the entire suite. michael@0: */ michael@0: function doNextTest() { michael@0: if (gAllTests.length <= gCurrTest) { michael@0: blankSlate(); michael@0: waitForAsyncUpdates(finish); michael@0: } michael@0: else { michael@0: let ct = gCurrTest; michael@0: gCurrTest++; michael@0: gAllTests[ct](); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Ensures that the specified downloads are either cleared or not. michael@0: * michael@0: * @param aDownloadIDs michael@0: * Array of download database IDs michael@0: * @param aShouldBeCleared michael@0: * True if each download should be cleared, false otherwise michael@0: */ michael@0: function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) { michael@0: let niceStr = aShouldBeCleared ? "no longer" : "still"; michael@0: aDownloadIDs.forEach(function (id) { michael@0: is((yield downloadExists(id)), !aShouldBeCleared, michael@0: "download " + id + " should " + niceStr + " exist"); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Ensures that the given pref is the expected value. michael@0: * michael@0: * @param aPrefName michael@0: * The pref's sub-branch under the privacy branch michael@0: * @param aExpectedVal michael@0: * The pref's expected value michael@0: * @param aMsg michael@0: * Passed to is() michael@0: */ michael@0: function intPrefIs(aPrefName, aExpectedVal, aMsg) { michael@0: is(gPrefService.getIntPref("privacy." + aPrefName), aExpectedVal, aMsg); michael@0: } michael@0: michael@0: /** michael@0: * Creates a visit time. michael@0: * michael@0: * @param aMinutesAgo michael@0: * The visit will be visited this many minutes ago michael@0: */ michael@0: function visitTimeForMinutesAgo(aMinutesAgo) { michael@0: return now_uSec - aMinutesAgo * kUsecPerMin; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: function test() { michael@0: requestLongerTimeout(2); michael@0: waitForExplicitFinish(); michael@0: blankSlate(); michael@0: // Kick off all the tests in the gAllTests array. michael@0: waitForAsyncUpdates(doNextTest); michael@0: }