Bug 498181 - Offer to reset a user's profile if it was last used more than two months ago. r=jaws
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 28 Jun 2013 23:25:08 -0400
changeset 136998 37015ff213ae34370d344234feaf73411e455283
parent 136997 e07fc1165da94d50dc187cd27808afb160278985
child 136999 70488f55e30d0e1de7cf94b57444aea7bd471221
push id24907
push userryanvm@gmail.com
push dateMon, 01 Jul 2013 12:12:33 +0000
treeherdermozilla-central@9af8dd6825dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs498181
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 498181 - Offer to reset a user's profile if it was last used more than two months ago. r=jaws
browser/components/nsBrowserGlue.js
toolkit/content/Makefile.in
toolkit/content/aboutSupport.js
toolkit/content/aboutSupport.xhtml
toolkit/content/jar.mn
toolkit/content/resetProfile.js
toolkit/locales/en-US/chrome/global/resetProfile.properties
toolkit/locales/jar.mn
toolkit/modules/Makefile.in
toolkit/modules/ResetProfile.jsm
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -511,30 +511,74 @@ BrowserGlue.prototype = {
     ];
 
     let nb = win.document.getElementById("global-notificationbox");
     nb.appendNotification(message, "slow-startup",
                           "chrome://browser/skin/slowStartup-16.png",
                           nb.PRIORITY_INFO_LOW, buttons);
   },
 
+  /**
+   * Show a notification bar offering a reset if the profile has been unused for some time.
+   */
+  _resetUnusedProfileNotification: function () {
+    let win = this.getMostRecentBrowserWindow();
+    if (!win)
+      return;
+
+    Cu.import("resource://gre/modules/ResetProfile.jsm");
+    if (!ResetProfile.resetSupported())
+      return;
+
+    let productName = Services.strings
+                              .createBundle("chrome://branding/locale/brand.properties")
+                              .GetStringFromName("brandShortName");
+    let resetBundle = Services.strings
+                              .createBundle("chrome://global/locale/resetProfile.properties");
+
+    let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
+    let buttons = [
+      {
+        label:     resetBundle.formatStringFromName("resetProfile.resetButton.label", [productName], 1),
+        accessKey: resetBundle.GetStringFromName("resetProfile.resetButton.accesskey"),
+        callback: function () {
+          ResetProfile.openConfirmationDialog(win);
+        }
+      },
+    ];
+
+    let nb = win.document.getElementById("global-notificationbox");
+    nb.appendNotification(message, "reset-unused-profile",
+                          "chrome://global/skin/icons/question-16.png",
+                          nb.PRIORITY_INFO_LOW, buttons);
+  },
+
   // the first browser window has finished initializing
   _onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
 #ifdef XP_WIN
     // For windows seven, initialize the jump list module.
     const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
     if (WINTASKBAR_CONTRACTID in Cc &&
         Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
       let temp = {};
       Cu.import("resource:///modules/WindowsJumpLists.jsm", temp);
       temp.WinTaskbarJumpList.startup();
     }
 #endif
 
     this._trackSlowStartup();
+
+    // Offer to reset a user's profile if it hasn't been used for 60 days.
+    const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
+    let processStartupTime = Services.startup.getStartupInfo().process;
+    let lastUse = Services.appinfo.replacedLockTime;
+    if (processStartupTime && lastUse &&
+        processStartupTime.getTime() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
+      this._resetUnusedProfileNotification();
+    }
   },
 
   /**
    * Profile shutdown handler (contains profile cleanup routines).
    * All components depending on Places should be shut down in
    * _onPlacesShutdown() and not here.
    */
   _onProfileShutdown: function BG__onProfileShutdown() {
--- a/toolkit/content/Makefile.in
+++ b/toolkit/content/Makefile.in
@@ -20,18 +20,16 @@ DEFINES += \
   -Dac_configure_args="$(ac_configure_args)" \
   -DCC="$(CC)" \
   -DCC_VERSION="$(CC_VERSION)" \
   -DCFLAGS="$(CFLAGS)" \
   -DCXX="$(CXX)" \
   -DCXX_VERSION="$(CXX_VERSION)" \
   -DCXXFLAGS="$(CXXFLAGS)" \
   -DCPPFLAGS="$(CPPFLAGS)" \
-  -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
-  -DMOZ_BUILD_APP=$(MOZ_BUILD_APP) \
   $(NULL)
 
 MOZ_SOURCE_STAMP ?= $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null)
 ifdef MOZ_SOURCE_STAMP
 DEFINES += -DSOURCE_CHANGESET="$(MOZ_SOURCE_STAMP)"
 endif
 
 ifeq (Android,$(OS_TARGET))
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -1,16 +1,17 @@
 /* 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/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/Troubleshoot.jsm");
+Components.utils.import("resource://gre/modules/ResetProfile.jsm");
 
 window.addEventListener("load", function onload(event) {
   window.removeEventListener("load", onload, false);
   Troubleshoot.snapshot(function (snapshot) {
     for (let prop in snapshotFormatters)
       snapshotFormatters[prop](snapshot[prop]);
   });
   populateResetBox();
@@ -511,36 +512,11 @@ function showUpdateHistory() {
                    .createInstance(Ci.nsIUpdatePrompt);
   prompter.showUpdateHistory(window);
 }
 
 /**
  * Profile reset is only supported for the default profile if the appropriate migrator exists.
  */
 function populateResetBox() {
-  if (resetSupported())
+  if (ResetProfile.resetSupported())
     $("reset-box").style.visibility = "visible";
 }
