Bug 1095739 - Allow a "new user" experience to happen subsequent to Firefox being uninstalled. r?gijs,jimm
MozReview-Commit-ID: 3Atu6pU4IH4
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -151,16 +151,19 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
+ "resource://gre/modules/WindowsRegistry.jsm");
+
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
XPCOMUtils.defineLazyServiceGetter(this, "AlertsService",
"@mozilla.org/alerts-service;1", "nsIAlertsService");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@@ -900,44 +903,55 @@ 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.
+ * Show a notification bar offering a reset.
+ *
+ * @param reason
+ * String of either "unused" or "uninstall", specifying the reason
+ * why a profile reset is offered.
*/
- _resetUnusedProfileNotification: function () {
+ _resetProfileNotification: function (reason) {
let win = RecentWindow.getMostRecentBrowserWindow();
if (!win)
return;
Cu.import("resource://gre/modules/ResetProfile.jsm");
if (!ResetProfile.resetSupported())
return;
let productName = gBrandBundle.GetStringFromName("brandShortName");
let resetBundle = Services.strings
.createBundle("chrome://global/locale/resetProfile.properties");
- let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
+ let message;
+ if (reason == "unused") {
+ message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
+ } else if (reason == "uninstall") {
+ message = resetBundle.formatStringFromName("resetUninstalled.message", [productName, productName], 2);
+ } else {
+ throw new Error(`Unknown reason (${reason}) given to _resetProfileNotification.`);
+ }
let buttons = [
{
label: resetBundle.formatStringFromName("refreshProfile.resetButton.label", [productName], 1),
accessKey: resetBundle.GetStringFromName("refreshProfile.resetButton.accesskey"),
callback: function () {
ResetProfile.openConfirmationDialog(win);
}
},
];
let nb = win.document.getElementById("global-notificationbox");
- nb.appendNotification(message, "reset-unused-profile",
+ nb.appendNotification(message, "reset-profile-notification",
"chrome://global/skin/icons/question-16.png",
nb.PRIORITY_INFO_LOW, buttons);
},
_notifyUnsignedAddonsDisabled: function () {
let win = RecentWindow.getMostRecentBrowserWindow();
if (!win)
return;
@@ -1025,19 +1039,40 @@ BrowserGlue.prototype = {
// 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 lastUse = Services.appinfo.replacedLockTime;
let disableResetPrompt = false;
try {
disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt");
} catch(e) {}
+
if (!disableResetPrompt && lastUse &&
Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
- this._resetUnusedProfileNotification();
+ this._resetProfileNotification("unused");
+ } else if (AppConstants.platform == "win") {
+ // Check if we were just re-installed and offer Firefox Reset
+ let updateChannel;
+ try {
+ updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
+ } catch (ex) {}
+ if (updateChannel) {
+ let uninstalledValue =
+ WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Mozilla\\Firefox",
+ `Uninstalled-${updateChannel}`);
+ let removalSuccessful =
+ WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Mozilla\\Firefox",
+ `Uninstalled-${updateChannel}`);
+ if (!disableResetPrompt && removalSuccessful &&
+ uninstalledValue == "True") {
+ this._resetProfileNotification("uninstall");
+ }
+ }
}
this._checkForOldBuildUpdates();
this._firstWindowTelemetry(aWindow);
this._firstWindowLoaded();
},
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -445,16 +445,23 @@ Section "Uninstall"
${EndIf}
${EndIf}
; Refresh desktop icons otherwise the start menu internet item won't be
; removed and other ugly things will happen like recreation of the app's
; clients registry key by the OS under some conditions.
System::Call "shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i 0, i 0, i 0)"
+ ; Users who uninstall then reinstall expecting Firefox to use a clean profile
+ ; may be surprised during first-run. This key is checked during startup of Firefox and
+ ; subsequently deleted after checking. If the value is found during startup
+ ; the browser will offer to Reset Firefox. We use the UpdateChannel to match
+ ; uninstalls of Firefox-release with reinstalls of Firefox-release, for example.
+ WriteRegStr HKCU "Software\Mozilla\Firefox" "Uninstalled-${UpdateChannel}" "True"
+
!ifdef MOZ_MAINTENANCE_SERVICE
; Get the path the allowed cert is at and remove it
; Keep this block of code last since it modfies the reg view
ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
Pop $MaintCertKey
${If} $MaintCertKey != ""
; Always use the 64bit registry for certs on 64bit systems.
${If} ${RunningX64}
--- a/toolkit/locales/en-US/chrome/global/resetProfile.properties
+++ b/toolkit/locales/en-US/chrome/global/resetProfile.properties
@@ -1,12 +1,14 @@
# 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 (resetUninstalled.message): %S is brandShortName.
+resetUninstalled.message=It looks like you've reinstalled %S. Do you want to clean it up for a fresh, like-new experience? And by the way, thanks for using %S!
# LOCALIZATION NOTE (refreshProfile.resetButton.label): %S is brandShortName.
refreshProfile.resetButton.label=Refresh %S…
refreshProfile.resetButton.accesskey=e
--- a/toolkit/modules/WindowsRegistry.jsm
+++ b/toolkit/modules/WindowsRegistry.jsm
@@ -59,26 +59,30 @@ var WindowsRegistry = {
* The root registry to use.
* @param aPath
* The registry path to the key.
* @param aKey
* The key name.
* @param [aRegistryNode=0]
* Optionally set to nsIWindowsRegKey.WOW64_64 (or nsIWindowsRegKey.WOW64_32)
* to access a 64-bit (32-bit) key from either a 32-bit or 64-bit application.
+ * @return True if the key was removed or never existed, false otherwise.
*/
removeRegKey: function(aRoot, aPath, aKey, aRegistryNode=0) {
let registry = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(Ci.nsIWindowsRegKey);
+ let result = true;
try {
let mode = Ci.nsIWindowsRegKey.ACCESS_QUERY_VALUE |
Ci.nsIWindowsRegKey.ACCESS_SET_VALUE |
aRegistryNode;
registry.open(aRoot, aPath, mode);
if (registry.hasValue(aKey)) {
registry.removeValue(aKey);
+ result = !registry.hasValue(aKey);
}
} catch (ex) {
} finally {
registry.close();
+ return result;
}
}
};