From 3604ebbf0e5628b626c4b6e0803769635e215601 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 31 Aug 2015 22:03:20 -0400 Subject: [PATCH] Backed out changesets b2c46fdeca8b, 6b768986595f, and 78c891ba5de1 (bug 1089695) for frequent Marionette crashes. --- browser/base/content/browser.js | 52 ++ browser/base/content/sanitize.js | 532 +++++------ browser/base/content/sanitizeDialog.js | 26 +- .../browser_sanitize-passwordDisabledHosts.js | 12 +- .../browser_sanitize-sitepermissions.js | 6 +- .../test/general/browser_sanitizeDialog.js | 849 +++++++++--------- .../test/newtab/browser_newtab_bug722273.js | 51 +- browser/base/content/test/newtab/head.js | 60 +- browser/base/jar.mn | 2 +- browser/components/nsBrowserGlue.js | 5 +- .../tests/unit/test_clearHistory_shutdown.js | 14 +- browser/modules/Sanitizer.jsm | 22 - browser/modules/moz.build | 1 - toolkit/components/places/PlacesUtils.jsm | 55 +- 14 files changed, 794 insertions(+), 893 deletions(-) delete mode 100644 browser/modules/Sanitizer.jsm diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 978b63149b4..ff35ed857d6 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1283,6 +1283,9 @@ var gBrowserInit = { gBrowser.selectedBrowser.focus(); } + // Set up Sanitize Item + this._initializeSanitizer(); + // Enable/Disable auto-hide tabbar gBrowser.tabContainer.updateVisibility(); @@ -1667,6 +1670,9 @@ var gBrowserInit = { // initialise the offline listener BrowserOffline.init(); + // Set up Sanitize Item + this._initializeSanitizer(); + // initialize the private browsing UI gPrivateBrowsingUI.init(); @@ -1691,6 +1697,52 @@ var gBrowserInit = { BrowserOffline.uninit(); }, #endif + + _initializeSanitizer: function() { + const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize"; + if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) { + gPrefService.clearUserPref(kDidSanitizeDomain); + // We need to persist this preference change, since we want to + // check it at next app start even if the browser exits abruptly + gPrefService.savePrefFile(null); + } + + /** + * Migrate Firefox 3.0 privacy.item prefs under one of these conditions: + * + * a) User has customized any privacy.item prefs + * b) privacy.sanitize.sanitizeOnShutdown is set + */ + if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) { + let itemBranch = gPrefService.getBranch("privacy.item."); + let itemArray = itemBranch.getChildList(""); + + // See if any privacy.item prefs are set + let doMigrate = itemArray.some(function (name) itemBranch.prefHasUserValue(name)); + // Or if sanitizeOnShutdown is set + if (!doMigrate) + doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown"); + + if (doMigrate) { + let cpdBranch = gPrefService.getBranch("privacy.cpd."); + let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown."); + for (let name of itemArray) { + try { + // don't migrate password or offlineApps clearing in the CRH dialog since + // there's no UI for those anymore. They default to false. bug 497656 + if (name != "passwords" && name != "offlineApps") + cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name)); + clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name)); + } + catch(e) { + Cu.reportError("Exception thrown during privacy pref migration: " + e); + } + } + } + + gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true); + } + }, } diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index a0c0d29e8b4..153bfd651fe 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -1,12 +1,10 @@ -// -*- indent-tabs-mode: nil; js-indent-level: 4 -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +# -*- indent-tabs-mode: nil; js-indent-level: 4 -*- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", - "resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", @@ -21,21 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "console", - "resource://gre/modules/devtools/Console.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Preferences", - "resource://gre/modules/Preferences.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "setTimeout", - "resource://gre/modules/Timer.jsm"); -/** - * A number of iterations after which to yield time back - * to the system. - */ -const YIELD_PERIOD = 10; - -function Sanitizer() { -} +function Sanitizer() {} Sanitizer.prototype = { // warning to the caller: this one may raise an exception (e.g. bug #265028) clearItem: function (aItemName) @@ -44,14 +29,6 @@ Sanitizer.prototype = { this.items[aItemName].clear(); }, - promiseCanClearItem: function (aItemName, aArg) { - return new Promise(resolve => { - return this.canClearItem(aItemName, - (_, canClear) => resolve(canClear), - aArg) - }); - }, - canClearItem: function (aItemName, aCallback, aArg) { let canClear = this.items[aItemName].canClear; @@ -80,61 +57,15 @@ Sanitizer.prototype = { * If the consumer specifies the (optional) array parameter, only those * items get cleared (irrespective of the preference settings) */ - sanitize: Task.async(function*(aItemsToClear = null) { - let progress = {}; - let promise = this._sanitize(aItemsToClear, progress); - - // - // Depending on preferences, the sanitizer may perform asynchronous - // work before it starts cleaning up the Places database (e.g. closing - // windows). We need to make sure that the connection to that database - // hasn't been closed by the time we use it. - // - let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"] - .getService(Ci.nsPIPlacesDatabase) - .shutdownClient - .jsclient; - - shutdownClient.addBlocker("sanitize.js: Sanitize", - promise, - { - fetchState: () => { - return { progress }; - } - } - ); - try { - yield promise; - } finally { - Services.obs.notifyObservers(null, "sanitizer-sanitization-complete", ""); - } - }), - - _sanitize: Task.async(function*(aItemsToClear, progress = {}) { - let seenError = false; - let itemsToClear; + sanitize: function (aItemsToClear) + { + var deferred = Promise.defer(); + var seenError = false; if (Array.isArray(aItemsToClear)) { - // Shallow copy the array, as we are going to modify - // it in place later. - itemsToClear = [...aItemsToClear]; + var itemsToClear = [...aItemsToClear]; } else { let branch = Services.prefs.getBranch(this.prefDomain); - itemsToClear = Object.keys(this.items).filter(itemName => { - try { - return branch.getBoolPref(itemName); - } catch (ex) { - return false; - } - }); - } - - // Store the list of items to clear, in case we are killed before we - // get a chance to complete. - Preferences.set(Sanitizer.PREF_SANITIZE_IN_PROGRESS, JSON.stringify(itemsToClear)); - - // Store the list of items to clear, for debugging/forensics purposes - for (let k of itemsToClear) { - progress[k] = "ready"; + itemsToClear = Object.keys(this.items).filter(itemName => branch.getBoolPref(itemName)); } // Ensure open windows get cleared first, if they're in our list, so that they don't stick @@ -143,56 +74,98 @@ Sanitizer.prototype = { let openWindowsIndex = itemsToClear.indexOf("openWindows"); if (openWindowsIndex != -1) { itemsToClear.splice(openWindowsIndex, 1); - yield this.items.openWindows.clear(); - progress.openWindows = "cleared"; + let item = this.items.openWindows; + + let ok = item.clear(() => { + try { + let clearedPromise = this.sanitize(itemsToClear); + clearedPromise.then(deferred.resolve, deferred.reject); + } catch(e) { + let error = "Sanitizer threw after closing windows: " + e; + Cu.reportError(error); + deferred.reject(error); + } + }); + // When cancelled, reject immediately + if (!ok) { + deferred.reject("Sanitizer canceled closing windows"); + } + + return deferred.promise; } + let cookiesIndex = itemsToClear.indexOf("cookies"); + if (cookiesIndex != -1) { + itemsToClear.splice(cookiesIndex, 1); + let item = this.items.cookies; + item.range = this.range; + let ok = item.clear(() => { + try { + if (!itemsToClear.length) { + // we're done + deferred.resolve(); + return; + } + let clearedPromise = this.sanitize(itemsToClear); + clearedPromise.then(deferred.resolve, deferred.reject); + } catch(e) { + let error = "Sanitizer threw after clearing cookies: " + e; + Cu.reportError(error); + deferred.reject(error); + } + }); + // When cancelled, reject immediately + if (!ok) { + deferred.reject("Sanitizer canceled clearing cookies"); + } + + return deferred.promise; + } + + TelemetryStopwatch.start("FX_SANITIZE_TOTAL"); + // Cache the range of times to clear - let range = null; - // If we ignore timespan, clear everything, - // otherwise, pick a range. - if (!this.ignoreTimespan) { + if (this.ignoreTimespan) + var range = null; // If we ignore timespan, clear everything + else range = this.range || Sanitizer.getClearRange(); - } + let itemCount = Object.keys(itemsToClear).length; + let onItemComplete = function() { + if (!--itemCount) { + TelemetryStopwatch.finish("FX_SANITIZE_TOTAL"); + seenError ? deferred.reject() : deferred.resolve(); + } + }; for (let itemName of itemsToClear) { let item = this.items[itemName]; - if (!("clear" in item)) { - progress[itemName] = "`clear` not in item"; - continue; - } item.range = range; - let canClear = yield this.promiseCanClearItem(itemName); - if (!canClear) { - progress[itemName] = "cannot clear item"; - continue; - } - // Some of these clear() may raise exceptions (see bug #265028) - // to sanitize as much as possible, we catch and store them, - // rather than fail fast. - // Callers should check returned errors and give user feedback - // about items that could not be sanitized - let refObj = {}; - try { - TelemetryStopwatch.start("FX_SANITIZE_TOTAL", refObj); - yield item.clear(); - progress[itemName] = "cleared"; - } catch(er) { - progress[itemName] = "failed"; - seenError = true; - console.error("Error sanitizing " + itemName, er); - } finally { - TelemetryStopwatch.finish("FX_SANITIZE_TOTAL", refObj); + if ("clear" in item) { + let clearCallback = (itemName, aCanClear) => { + // Some of these clear() may raise exceptions (see bug #265028) + // to sanitize as much as possible, we catch and store them, + // rather than fail fast. + // Callers should check returned errors and give user feedback + // about items that could not be sanitized + let item = this.items[itemName]; + try { + if (aCanClear) + item.clear(); + } catch(er) { + seenError = true; + Components.utils.reportError("Error sanitizing " + itemName + + ": " + er + "\n"); + } + onItemComplete(); + }; + this.canClearItem(itemName, clearCallback); + } else { + onItemComplete(); } } - // Sanitization is complete. - Preferences.reset(Sanitizer.PREF_SANITIZE_IN_PROGRESS); - progress = {}; - if (seenError) { - throw new Error("Error sanitizing"); - } - }), + return deferred.promise; + }, // Time span only makes sense in certain cases. Consumers who want // to only clear some private data can opt in by setting this to false, @@ -206,8 +179,7 @@ Sanitizer.prototype = { cache: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_CACHE", refObj); + TelemetryStopwatch.start("FX_SANITIZE_CACHE"); var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]. getService(Ci.nsICacheStorageService); @@ -223,7 +195,7 @@ Sanitizer.prototype = { imageCache.clearCache(false); // true=chrome, false=content } catch(er) {} - TelemetryStopwatch.finish("FX_SANITIZE_CACHE", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_CACHE"); }, get canClear() @@ -233,12 +205,10 @@ Sanitizer.prototype = { }, cookies: { - clear: Task.async(function* () + clear: function (aCallback) { - let yieldCounter = 0; - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_COOKIES", refObj); - TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2", refObj); + TelemetryStopwatch.start("FX_SANITIZE_COOKIES"); + TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2"); var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"] .getService(Ci.nsICookieManager); @@ -248,22 +218,17 @@ Sanitizer.prototype = { while (cookiesEnum.hasMoreElements()) { var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2); - if (cookie.creationTime > this.range[0]) { + if (cookie.creationTime > this.range[0]) // This cookie was created after our cutoff, clear it cookieMgr.remove(cookie.host, cookie.name, cookie.path, false); - - if (++yieldCounter % YIELD_PERIOD == 0) { - yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long - } - } } } else { // Remove everything cookieMgr.removeAll(); - yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long } - TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2", refObj); + + TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2"); // Clear deviceIds. Done asynchronously (returns before complete). let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"] @@ -271,13 +236,17 @@ Sanitizer.prototype = { mediaMgr.sanitizeDeviceIds(this.range && this.range[0]); // Clear plugin data. - TelemetryStopwatch.start("FX_SANITIZE_PLUGINS", refObj); - yield this.promiseClearPluginCookies(); - TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS", refObj); - TelemetryStopwatch.finish("FX_SANITIZE_COOKIES", refObj); - }), + TelemetryStopwatch.start("FX_SANITIZE_PLUGINS"); + this.clearPluginCookies().then( + function() { + TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS"); + TelemetryStopwatch.finish("FX_SANITIZE_COOKIES"); + aCallback(); + }); + return true; + }, - promiseClearPluginCookies: Task.async(function*() { + clearPluginCookies: function() { const phInterface = Ci.nsIPluginHost; const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL; let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface); @@ -289,23 +258,33 @@ Sanitizer.prototype = { let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000) : -1; if (!this.range || age >= 0) { let tags = ph.getPluginTags(); - for (let tag of tags) { - try { - let rv = yield new Promise(resolve => - ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, resolve) - ); - // If the plugin doesn't support clearing by age, clear everything. - if (rv == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) { - yield new Promise(resolve => - ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve) - ); + function iterate(tag) { + let promise = new Promise(resolve => { + try { + let onClear = function(rv) { + // If the plugin doesn't support clearing by age, clear everything. + if (rv == Components.results. NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) { + ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, function() { + resolve(); + }); + } else { + resolve(); + } + }; + ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, onClear); + } catch (ex) { + resolve(); } - } catch (ex) { - // Ignore errors from plug-ins - } + }); + return promise; } + let promises = []; + for (let tag of tags) { + promises.push(iterate(tag)); + } + return Promise.all(promises); } - }), + }, get canClear() { @@ -316,11 +295,10 @@ Sanitizer.prototype = { offlineApps: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_OFFLINEAPPS", refObj); + TelemetryStopwatch.start("FX_SANITIZE_OFFLINEAPPS"); Components.utils.import("resource:///modules/offlineAppCache.jsm"); OfflineAppCacheHelper.clear(); - TelemetryStopwatch.finish("FX_SANITIZE_OFFLINEAPPS", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_OFFLINEAPPS"); }, get canClear() @@ -330,37 +308,31 @@ Sanitizer.prototype = { }, history: { - clear: Task.async(function* () + clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_HISTORY", refObj); + TelemetryStopwatch.start("FX_SANITIZE_HISTORY"); + + if (this.range) + PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]); + else + PlacesUtils.history.removeAllPages(); + try { - if (this.range) { - yield PlacesUtils.history.removeVisitsByFilter({ - beginDate: new Date(this.range[0] / 1000), - endDate: new Date(this.range[1] / 1000) - }); - } else { - // Remove everything. - yield PlacesUtils.history.clear(); - } - - try { - let clearStartingTime = this.range ? String(this.range[0]) : ""; - Services.obs.notifyObservers(null, "browser:purge-session-history", clearStartingTime); - } catch (e) { } - - try { - let predictor = Components.classes["@mozilla.org/network/predictor;1"] - .getService(Components.interfaces.nsINetworkPredictor); - predictor.reset(); - } catch (e) { - console.error("Error while resetting the predictor", e); - } - } finally { - TelemetryStopwatch.finish("FX_SANITIZE_HISTORY", refObj); + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + let clearStartingTime = this.range ? String(this.range[0]) : ""; + os.notifyObservers(null, "browser:purge-session-history", clearStartingTime); } - }), + catch (e) { } + + try { + var predictor = Components.classes["@mozilla.org/network/predictor;1"] + .getService(Components.interfaces.nsINetworkPredictor); + predictor.reset(); + } catch (e) { } + + TelemetryStopwatch.finish("FX_SANITIZE_HISTORY"); + }, get canClear() { @@ -373,8 +345,7 @@ Sanitizer.prototype = { formdata: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj); + TelemetryStopwatch.start("FX_SANITIZE_FORMDATA"); // Clear undo history of all searchBars var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'] @@ -401,7 +372,7 @@ Sanitizer.prototype = { } FormHistory.update(change); - TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA"); }, canClear : function(aCallback, aArg) @@ -448,8 +419,7 @@ Sanitizer.prototype = { downloads: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS", refObj); + TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS"); Task.spawn(function () { let filterByTime = null; if (this.range) { @@ -463,9 +433,9 @@ Sanitizer.prototype = { // Clear all completed/cancelled downloads let list = yield Downloads.getList(Downloads.ALL); list.removeFinished(filterByTime); - TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS"); }.bind(this)).then(null, error => { - TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS"); Components.utils.reportError(error); }); }, @@ -480,8 +450,7 @@ Sanitizer.prototype = { sessions: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_SESSIONS", refObj); + TelemetryStopwatch.start("FX_SANITIZE_SESSIONS"); // clear all auth tokens var sdr = Components.classes["@mozilla.org/security/sdr;1"] @@ -493,7 +462,7 @@ Sanitizer.prototype = { .getService(Components.interfaces.nsIObserverService); os.notifyObservers(null, "net:clear-active-logins", null); - TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS"); }, get canClear() @@ -505,8 +474,7 @@ Sanitizer.prototype = { siteSettings: { clear: function () { - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS", refObj); + TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS"); // Clear site-specific permissions like "Allow this site to open popups" // we ignore the "end" range and hope it is now() - none of the @@ -556,7 +524,7 @@ Sanitizer.prototype = { dump("Web Push may not be available.\n"); } - TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS", refObj); + TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS"); }, get canClear() @@ -591,10 +559,15 @@ Sanitizer.prototype = { win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow(); } }, - clear: Task.async(function*() { + clear: function(aCallback) + { // NB: this closes all *browser* windows, not other windows like the library, about window, // browser console, etc. + if (!aCallback) { + throw "Sanitizer's openWindows clear() requires a callback."; + } + // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload // dialogs let existingWindow = Services.appShell.hiddenDOMWindow; @@ -609,7 +582,7 @@ Sanitizer.prototype = { // If someone says "no" to a beforeunload prompt, we abort here: if (!this._canCloseWindow(someWin)) { this._resetAllWindowClosures(windowList); - throw new Error("Sanitize could not close windows: cancelled by user"); + return false; } // ...however, beforeunload prompts spin the event loop, and so the code here won't get @@ -618,14 +591,13 @@ Sanitizer.prototype = { // 'forget', and the timespans will be all wrong by now anyway: if (existingWindow.performance.now() > (startDate + 60 * 1000)) { this._resetAllWindowClosures(windowList); - throw new Error("Sanitize could not close windows: timeout"); + return false; } } // If/once we get here, we should actually be able to close all windows. - let refObj = {}; - TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj); + TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS"); // First create a new window. We do this first so that on non-mac, we don't // accidentally close the app by closing all the windows. @@ -635,58 +607,39 @@ Sanitizer.prototype = { let newWindow = existingWindow.openDialog("chrome://browser/content/", "_blank", features, defaultArgs); - if (AppConstants.platform == "macosx") { - let onFullScreen = function(e) { - newWindow.removeEventListener("fullscreen", onFullScreen); - let docEl = newWindow.document.documentElement; - let sizemode = docEl.getAttribute("sizemode"); - if (!newWindow.fullScreen && sizemode == "fullscreen") { - docEl.setAttribute("sizemode", "normal"); - e.preventDefault(); - e.stopPropagation(); - return false; - } + // Window creation and destruction is asynchronous. We need to wait + // until all existing windows are fully closed, and the new window is + // fully open, before continuing. Otherwise the rest of the sanitizer + // could run too early (and miss new cookies being set when a page + // closes) and/or run too late (and not have a fully-formed window yet + // in existence). See bug 1088137. + let newWindowOpened = false; + function onWindowOpened(subject, topic, data) { + if (subject != newWindow) + return; + + Services.obs.removeObserver(onWindowOpened, "browser-delayed-startup-finished"); + newWindowOpened = true; + // If we're the last thing to happen, invoke callback. + if (numWindowsClosing == 0) { + TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS"); + aCallback(); } - newWindow.addEventListener("fullscreen", onFullScreen); } - let promiseReady = new Promise(resolve => { - // Window creation and destruction is asynchronous. We need to wait - // until all existing windows are fully closed, and the new window is - // fully open, before continuing. Otherwise the rest of the sanitizer - // could run too early (and miss new cookies being set when a page - // closes) and/or run too late (and not have a fully-formed window yet - // in existence). See bug 1088137. - let newWindowOpened = false; - function onWindowOpened(subject, topic, data) { - if (subject != newWindow) - return; - - Services.obs.removeObserver(onWindowOpened, "browser-delayed-startup-finished"); - if (AppConstants.platform == "macosx") { - newWindow.removeEventListener("fullscreen", onFullScreen); - } - newWindowOpened = true; + let numWindowsClosing = windowList.length; + function onWindowClosed() { + numWindowsClosing--; + if (numWindowsClosing == 0) { + Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed"); // If we're the last thing to happen, invoke callback. - if (numWindowsClosing == 0) { - TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj); - resolve(); + if (newWindowOpened) { + TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS"); + aCallback(); } } + } - let numWindowsClosing = windowList.length; - function onWindowClosed() { - numWindowsClosing--; - if (numWindowsClosing == 0) { - Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed"); - // If we're the last thing to happen, invoke callback. - if (newWindowOpened) { - TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj); - resolve(); - } - } - } - }); Services.obs.addObserver(onWindowOpened, "browser-delayed-startup-finished", false); Services.obs.addObserver(onWindowClosed, "xul-window-destroyed", false); @@ -695,8 +648,8 @@ Sanitizer.prototype = { windowList.pop().close(); } newWindow.focus(); - yield promiseReady; - }), + return true; + }, get canClear() { @@ -709,10 +662,9 @@ Sanitizer.prototype = { // "Static" members -Sanitizer.PREF_DOMAIN = "privacy.sanitize."; -Sanitizer.PREF_SANITIZE_ON_SHUTDOWN = "privacy.sanitize.sanitizeOnShutdown"; -Sanitizer.PREF_SANITIZE_IN_PROGRESS = "privacy.sanitize.sanitizeInProgress"; -Sanitizer.PREF_SANITIZE_DID_SHUTDOWN = "privacy.sanitize.didShutdownSanitize"; +Sanitizer.prefDomain = "privacy.sanitize."; +Sanitizer.prefShutdown = "sanitizeOnShutdown"; +Sanitizer.prefDidShutdown = "didShutdownSanitize"; // Time span constants corresponding to values of the privacy.sanitize.timeSpan // pref. Used to determine how much history to clear, for various items @@ -771,7 +723,7 @@ Sanitizer.__defineGetter__("prefs", function() return Sanitizer._prefs ? Sanitizer._prefs : Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService) - .getBranch(Sanitizer.PREF_DOMAIN); + .getBranch(Sanitizer.prefDomain); }); // Shows sanitization UI @@ -779,10 +731,11 @@ Sanitizer.showUI = function(aParentWindow) { var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); - let win = AppConstants.platform == "macosx" ? - null: // make this an app-modal window on Mac - aParentWindow; - ww.openWindow(win, +#ifdef XP_MACOSX + ww.openWindow(null, // make this an app-modal window on Mac +#else + ww.openWindow(aParentWindow, +#endif "chrome://browser/content/sanitize.xul", "Sanitize", "chrome,titlebar,dialog,centerscreen,modal", @@ -798,15 +751,24 @@ Sanitizer.sanitize = function(aParentWindow) Sanitizer.showUI(aParentWindow); }; -Sanitizer.onStartup = Task.async(function*() { - // Make sure that we are triggered during shutdown, at the right time. - let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"] - .getService(Ci.nsPIPlacesDatabase) - .shutdownClient - .jsclient; +Sanitizer.onStartup = function() +{ + // we check for unclean exit with pending sanitization + Sanitizer._checkAndSanitize(); +}; - shutdownClient.addBlocker("sanitize.js: Sanitize on shutdown", - () => Sanitizer.onShutdown()); +Sanitizer.onShutdown = function() +{ + // we check if sanitization is needed and perform it + Sanitizer._checkAndSanitize(); +}; + +// this is called on startup and shutdown, to perform pending sanitizations +Sanitizer._checkAndSanitize = function() +{ + const prefs = Sanitizer.prefs; + if (prefs.getBoolPref(Sanitizer.prefShutdown) && + !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) { // One time migration to remove support for the clear saved passwords on exit feature. if (!Services.prefs.getBoolPref("privacy.sanitize.migrateClearSavedPwdsOnExit")) { @@ -819,31 +781,13 @@ Sanitizer.onStartup = Task.async(function*() { } Services.prefs.clearUserPref(deprecatedPref); Services.prefs.setBoolPref("privacy.sanitize.migrateClearSavedPwdsOnExit", true); - } + } - // Handle incomplete sanitizations - if (Preferences.has(Sanitizer.PREF_SANITIZE_IN_PROGRESS)) { - // Firefox crashed during sanitization. - let s = new Sanitizer(); - let json = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS); - let itemsToClear = JSON.parse(json); - yield s.sanitize(itemsToClear); + // this is a shutdown or a startup after an unclean exit + var s = new Sanitizer(); + s.prefDomain = "privacy.clearOnShutdown."; + s.sanitize().then(function() { + prefs.setBoolPref(Sanitizer.prefDidShutdown, true); + }); } - if (Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN)) { - // Firefox crashed before having a chance to sanitize during shutdown. - // (note that if Firefox crashed during shutdown sanitization, we - // will hit both `if` so we will run a second double-sanitization). - yield Sanitizer.onShutdown(); - } -}); - -Sanitizer.onShutdown = Task.async(function*() { - if (!Preferences.get(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN)) { - return; - } - // Need to sanitize upon shutdown - let s = new Sanitizer(); - s.prefDomain = "privacy.clearOnShutdown."; - yield s.sanitize(); - Preferences.set(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN, true); -}); +}; diff --git a/browser/base/content/sanitizeDialog.js b/browser/base/content/sanitizeDialog.js index 77ff794864e..538b179cab6 100644 --- a/browser/base/content/sanitizeDialog.js +++ b/browser/base/content/sanitizeDialog.js @@ -5,9 +5,6 @@ const Cc = Components.classes; const Ci = Components.interfaces; -const Cu = Components.utils; - -let {Sanitizer} = Cu.import("resource:///modules/Sanitizer.jsm", {}); var gSanitizePromptDialog = { @@ -46,20 +43,17 @@ var gSanitizePromptDialog = { var s = new Sanitizer(); s.prefDomain = "privacy.cpd."; - let tasks = []; let sanitizeItemList = document.querySelectorAll("#itemList > [preference]"); for (let i = 0; i < sanitizeItemList.length; i++) { let prefItem = sanitizeItemList[i]; let name = s.getNameFromPreference(prefItem.getAttribute("preference")); - let promise = s.promiseCanClearItem(name).then(canClear => { - if (canClear) { - return; + s.canClearItem(name, function canClearCallback(aItem, aCanClear, aPrefItem) { + if (!aCanClear) { + aPrefItem.preference = null; + aPrefItem.checked = false; + aPrefItem.disabled = true; } - prefItem.preference = null; - prefItem.checked = false; - prefItem.disabled = true; - }); - tasks.push(promise); + }, prefItem); } document.documentElement.getButton("accept").label = @@ -73,10 +67,6 @@ var gSanitizePromptDialog = { } else this.warningBox.hidden = true; - - Promise.all(tasks).then(() => { - Services.obs.notifyObservers(null, "sanitize-dialog-setup-complete", ""); - }); }, selectByTimespan: function () @@ -129,7 +119,6 @@ var gSanitizePromptDialog = { acceptButton.setAttribute("label", this.bundleBrowser.getString("sanitizeButtonClearing")); docElt.getButton("cancel").disabled = true; - try { s.sanitize().then(null, Components.utils.reportError) .then(() => window.close()) @@ -138,6 +127,7 @@ var gSanitizePromptDialog = { Components.utils.reportError("Exception during sanitize: " + er); return true; // We *do* want to close immediately on error. } + return false; }, /** @@ -515,7 +505,7 @@ var gSanitizePromptDialog = { } try { - s.sanitize(); // We ignore the resulting Promise + s.sanitize(); } catch (er) { Components.utils.reportError("Exception during sanitize: " + er); } diff --git a/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js b/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js index 31dd30420f7..06cf2467e79 100644 --- a/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js +++ b/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js @@ -6,11 +6,13 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) .loadSubScript("chrome://browser/content/sanitize.js", tempScope); let Sanitizer = tempScope.Sanitizer; -add_task(function*() { +function test() { + var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); // Add a disabled host pwmgr.setLoginSavingEnabled("http://example.com", false); + // Sanity check is(pwmgr.getLoginSavingEnabled("http://example.com"), false, "example.com should be disabled for password saving since we haven't cleared that yet."); @@ -29,11 +31,11 @@ add_task(function*() { itemPrefs.setBoolPref("passwords", false); itemPrefs.setBoolPref("sessions", false); itemPrefs.setBoolPref("siteSettings", true); - + // Clear it - yield s.sanitize(); - + s.sanitize(); + // Make sure it's gone is(pwmgr.getLoginSavingEnabled("http://example.com"), true, "example.com should be enabled for password saving again now that we've cleared."); -}); +} diff --git a/browser/base/content/test/general/browser_sanitize-sitepermissions.js b/browser/base/content/test/general/browser_sanitize-sitepermissions.js index 05802252b1f..0db2af14d23 100644 --- a/browser/base/content/test/general/browser_sanitize-sitepermissions.js +++ b/browser/base/content/test/general/browser_sanitize-sitepermissions.js @@ -15,7 +15,7 @@ function countPermissions() { return result; } -add_task(function* test() { +function test() { // sanitize before we start so we have a good baseline. // Set up the sanitizer to just clear siteSettings let s = new Sanitizer(); @@ -45,8 +45,8 @@ add_task(function* test() { ok(pm.enumerator.hasMoreElements(), "Permission manager should have elements, since we just added one"); // Clear it - yield s.sanitize(); + s.sanitize(); // Make sure it's gone is(numAtStart, countPermissions(), "Permission manager should have the same count it started with"); -}); +} diff --git a/browser/base/content/test/general/browser_sanitizeDialog.js b/browser/base/content/test/general/browser_sanitizeDialog.js index b533d7b7829..fa867128a4e 100644 --- a/browser/base/content/test/general/browser_sanitizeDialog.js +++ b/browser/base/content/test/general/browser_sanitizeDialog.js @@ -24,10 +24,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", "resource://gre/modules/FormHistory.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Timer", - "resource://gre/modules/Timer.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", - "resource://testing-common/PlacesTestUtils.jsm"); let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) @@ -37,108 +33,105 @@ let Sanitizer = tempScope.Sanitizer; const kMsecPerMin = 60 * 1000; const kUsecPerMin = 60 * 1000000; -add_task(function* init() { - requestLongerTimeout(2); - blankSlate(); - registerCleanupFunction(() => { - blankSlate(); - return PlacesTestUtils.promiseAsyncUpdates(); - }); -}); +let formEntries, downloadIDs, olderDownloadIDs; -/** - * Initializes the dialog to its default state. - */ -add_task(function* default_state() { - let wh = new WindowHelper(); - wh.onload = function () { - // Select "Last Hour" - this.selectDuration(Sanitizer.TIMESPAN_HOUR); - // Hide details - if (!this.getItemList().collapsed) - this.toggleDetails(); - this.acceptDialog(); - }; - wh.open(); - return wh.promiseClosed; -}); +// Add tests here. Each is a function that's called by doNextTest(). +var gAllTests = [ -/** - * Cancels the dialog, makes sure history not cleared. - */ -add_task(function* test_cancel() { - // Add history (within the past hour) - let uris = []; - let places = []; - let pURI; - for (let i = 0; i < 30; i++) { - pURI = makeURI("http://" + i + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); - uris.push(pURI); - } - - return new Promise(resolve => { - PlacesTestUtils.addVisits(places).then(() => { + /** + * Initializes the dialog to its default state. + */ + function () { let wh = new WindowHelper(); wh.onload = function () { + // Select "Last Hour" this.selectDuration(Sanitizer.TIMESPAN_HOUR); - this.checkPrefCheckbox("history", false); - this.checkDetails(false); - - // Show details - this.toggleDetails(); - this.checkDetails(true); - // Hide details - this.toggleDetails(); - this.checkDetails(false); - this.cancelDialog(); + if (!this.getItemList().collapsed) + this.toggleDetails(); + this.acceptDialog(); }; - wh.onunload = function* () { - yield promiseHistoryClearedState(uris, false); - yield blankSlate(); - yield promiseHistoryClearedState(uris, true); - }; - wh.promiseClosed.then(resolve); wh.open(); - })}); -}); + }, -/** - * Ensures that the combined history-downloads checkbox clears both history - * visits and downloads when checked; the dialog respects simple timespan. - */ -add_task(function* test_history_downloads_checked() { - // Add downloads (within the past hour). - let downloadIDs = []; - for (let i = 0; i < 5; i++) { - yield addDownloadWithMinutesAgo(downloadIDs, i); - } - // Add downloads (over an hour ago). - let olderDownloadIDs = []; - for (let i = 0; i < 5; i++) { - yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); - } + /** + * Cancels the dialog, makes sure history not cleared. + */ + function () { + // Add history (within the past hour) + let uris = []; + let places = []; + let pURI; + for (let i = 0; i < 30; i++) { + pURI = makeURI("http://" + i + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); + uris.push(pURI); + } - // Add history (within the past hour). - let uris = []; - let places = []; - let pURI; - for (let i = 0; i < 30; i++) { - pURI = makeURI("http://" + i + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); - uris.push(pURI); - } - // Add history (over an hour ago). - let olderURIs = []; - for (let i = 0; i < 5; i++) { - pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)}); - olderURIs.push(pURI); - } - let promiseSanitized = promiseSanitizationComplete(); + PlacesTestUtils.addVisits(places).then(() => { + let wh = new WindowHelper(); + wh.onload = function () { + this.selectDuration(Sanitizer.TIMESPAN_HOUR); + this.checkPrefCheckbox("history", false); + this.checkDetails(false); + + // Show details + this.toggleDetails(); + this.checkDetails(true); + + // Hide details + this.toggleDetails(); + this.checkDetails(false); + this.cancelDialog(); + }; + wh.onunload = function () { + yield promiseHistoryClearedState(uris, false); + yield blankSlate(); + yield promiseHistoryClearedState(uris, true); + }; + wh.open(); + }); + }, + + function () { + // Add downloads (within the past hour). + Task.spawn(function () { + downloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(downloadIDs, i); + } + // Add downloads (over an hour ago). + olderDownloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); + } + + doNextTest(); + }).then(null, Components.utils.reportError); + }, + + /** + * Ensures that the combined history-downloads checkbox clears both history + * visits and downloads when checked; the dialog respects simple timespan. + */ + function () { + // Add history (within the past hour). + let uris = []; + let places = []; + let pURI; + for (let i = 0; i < 30; i++) { + pURI = makeURI("http://" + i + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); + uris.push(pURI); + } + // Add history (over an hour ago). + let olderURIs = []; + for (let i = 0; i < 5; i++) { + pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)}); + olderURIs.push(pURI); + } - return new Promise(resolve => { PlacesTestUtils.addVisits(places).then(() => { let totalHistoryVisits = uris.length + olderURIs.length; @@ -148,7 +141,7 @@ add_task(function* test_history_downloads_checked() { this.checkPrefCheckbox("history", true); this.acceptDialog(); }; - wh.onunload = function* () { + wh.onunload = function () { intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, "timeSpan pref should be hour after accepting dialog with " + "hour selected"); @@ -159,8 +152,6 @@ add_task(function* test_history_downloads_checked() { "downloads pref should be true after accepting dialog with " + "history checkbox checked"); - yield promiseSanitized; - // History visits and downloads within one hour should be cleared. yield promiseHistoryClearedState(uris, true); yield ensureDownloadsClearedState(downloadIDs, true); @@ -174,42 +165,54 @@ add_task(function* test_history_downloads_checked() { yield promiseHistoryClearedState(olderURIs, true); yield ensureDownloadsClearedState(olderDownloadIDs, true); }; - wh.promiseClosed.then(resolve); wh.open(); }); - }); -}); + }, -/** - * Ensures that the combined history-downloads checkbox removes neither - * history visits nor downloads when not checked. - */ -add_task(function* test_history_downloads_unchecked() { - // Add form entries - let formEntries = []; + /** + * Add form history entries for the next test. + */ + function () { + formEntries = []; - for (let i = 0; i < 5; i++) { - formEntries.push((yield promiseAddFormEntryWithMinutesAgo(i))); - } + let iter = function() { + for (let i = 0; i < 5; i++) { + formEntries.push(addFormEntryWithMinutesAgo(iter, i)); + yield undefined; + } + doNextTest(); + }(); + iter.next(); + }, - // Add downloads (within the past hour). - let downloadIDs = []; - for (let i = 0; i < 5; i++) { - yield addDownloadWithMinutesAgo(downloadIDs, i); - } + function () { + // Add downloads (within the past hour). + Task.spawn(function () { + downloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(downloadIDs, i); + } - // Add history, downloads, form entries (within the past hour). - let uris = []; - let places = []; - let pURI; - for (let i = 0; i < 5; i++) { - pURI = makeURI("http://" + i + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); - uris.push(pURI); - } + doNextTest(); + }).then(null, Components.utils.reportError); + }, + + /** + * Ensures that the combined history-downloads checkbox removes neither + * history visits nor downloads when not checked. + */ + function () { + // Add history, downloads, form entries (within the past hour). + let uris = []; + let places = []; + let pURI; + for (let i = 0; i < 5; i++) { + pURI = makeURI("http://" + i + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); + uris.push(pURI); + } - return new Promise(resolve => { PlacesTestUtils.addVisits(places).then(() => { let wh = new WindowHelper(); wh.onload = function () { @@ -223,7 +226,7 @@ add_task(function* test_history_downloads_unchecked() { this.checkPrefCheckbox("formdata", true); this.acceptDialog(); }; - wh.onunload = function* () { + wh.onunload = function () { intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, "timeSpan pref should be hour after accepting dialog with " + "hour selected"); @@ -248,31 +251,25 @@ add_task(function* test_history_downloads_unchecked() { yield promiseHistoryClearedState(uris, true); yield ensureDownloadsClearedState(downloadIDs, true); }; - wh.promiseClosed.then(resolve); wh.open(); }); - }); -}); + }, -/** - * Ensures that the "Everything" duration option works. - */ -add_task(function* test_everything() { - // Add history. - let uris = []; - let places = []; - let pURI; - // within past hour, within past two hours, within past four hours and - // outside past four hours - [10, 70, 130, 250].forEach(function(aValue) { - pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); - uris.push(pURI); - }); - - let promiseSanitized = promiseSanitizationComplete(); - - return new Promise(resolve => { + /** + * Ensures that the "Everything" duration option works. + */ + function () { + // Add history. + let uris = []; + let places = []; + let pURI; + // within past hour, within past two hours, within past four hours and + // outside past four hours + [10, 70, 130, 250].forEach(function(aValue) { + pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); + uris.push(pURI); + }); PlacesTestUtils.addVisits(places).then(() => { let wh = new WindowHelper(); wh.onload = function () { @@ -294,39 +291,32 @@ add_task(function* test_everything() { this.acceptDialog(); }; wh.onunload = function () { - yield promiseSanitized; intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING, "timeSpan pref should be everything after accepting dialog " + "with everything selected"); yield promiseHistoryClearedState(uris, true); }; - wh.promiseClosed.then(resolve); wh.open(); }); - }); -}); + }, -/** - * Ensures that the "Everything" warning is visible on dialog open after - * the previous test. - */ -add_task(function* test_everything_warning() { - // Add history. - let uris = []; - let places = []; - let pURI; - // within past hour, within past two hours, within past four hours and - // outside past four hours - [10, 70, 130, 250].forEach(function(aValue) { - pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); - places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); - uris.push(pURI); - }); - - let promiseSanitized = promiseSanitizationComplete(); - - return new Promise(resolve => { + /** + * Ensures that the "Everything" warning is visible on dialog open after + * the previous test. + */ + function () { + // Add history. + let uris = []; + let places = []; + let pURI; + // within past hour, within past two hours, within past four hours and + // outside past four hours + [10, 70, 130, 250].forEach(function(aValue) { + pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); + uris.push(pURI); + }); PlacesTestUtils.addVisits(places).then(() => { let wh = new WindowHelper(); wh.onload = function () { @@ -342,30 +332,33 @@ add_task(function* test_everything_warning() { "timeSpan pref should be everything after accepting dialog " + "with everything selected"); - yield promiseSanitized; - yield promiseHistoryClearedState(uris, true); }; - wh.promiseClosed.then(resolve); wh.open(); }); - }); -}); + }, -/** - * The next three tests checks that when a certain history item cannot be - * cleared then the checkbox should be both disabled and unchecked. - * In addition, we ensure that this behavior does not modify the preferences. - */ -add_task(function* test_cannot_clear_history() { - // Add form entries - let formEntries = [ (yield promiseAddFormEntryWithMinutesAgo(10)) ]; + /** + * Add form history entry for the next test. + */ + function () { + let iter = function() { + formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; + yield undefined; + doNextTest(); + }(); - let promiseSanitized = promiseSanitizationComplete(); + iter.next(); + }, - // Add history. - let pURI = makeURI("http://" + 10 + "-minutes-ago.com/"); - return new Promise(resolve => { + /** + * The next three tests checks that when a certain history item cannot be + * cleared then the checkbox should be both disabled and unchecked. + * In addition, we ensure that this behavior does not modify the preferences. + */ + function () { + // Add history. + let pURI = makeURI("http://" + 10 + "-minutes-ago.com/"); PlacesTestUtils.addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}).then(() => { let uris = [ pURI ]; @@ -386,88 +379,90 @@ add_task(function* test_cannot_clear_history() { this.acceptDialog(); }; wh.onunload = function () { - yield promiseSanitized; - yield promiseHistoryClearedState(uris, true); let exists = yield formNameExists(formEntries[0]); is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); }; - wh.promiseClosed.then(resolve); wh.open(); }); - }); -}); - -add_task(function* test_no_formdata_history_to_clear() { - let promiseSanitized = promiseSanitizationComplete(); - let wh = new WindowHelper(); - wh.onload = function() { - boolPrefIs("cpd.history", true, - "history pref should be true after accepting dialog with " + - "history checkbox checked"); - boolPrefIs("cpd.formdata", true, - "formdata pref should be true after accepting dialog with " + - "formdata checkbox checked"); - - // Even though the formdata pref is true, because there is no history - // left to clear, the checkbox will be disabled. - var cb = this.win.document.querySelectorAll( - "#itemList > [preference='privacy.cpd.formdata']"); - ok(cb.length == 1 && cb[0].disabled && !cb[0].checked, - "There is no formdata history, checkbox should be disabled and be " + - "cleared to reduce user confusion (bug 497664)."); - - var cb = this.win.document.querySelectorAll( - "#itemList > [preference='privacy.cpd.history']"); - ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, - "There is no history, but history checkbox should always be enabled " + - "and will be checked from previous preference."); - - this.acceptDialog(); - } - wh.open(); - yield wh.promiseClosed; - yield promiseSanitized; -}); - -add_task(function* test_form_entries() { - let formEntry = (yield promiseAddFormEntryWithMinutesAgo(10)); - - let promiseSanitized = promiseSanitizationComplete(); - - let wh = new WindowHelper(); - wh.onload = function() { - boolPrefIs("cpd.formdata", true, - "formdata pref should persist previous value after accepting " + - "dialog where you could not clear formdata."); - - var cb = this.win.document.querySelectorAll( - "#itemList > [preference='privacy.cpd.formdata']"); - - info("There exists formEntries so the checkbox should be in sync with the pref."); - is(cb.length, 1, "There is only one checkbox for form data"); - ok(!cb[0].disabled, "The checkbox is enabled"); - ok(cb[0].checked, "The checkbox is checked"); - - this.acceptDialog(); - }; - wh.onunload = function () { - yield promiseSanitized; - let exists = yield formNameExists(formEntry); - is(exists, false, "form entry " + formEntry + " should no longer exist"); - }; - wh.open(); - return wh.promiseClosed; -}); + }, + function () { + let wh = new WindowHelper(); + wh.onload = function() { + boolPrefIs("cpd.history", true, + "history pref should be true after accepting dialog with " + + "history checkbox checked"); + boolPrefIs("cpd.formdata", true, + "formdata pref should be true after accepting dialog with " + + "formdata checkbox checked"); -/** - * Ensure that toggling details persists - * across dialog openings. - */ -add_task(function* test_toggling_details_persists() { - { + // Even though the formdata pref is true, because there is no history + // left to clear, the checkbox will be disabled. + var cb = this.win.document.querySelectorAll( + "#itemList > [preference='privacy.cpd.formdata']"); + + // Wait until the checkbox is disabled. This is done asynchronously + // from Sanitizer.init() as FormHistory.count() is a purely async API. + promiseWaitForCondition(() => cb[0].disabled).then(() => { + ok(cb.length == 1 && cb[0].disabled && !cb[0].checked, + "There is no formdata history, checkbox should be disabled and be " + + "cleared to reduce user confusion (bug 497664)."); + + cb = this.win.document.querySelectorAll( + "#itemList > [preference='privacy.cpd.history']"); + ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, + "There is no history, but history checkbox should always be enabled " + + "and will be checked from previous preference."); + + this.acceptDialog(); + }); + } + wh.open(); + }, + + /** + * Add form history entry for the next test. + */ + function () { + let iter = function() { + formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; + yield undefined; + doNextTest(); + }(); + + iter.next(); + }, + + function () { + let wh = new WindowHelper(); + wh.onload = function() { + boolPrefIs("cpd.formdata", true, + "formdata pref should persist previous value after accepting " + + "dialog where you could not clear formdata."); + + var cb = this.win.document.querySelectorAll( + "#itemList > [preference='privacy.cpd.formdata']"); + ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, + "There exists formEntries so the checkbox should be in sync with " + + "the pref."); + + this.acceptDialog(); + }; + wh.onunload = function () { + let exists = yield formNameExists(formEntries[0]); + is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); + }; + wh.open(); + }, + + + /** + * These next six tests together ensure that toggling details persists + * across dialog openings. + */ + function () { let wh = new WindowHelper(); wh.onload = function () { // Check all items and select "Everything" @@ -480,9 +475,8 @@ add_task(function* test_toggling_details_persists() { this.acceptDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should remain closed because all items are checked. @@ -493,9 +487,8 @@ add_task(function* test_toggling_details_persists() { this.acceptDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should be open because not all items are checked. @@ -507,9 +500,8 @@ add_task(function* test_toggling_details_persists() { this.acceptDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should be open because not all items are checked. @@ -521,9 +513,8 @@ add_task(function* test_toggling_details_persists() { this.cancelDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should be open because not all items are checked. @@ -537,9 +528,8 @@ add_task(function* test_toggling_details_persists() { this.acceptDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should not be open because "Last Hour" is selected @@ -548,9 +538,8 @@ add_task(function* test_toggling_details_persists() { this.cancelDialog(); }; wh.open(); - yield wh.promiseClosed; - } - { + }, + function () { let wh = new WindowHelper(); wh.onload = function () { // Details should have remained closed @@ -562,118 +551,115 @@ add_task(function* test_toggling_details_persists() { this.cancelDialog(); }; wh.open(); - yield wh.promiseClosed; - } -}); + }, + function () { + // Test for offline cache deletion -// Test for offline cache deletion -add_task(function* test_offline_cache() { - // Prepare stuff, we will work with www.example.com - var URL = "http://www.example.com"; + // Prepare stuff, we will work with www.example.com + var URL = "http://www.example.com"; - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var URI = ios.newURI(URL, null, null); + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var URI = ios.newURI(URL, null, null); - var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - var principal = sm.getNoAppCodebasePrincipal(URI); + var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = sm.createCodebasePrincipal(URI, {}); - // Give www.example.com privileges to store offline data - var pm = Cc["@mozilla.org/permissionmanager;1"] - .getService(Ci.nsIPermissionManager); - pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION); - pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN); - - // Store something to the offline cache - var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"] - .getService(Ci.nsIApplicationCacheService); - var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default); - var appcache = appcacheserv.createApplicationCache(appcachegroupid); - - var cacheserv = Cc["@mozilla.org/netwerk/cache-storage-service;1"] - .getService(Ci.nsICacheStorageService); - var storage = cacheserv.appCacheStorage(LoadContextInfo.default, appcache); - - // Open the dialog - let wh = new WindowHelper(); - wh.onload = function () { - this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); - // Show details - this.toggleDetails(); - // Clear only offlineApps - this.uncheckAllCheckboxes(); - this.checkPrefCheckbox("offlineApps", true); - this.acceptDialog(); - }; - wh.onunload = function () { - // Check if the cache has been deleted - var size = -1; - var visitor = { - onCacheStorageInfo: function (aEntryCount, aConsumption, aCapacity, aDiskDirectory) - { - size = aConsumption; - } - }; - storage.asyncVisitStorage(visitor, false); - // Offline cache visit happens synchronously, since it's forwarded to the old code - is(size, 0, "offline application cache entries evicted"); - }; - - var cacheListener = { - onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; }, - onCacheEntryAvailable: function (entry, isnew, appcache, status) { - is(status, Cr.NS_OK); - var stream = entry.openOutputStream(0); - var content = "content"; - stream.write(content, content.length); - stream.close(); - entry.close(); - wh.open(); - } - }; - - storage.asyncOpenURI(makeURI(URL), "", Ci.nsICacheStorage.OPEN_TRUNCATE, cacheListener); - return wh.promiseClosed; -}); - -// Test for offline apps permission deletion -add_task(function* test_offline_apps_permissions() { - // Prepare stuff, we will work with www.example.com - var URL = "http://www.example.com"; - - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var URI = ios.newURI(URL, null, null); - - var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - var principal = sm.createCodebasePrincipal(URI, {}); - - let promiseSanitized = promiseSanitizationComplete(); - - // Open the dialog - let wh = new WindowHelper(); - wh.onload = function () { - this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); - // Show details - this.toggleDetails(); - // Clear only offlineApps - this.uncheckAllCheckboxes(); - this.checkPrefCheckbox("siteSettings", true); - this.acceptDialog(); - }; - wh.onunload = function () { - yield promiseSanitized; - - // Check all has been deleted (privileges, data, cache) + // Give www.example.com privileges to store offline data var pm = Cc["@mozilla.org/permissionmanager;1"] .getService(Ci.nsIPermissionManager); - is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed"); - }; - wh.open(); - return wh.promiseClosed; -}); + pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION); + pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN); + + // Store something to the offline cache + var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"] + .getService(Ci.nsIApplicationCacheService); + var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default); + var appcache = appcacheserv.createApplicationCache(appcachegroupid); + + var cacheserv = Cc["@mozilla.org/netwerk/cache-storage-service;1"] + .getService(Ci.nsICacheStorageService); + var storage = cacheserv.appCacheStorage(LoadContextInfo.default, appcache); + + // Open the dialog + let wh = new WindowHelper(); + wh.onload = function () { + this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); + // Show details + this.toggleDetails(); + // Clear only offlineApps + this.uncheckAllCheckboxes(); + this.checkPrefCheckbox("offlineApps", true); + this.acceptDialog(); + }; + wh.onunload = function () { + // Check if the cache has been deleted + var size = -1; + var visitor = { + onCacheStorageInfo: function (aEntryCount, aConsumption, aCapacity, aDiskDirectory) + { + size = aConsumption; + } + }; + storage.asyncVisitStorage(visitor, false); + // Offline cache visit happens synchronously, since it's forwarded to the old code + is(size, 0, "offline application cache entries evicted"); + }; + + var cacheListener = { + onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; }, + onCacheEntryAvailable: function (entry, isnew, appcache, status) { + is(status, Cr.NS_OK); + var stream = entry.openOutputStream(0); + var content = "content"; + stream.write(content, content.length); + stream.close(); + entry.close(); + wh.open(); + } + }; + + storage.asyncOpenURI(makeURI(URL), "", Ci.nsICacheStorage.OPEN_TRUNCATE, cacheListener); + }, + function () { + // Test for offline apps permission deletion + + // Prepare stuff, we will work with www.example.com + var URL = "http://www.example.com"; + + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var URI = ios.newURI(URL, null, null); + + var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = sm.createCodebasePrincipal(URI, {}); + + // Open the dialog + let wh = new WindowHelper(); + wh.onload = function () { + this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); + // Show details + this.toggleDetails(); + // Clear only offlineApps + this.uncheckAllCheckboxes(); + this.checkPrefCheckbox("siteSettings", true); + this.acceptDialog(); + }; + wh.onunload = function () { + // Check all has been deleted (privileges, data, cache) + var pm = Cc["@mozilla.org/permissionmanager;1"] + .getService(Ci.nsIPermissionManager); + is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed"); + }; + wh.open(); + } +]; + +// Index in gAllTests of the test currently being run. Incremented for each +// test run. See doNextTest(). +var gCurrTest = 0; let now_mSec = Date.now(); let now_uSec = now_mSec * 1000; @@ -689,7 +675,6 @@ let now_uSec = now_mSec * 1000; */ function WindowHelper(aWin) { this.win = aWin; - this.promiseClosed = new Promise(resolve => {this._resolveClosed = resolve}); } WindowHelper.prototype = { @@ -834,8 +819,6 @@ WindowHelper.prototype = { var loaded = false; let win = aSubject.QueryInterface(Ci.nsIDOMWindow); - let promiseDialogReady = promiseSanitizationDialogReady(); - win.addEventListener("load", function onload(event) { win.removeEventListener("load", onload, false); @@ -845,10 +828,17 @@ WindowHelper.prototype = { wh.win = win; loaded = true; - Task.spawn(function*() { - yield promiseDialogReady; - yield new Promise(resolve => setTimeout(resolve, 0)); - yield wh.onload(); + executeSoon(function () { + // Some exceptions that reach here don't reach the test harness, but + // ok()/is() do... + try { + wh.onload(); + } + catch (exc) { + win.close(); + ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); + finish(); + } }); }, false); @@ -865,14 +855,23 @@ WindowHelper.prototype = { win.removeEventListener("unload", onunload, false); wh.win = win; - // Some exceptions that reach here don't reach the test harness, but - // ok()/is() do... - Task.spawn(function*() { - if (wh.onunload) { - yield wh.onunload(); + executeSoon(function () { + // Some exceptions that reach here don't reach the test harness, but + // ok()/is() do... + try { + if (wh.onunload) { + Task.spawn(wh.onunload).then(function() { + waitForAsyncUpdates(doNextTest); + }).then(null, Components.utils.reportError); + } else { + waitForAsyncUpdates(doNextTest); + } + } + catch (exc) { + win.close(); + ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); + finish(); } - yield PlacesTestUtils.promiseAsyncUpdates(); - wh._resolveClosed(); }); }, false); } @@ -910,14 +909,6 @@ WindowHelper.prototype = { } }; -function promiseSanitizationDialogReady() { - return promiseTopicObserved("sanitize-dialog-setup-complete"); -} - -function promiseSanitizationComplete() { - return promiseTopicObserved("sanitizer-sanitization-complete"); -} - /** * Adds a download to history. * @@ -949,23 +940,19 @@ function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { * @param aMinutesAgo * The entry will be added this many minutes ago */ -function promiseAddFormEntryWithMinutesAgo(aMinutesAgo) { +function addFormEntryWithMinutesAgo(then, aMinutesAgo) { let name = aMinutesAgo + "-minutes-ago"; // Artifically age the entry to the proper vintage. let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin); - return new Promise((resolve, reject) => - FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp }, + FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp }, { handleError: function (error) { do_throw("Error occurred updating form history: " + error); - reject(); }, - handleCompletion: function (reason) { - resolve(name); - } - }) - ) + handleCompletion: function (reason) { then.next(); } + }); + return name; } /** @@ -1052,6 +1039,22 @@ function downloadExists(aPath) }); } +/** + * Runs the next test in the gAllTests array. If all tests have been run, + * finishes the entire suite. + */ +function doNextTest() { + if (gAllTests.length <= gCurrTest) { + blankSlate(); + waitForAsyncUpdates(finish); + } + else { + let ct = gCurrTest; + gCurrTest++; + gAllTests[ct](); + } +} + /** * Ensures that the specified downloads are either cleared or not. * @@ -1091,3 +1094,13 @@ function intPrefIs(aPrefName, aExpectedVal, aMsg) { function visitTimeForMinutesAgo(aMinutesAgo) { return now_uSec - aMinutesAgo * kUsecPerMin; } + +/////////////////////////////////////////////////////////////////////////////// + +function test() { + requestLongerTimeout(2); + waitForExplicitFinish(); + blankSlate(); + // Kick off all the tests in the gAllTests array. + waitForAsyncUpdates(doNextTest); +} diff --git a/browser/base/content/test/newtab/browser_newtab_bug722273.js b/browser/base/content/test/newtab/browser_newtab_bug722273.js index 82d86063cb0..bc561b321e1 100644 --- a/browser/base/content/test/newtab/browser_newtab_bug722273.js +++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js @@ -11,27 +11,20 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"] let {Sanitizer} = tmp; -add_task(function*() { - yield promiseSanitizeHistory(); - yield promiseAddFakeVisits(); - yield addNewTabPageTabPromise(); +function runTests() { + sanitizeHistory(); + yield addFakeVisits(); + yield addNewTabPageTab(); + is(getCell(0).site.url, URL, "first site is our fake site"); - whenPagesUpdated(() => {}); - yield promiseSanitizeHistory(); + whenPagesUpdated(); + yield sanitizeHistory(); - // Now wait until the grid is updated - while (true) { - if (!getCell(0).site) { - break; - } - info("the fake site is still present"); - yield new Promise(resolve => setTimeout(resolve, 1000)); - } - ok(!getCell(0).site, "fake site is gone"); -}); + ok(!getCell(0).site, "the fake site is gone"); +} -function promiseAddFakeVisits() { +function addFakeVisits() { let visits = []; for (let i = 59; i > 0; i--) { visits.push({ @@ -44,21 +37,19 @@ function promiseAddFakeVisits() { title: "fake site", visits: visits }; - return new Promise((resolve, reject) => { - PlacesUtils.asyncHistory.updatePlaces(place, { - handleError: function () reject(new Error("Couldn't add visit")), - handleResult: function () {}, - handleCompletion: function () { - NewTabUtils.links.populateCache(function () { - NewTabUtils.allPages.update(); - resolve(); - }, true); - } - }); + PlacesUtils.asyncHistory.updatePlaces(place, { + handleError: function () ok(false, "couldn't add visit"), + handleResult: function () {}, + handleCompletion: function () { + NewTabUtils.links.populateCache(function () { + NewTabUtils.allPages.update(); + TestRunner.next(); + }, true); + } }); } -function promiseSanitizeHistory() { +function sanitizeHistory() { let s = new Sanitizer(); s.prefDomain = "privacy.cpd."; @@ -73,5 +64,5 @@ function promiseSanitizeHistory() { prefs.setBoolPref("sessions", false); prefs.setBoolPref("siteSettings", false); - return s.sanitize(); + s.sanitize(); } diff --git a/browser/base/content/test/newtab/head.js b/browser/base/content/test/newtab/head.js index a6935070c74..ab88fc9f643 100644 --- a/browser/base/content/test/newtab/head.js +++ b/browser/base/content/test/newtab/head.js @@ -116,58 +116,18 @@ function watchLinksChangeOnce() { /** * Provide the default test function to start our test runner. - * - * We need different code paths for tests that are still wired for - * `TestRunner` and tests that have been ported to `add_task` as - * we cannot have both in the same file. */ -function isTestPortedToAddTask() { - return gTestPath.endsWith("browser_newtab_bug722273.js"); -} -if (!isTestPortedToAddTask()) { - this.test = function() { - waitForExplicitFinish(); - // start TestRunner.run() after directory links is downloaded and written to disk - watchLinksChangeOnce().then(() => { - // Wait for hidden page to update with the desired links - whenPagesUpdated(() => TestRunner.run(), true); - }); - - // Save the original directory source (which is set globally for tests) - gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE); - Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource); - } -} else { - add_task(function* setup() { - registerCleanupFunction(function() { - return new Promise(resolve => { - function cleanupAndFinish() { - PlacesTestUtils.clearHistory().then(() => { - whenPagesUpdated(resolve); - NewTabUtils.restore(); - }); - } - - let callbacks = NewTabUtils.links._populateCallbacks; - let numCallbacks = callbacks.length; - - if (numCallbacks) - callbacks.splice(0, numCallbacks, cleanupAndFinish); - else - cleanupAndFinish(); - }); - }); - - let promiseReady = Task.spawn(function*() { - yield watchLinksChangeOnce(); - yield new Promise(resolve => whenPagesUpdated(resolve, true)); - }); - - // Save the original directory source (which is set globally for tests) - gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE); - Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource); - yield promiseReady; +function test() { + waitForExplicitFinish(); + // start TestRunner.run() after directory links is downloaded and written to disk + watchLinksChangeOnce().then(() => { + // Wait for hidden page to update with the desired links + whenPagesUpdated(() => TestRunner.run(), true); }); + + // Save the original directory source (which is set globally for tests) + gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE); + Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource); } /** diff --git a/browser/base/jar.mn b/browser/base/jar.mn index 12324d888a7..7d5b86001ff 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -138,7 +138,7 @@ browser.jar: content/browser/safeMode.css (content/safeMode.css) content/browser/safeMode.js (content/safeMode.js) content/browser/safeMode.xul (content/safeMode.xul) - content/browser/sanitize.js (content/sanitize.js) +* content/browser/sanitize.js (content/sanitize.js) * content/browser/sanitize.xul (content/sanitize.xul) * content/browser/sanitizeDialog.js (content/sanitizeDialog.js) content/browser/sanitizeDialog.css (content/sanitizeDialog.css) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 33fb7212dd5..fad7f988720 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -507,9 +507,6 @@ BrowserGlue.prototype = { case "autocomplete-did-enter-text": this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput)); break; - case "test-initialize-sanitizer": - this._sanitizer.onStartup(); - break; } }, @@ -1162,7 +1159,6 @@ BrowserGlue.prototype = { Cu.import("resource://gre/modules/RokuApp.jsm"); return new RokuApp(aService); }, - mirror: true, types: ["video/mp4"], extensions: ["mp4"] }; @@ -1764,6 +1760,7 @@ BrowserGlue.prototype = { * - export bookmarks as HTML, if so configured. */ _onPlacesShutdown: function BG__onPlacesShutdown() { + this._sanitizer.onShutdown(); PageThumbs.uninit(); if (this._bookmarksBackupIdleTime) { diff --git a/browser/components/places/tests/unit/test_clearHistory_shutdown.js b/browser/components/places/tests/unit/test_clearHistory_shutdown.js index 36a9ab7fc6e..a20f124549b 100644 --- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js +++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js @@ -49,8 +49,6 @@ add_task(function* test_execute() { let glue = Cc["@mozilla.org/browser/browserglue;1"]. getService(Ci.nsIObserver); glue.observe(null, "initial-migration-will-import-default-bookmarks", null); - glue.observe(null, "test-initialize-sanitizer", null); - Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", true); Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true); @@ -73,11 +71,13 @@ add_task(function* test_execute() { } do_print("Add cache."); yield storeCache(FTP_URL, "testData"); +}); +add_task(function* run_test_continue() { do_print("Simulate and wait shutdown."); yield shutdownPlaces(); - let stmt = DBConn(true).createStatement( + let stmt = DBConn().createStatement( "SELECT id FROM moz_places WHERE url = :page_url " ); @@ -93,7 +93,13 @@ add_task(function* test_execute() { do_print("Check cache"); // Check cache. - yield checkCache(FTP_URL); + let promiseCacheChecked = checkCache(FTP_URL); + + do_print("Shutdown the download manager"); + // Shutdown the download manager. + Services.obs.notifyObservers(null, "quit-application", null); + + yield promiseCacheChecked; }); function storeCache(aURL, aContent) { diff --git a/browser/modules/Sanitizer.jsm b/browser/modules/Sanitizer.jsm deleted file mode 100644 index cf9ea474a8a..00000000000 --- a/browser/modules/Sanitizer.jsm +++ /dev/null @@ -1,22 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -// -// A shared module for sanitize.js -// -// Until bug 1167238 lands, this serves only as a way to ensure that -// sanitize is loaded from its own compartment, rather than from that -// of the sanitize dialog. -// - -this.EXPORTED_SYMBOLS = ["Sanitizer"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -let scope = {}; -Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js", scope); - -this.Sanitizer = scope.Sanitizer; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 0f6ff61520a..eadb836f5fe 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -38,7 +38,6 @@ EXTRA_JS_MODULES += [ 'ReaderParent.jsm', 'RecentWindow.jsm', 'RemotePrompt.jsm', - 'Sanitizer.jsm', 'SelfSupportBackend.jsm', 'SitePermissions.jsm', 'Social.jsm', diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm index 5490722e549..eeaf33a36b2 100644 --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -2071,19 +2071,6 @@ XPCOMUtils.defineLazyGetter(this, "bundle", function() { createBundle(PLACES_STRING_BUNDLE_URI); }); -// A promise resolved once the Sqlite.jsm connections -// can be closed. -let promiseCanCloseConnection = function() { - let TOPIC = "places-will-close-connection"; - return new Promise(resolve => { - let observer = function() { - Services.obs.removeObserver(observer, TOPIC); - resolve(); - } - Services.obs.addObserver(observer, TOPIC, false) - }); -}; - XPCOMUtils.defineLazyGetter(this, "gAsyncDBConnPromised", () => new Promise((resolve) => { Sqlite.cloneStorageConnection({ @@ -2091,21 +2078,12 @@ XPCOMUtils.defineLazyGetter(this, "gAsyncDBConnPromised", readOnly: true }).then(conn => { try { - let promiseReady = promiseCanCloseConnection(); - let state = "0. not started"; - Sqlite.shutdown.addBlocker("PlacesUtils read-only connection closing", - Task.async(function*() { - // Don't close the connection as long as it might be used. - state = "1. waiting for `places-will-close-connection`"; - yield promiseReady; - - // But close the connection before Sqlite shutdown. - state = "2. closing the connection"; - yield conn.close(); - - state = "3. done"; - }), - () => state); + Sqlite.shutdown.addBlocker( + "PlacesUtils read-only connection closing", + conn.close.bind(conn)); + PlacesUtils.history.shutdownClient.jsclient.addBlocker( + "PlacesUtils read-only connection closing", + conn.close.bind(conn)); } catch(ex) { // It's too late to block shutdown, just close the connection. conn.close(); @@ -2122,21 +2100,12 @@ XPCOMUtils.defineLazyGetter(this, "gAsyncDBWrapperPromised", connection: PlacesUtils.history.DBConnection, }).then(conn => { try { - let promiseReady = promiseCanCloseConnection(); - let state = "0. not started"; - Sqlite.shutdown.addBlocker("PlacesUtils wrapped connection closing", - Task.async(function*() { - // Don't close the connection as long as it might be used. - state = "1. waiting for `places-will-close-connection`"; - yield promiseReady; - - // But close the connection before Sqlite shutdown. - state = "2. closing the connection"; - yield conn.close(); - - state = "3. done"; - }), - () => state); + Sqlite.shutdown.addBlocker( + "PlacesUtils wrapped connection closing", + conn.close.bind(conn)); + PlacesUtils.history.shutdownClient.jsclient.addBlocker( + "PlacesUtils wrapped connection closing", + conn.close.bind(conn)); } catch(ex) { // It's too late to block shutdown, just close the connection. conn.close();