-
-/**
- * Restart the application to reset the profile.
- */
-function resetProfileAndRestart() {
-  let branding = Services.strings.createBundle("chrome://branding/locale/brand.properties");
-  let brandShortName = branding.GetStringFromName("brandShortName");
-
-  // Prompt the user to confirm.
-  let retVals = {
-    reset: false,
-  };
-  window.openDialog("chrome://global/content/resetProfile.xul", null,
-                    "chrome,modal,centerscreen,titlebar,dialog=yes", retVals);
-  if (!retVals.reset)
-    return;
-
-  // Set the reset profile environment variable.
-  let env = Cc["@mozilla.org/process/environment;1"]
-              .getService(Ci.nsIEnvironment);
-  env.set("MOZ_RESET_PROFILE_RESTART", "1");
-
-  let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
-  appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
-}
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -25,17 +25,17 @@
             src="chrome://global/content/resetProfile.js"/>
   </head>
 
   <body dir="&locale.dir;">
 
     <div id="reset-box" style="visibility: hidden">
       <h3>&resetProfile.title;</h3>
       <p>&resetProfile.description;</p>
-      <button onclick="resetProfileAndRestart()">
+      <button onclick="ResetProfile.openConfirmationDialog(window)">
         &resetProfile.button.label2;
       </button>
     </div>
 
     <h1>
       &aboutSupport.pageTitle;
     </h1>
 
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -38,17 +38,17 @@ toolkit.jar:
    content/global/finddialog.js               (finddialog.js)
 *+ content/global/finddialog.xul              (finddialog.xul)
    content/global/findUtils.js                (findUtils.js)
    content/global/filepicker.properties       (filepicker.properties)
 *+ content/global/globalOverlay.js            (globalOverlay.js)
 +  content/global/mozilla.xhtml               (mozilla.xhtml)
    content/global/nsDragAndDrop.js            (nsDragAndDrop.js)
    content/global/resetProfile.css            (resetProfile.css)
-*  content/global/resetProfile.js             (resetProfile.js)
+   content/global/resetProfile.js             (resetProfile.js)
    content/global/resetProfile.xul            (resetProfile.xul)
    content/global/resetProfileProgress.xul    (resetProfileProgress.xul)
    content/global/treeUtils.js                (treeUtils.js)
    content/global/viewZoomOverlay.js          (viewZoomOverlay.js)
 *+ content/global/bindings/autocomplete.xml    (widgets/autocomplete.xml)
    content/global/bindings/browser.xml         (widgets/browser.xml)
    content/global/bindings/button.xml          (widgets/button.xml)
    content/global/bindings/checkbox.xml        (widgets/checkbox.xml)
--- a/toolkit/content/resetProfile.js
+++ b/toolkit/content/resetProfile.js
@@ -1,85 +1,34 @@
 /* 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/Services.jsm");
+"use strict";
 
-let Cc = Components.classes;
-let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ResetProfile.jsm");
 
 // based on onImportItemsPageShow from migration.js
 function populateResetPane(aContainerID) {
   let resetProfileItems = document.getElementById(aContainerID);
   try {
-    let dataTypes = getMigratedData();
+    let dataTypes = ResetProfile.getMigratedData();
     for (let dataType of dataTypes) {
       let label = document.createElement("label");
       label.setAttribute("value", dataType);
       resetProfileItems.appendChild(label);
     }
   } catch (ex) {
     Cu.reportError(ex);
   }
 }
 
 function onResetProfileLoad() {
   populateResetPane("migratedItems");
 }
 
-/**
- * Check if reset is supported for the currently running profile.
- *
- * @return boolean whether reset is supported.
- */
-function resetSupported() {
-  let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
-                       getService(Ci.nsIToolkitProfileService);
-  let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-
-#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
-#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
-
-  // Reset is only supported for the default profile if the self-migrator used for reset exists.
-  try {
-    return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
-             ("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
-  } catch (e) {
-    // Catch exception when there is no selected profile.
-    Cu.reportError(e);
-  }
-  return false;
-}
-
-function getMigratedData() {
-  Components.utils.import("resource:///modules/MigrationUtils.jsm");
-
-#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
-#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
-
-  // From migration.properties
-  const MIGRATED_TYPES = [
-    128,// Windows/Tabs
-    4,  // History and Bookmarks
-    16, // Passwords
-    8,  // Form History
-    2,  // Cookies
-  ];
-
-  // Loop over possible data to migrate to give the user a list of what will be preserved.
-  let dataTypes = [];
-  for (let itemID of MIGRATED_TYPES) {
-    try {
-      let typeName = MigrationUtils.getLocalizedString(itemID + "_" + MOZ_APP_NAME);
-      dataTypes.push(typeName);
-    } catch (x) {
-      // Catch exceptions when the string for a data type doesn't exist.
-      Components.utils.reportError(x);
-    }
-  }
-  return dataTypes;
-}
-
 function onResetProfileAccepted() {
   let retVals = window.arguments[0];
   retVals.reset = true;
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/global/resetProfile.properties
@@ -0,0 +1,12 @@
+# 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/.
+
+# LOCALIZATION NOTE: These strings are used for profile reset.
+
+# LOCALIZATION NOTE (resetUnusedProfile.message): %S is brandShortName.
+resetUnusedProfile.message=It looks like you haven't started %S in a while. Do you want to clean it up for a fresh, like-new experience? And by the way, welcome back!
+
+# LOCALIZATION NOTE (resetProfile.resetButton.label): %S is brandShortName.
+resetProfile.resetButton.label=Reset %S…
+resetProfile.resetButton.accesskey=e
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -53,16 +53,17 @@
   locale/@AB_CD@/global/printjoboptions.dtd             (%chrome/global/printjoboptions.dtd)
   locale/@AB_CD@/global/printPageSetup.dtd              (%chrome/global/printPageSetup.dtd)
   locale/@AB_CD@/global/printPreview.dtd                (%chrome/global/printPreview.dtd)
   locale/@AB_CD@/global/printPreviewProgress.dtd        (%chrome/global/printPreviewProgress.dtd)
   locale/@AB_CD@/global/printdialog.properties          (%chrome/global/printdialog.properties)
   locale/@AB_CD@/global/printProgress.dtd               (%chrome/global/printProgress.dtd)
   locale/@AB_CD@/global/regionNames.properties          (%chrome/global/regionNames.properties)
   locale/@AB_CD@/global/resetProfile.dtd                (%chrome/global/resetProfile.dtd)
+  locale/@AB_CD@/global/resetProfile.properties         (%chrome/global/resetProfile.properties)
   locale/@AB_CD@/global/dialog.properties               (%chrome/global/dialog.properties)
   locale/@AB_CD@/global/tree.dtd                        (%chrome/global/tree.dtd)
   locale/@AB_CD@/global/textcontext.dtd                 (%chrome/global/textcontext.dtd)
   locale/@AB_CD@/global/videocontrols.dtd               (%chrome/global/videocontrols.dtd)
   locale/@AB_CD@/global/viewSource.dtd                  (%chrome/global/viewSource.dtd)
   locale/@AB_CD@/global/viewSource.properties           (%chrome/global/viewSource.properties)
   locale/@AB_CD@/global/webapps.properties              (%chrome/global/webapps.properties)
   locale/@AB_CD@/global/wizard.dtd                      (%chrome/global/wizard.dtd)
--- a/toolkit/modules/Makefile.in
+++ b/toolkit/modules/Makefile.in
@@ -8,18 +8,24 @@ srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 ifdef MOZ_TOOLKIT_SEARCH
 DEFINES += -DMOZ_TOOLKIT_SEARCH
 endif
 
+DEFINES += \
+  -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
+  -DMOZ_BUILD_APP=$(MOZ_BUILD_APP) \
+  $(NULL)
+
 EXTRA_PP_JS_MODULES = \
   CertUtils.jsm \
+  ResetProfile.jsm \
   Services.jsm \
   Troubleshoot.jsm \
   UpdateChannel.jsm \
   WindowDraggingUtils.jsm \
   $(NULL)
 
 ifneq (Android,$(OS_TARGET))
 EXTRA_PP_JS_MODULES += LightweightThemeConsumer.jsm
copy from toolkit/content/resetProfile.js
copy to toolkit/modules/ResetProfile.jsm
--- a/toolkit/content/resetProfile.js
+++ b/toolkit/modules/ResetProfile.jsm
@@ -1,85 +1,84 @@
 /* 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/Services.jsm");
-
-let Cc = Components.classes;
-let Ci = Components.interfaces;
+"use strict";
 
-// based on onImportItemsPageShow from migration.js
-function populateResetPane(aContainerID) {
-  let resetProfileItems = document.getElementById(aContainerID);
-  try {
-    let dataTypes = getMigratedData();
-    for (let dataType of dataTypes) {
-      let label = document.createElement("label");
-      label.setAttribute("value", dataType);
-      resetProfileItems.appendChild(label);
-    }
-  } catch (ex) {
-    Cu.reportError(ex);
-  }
-}
+this.EXPORTED_SYMBOLS = ["ResetProfile"];
 
-function onResetProfileLoad() {
-  populateResetPane("migratedItems");
-}
-
-/**
- * Check if reset is supported for the currently running profile.
- *
- * @return boolean whether reset is supported.
- */
-function resetSupported() {
-  let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
-                       getService(Ci.nsIToolkitProfileService);
-  let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 #expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
 #expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
 
-  // Reset is only supported for the default profile if the self-migrator used for reset exists.
-  try {
-    return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
-             ("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
-  } catch (e) {
-    // Catch exception when there is no selected profile.
-    Cu.reportError(e);
-  }
-  return false;
-}
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.ResetProfile = {
+  /**
+   * Check if reset is supported for the currently running profile.
+   *
+   * @return boolean whether reset is supported.
+   */
+  resetSupported: function() {
+    let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
+                         getService(Ci.nsIToolkitProfileService);
+    let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
 
-function getMigratedData() {
-  Components.utils.import("resource:///modules/MigrationUtils.jsm");
+    // Reset is only supported for the default profile if the self-migrator used for reset exists.
+    try {
+      return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
+        ("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
+    } catch (e) {
+      // Catch exception when there is no selected profile.
+      Cu.reportError(e);
+    }
+    return false;
+  },
 
-#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
-#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
+  getMigratedData: function() {
+    Cu.import("resource:///modules/MigrationUtils.jsm");
+
+    // From migration.properties
+    const MIGRATED_TYPES = [
+      128,// Windows/Tabs
+      4,  // History and Bookmarks
+      16, // Passwords
+      8,  // Form History
+      2,  // Cookies
+    ];
 
-  // From migration.properties
-  const MIGRATED_TYPES = [
-    128,// Windows/Tabs
-    4,  // History and Bookmarks
-    16, // Passwords
-    8,  // Form History
-    2,  // Cookies
-  ];
+    // Loop over possible data to migrate to give the user a list of what will be preserved.
+    let dataTypes = [];
+    for (let itemID of MIGRATED_TYPES) {
+      try {
+        let typeName = MigrationUtils.getLocalizedString(itemID + "_" + MOZ_APP_NAME);
+        dataTypes.push(typeName);
+      } catch (x) {
+        // Catch exceptions when the string for a data type doesn't exist.
+        Cu.reportError(x);
+      }
+    }
+    return dataTypes;
+  },
 
-  // Loop over possible data to migrate to give the user a list of what will be preserved.
-  let dataTypes = [];
-  for (let itemID of MIGRATED_TYPES) {
-    try {
-      let typeName = MigrationUtils.getLocalizedString(itemID + "_" + MOZ_APP_NAME);
-      dataTypes.push(typeName);
-    } catch (x) {
-      // Catch exceptions when the string for a data type doesn't exist.
-      Components.utils.reportError(x);
-    }
-  }
-  return dataTypes;
-}
+  /**
+   * Ask the user if they wish to restart the application to reset the profile.
+   */
+  openConfirmationDialog: function(window) {
+    // Prompt the user to confirm.
+    let params = {
+      reset: false,
+    };
+    window.openDialog("chrome://global/content/resetProfile.xul", null,
+                      "chrome,modal,centerscreen,titlebar,dialog=yes", params);
+    if (!params.reset)
+      return;
 
-function onResetProfileAccepted() {
-  let retVals = window.arguments[0];
-  retVals.reset = true;
-}
+    // Set the reset profile environment variable.
+    let env = Cc["@mozilla.org/process/environment;1"]
+                .getService(Ci.nsIEnvironment);
+    env.set("MOZ_RESET_PROFILE_RESTART", "1");
+
+    let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
+    appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
+  },
+};