Bug 1506173 - use separate pref for warning on quit with sessionstore enabled, r=jaws,flod
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 30 Nov 2018 20:21:59 +0000
changeset 508271 b9dcfde98e47814fdcefc6329e65b8337c74ae3d
parent 508270 3a4e05f965e943f4e1181a4270dacc98a491cac8
child 508272 65bf3e37a4409f3ca0350366d7be19368adfa21b
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, flod
bugs1506173
milestone65.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 1506173 - use separate pref for warning on quit with sessionstore enabled, r=jaws,flod Differential Revision: https://phabricator.services.mozilla.com/D13535
browser/app/profile/firefox.js
browser/base/content/tabbrowser.js
browser/components/nsBrowserGlue.js
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/main.xul
browser/locales/en-US/browser/preferences/preferences.ftl
browser/locales/en-US/chrome/browser/tabbrowser.properties
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -852,16 +852,19 @@ pref("browser.sessionstore.debug", false
 pref("browser.sessionstore.debug.no_auto_updates", false);
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
 // Maximum number of bytes of DOMSessionStorage data we collect per origin.
 pref("browser.sessionstore.dom_storage_limit", 2048);
 // Amount of failed SessionFile writes until we restart the worker.
 pref("browser.sessionstore.max_write_failures", 5);
 
+// Whether to warn the user when quitting, even though their tabs will be restored.
+pref("browser.sessionstore.warnOnQuit", false);
+
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
 // Whether history is enabled or not.
 pref("places.history.enabled", true);
 
 // the (maximum) number of the recent visits to sample
 // when calculating frecency
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2656,17 +2656,17 @@ window._gBrowser = {
       [contextTab];
     for (let tab of tabs) {
       if (tab._tPos < this.tabs.length - 1) {
         this.moveTabTo(tab, this.tabs.length - 1);
       }
     }
   },
 
-  warnAboutClosingTabs(tabsToClose, aCloseTabs, aOptionalMessage) {
+  warnAboutClosingTabs(tabsToClose, aCloseTabs) {
     if (tabsToClose <= 1)
       return true;
 
     const pref = aCloseTabs == this.closingTabsEnum.ALL ?
       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
     var shouldPrompt = Services.prefs.getBoolPref(pref);
     if (!shouldPrompt)
       return true;
@@ -2677,34 +2677,30 @@ window._gBrowser = {
     var warnOnClose = { value: true };
 
     // focus the window before prompting.
     // this will raise any minimized window, which will
     // make it obvious which window the prompt is for and will
     // solve the problem of windows "obscuring" the prompt.
     // see bug #350299 for more details
     window.focus();
-    var warningMessage;
-    if (aOptionalMessage) {
-      warningMessage = aOptionalMessage;
-    } else {
-      warningMessage =
-        PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
-          .replace("#1", tabsToClose);
-    }
+    let warningMessage = gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple");
+    warningMessage = PluralForm.get(tabsToClose, warningMessage).replace("#1", tabsToClose);
+    let flags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
+      (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1);
+    let checkboxLabel = aCloseTabs == this.closingTabsEnum.ALL ?
+      gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null;
     var buttonPressed =
       ps.confirmEx(window,
-        gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
+        gTabBrowserBundle.GetStringFromName("tabs.closeTitleTabs"),
         warningMessage,
-        (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
-        (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
+        flags,
         gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
         null, null,
-        aCloseTabs == this.closingTabsEnum.ALL ?
-        gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
+        checkboxLabel,
         warnOnClose);
     var reallyClose = (buttonPressed == 0);
 
     // don't set the pref unless they press OK and it's false
     if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
       Services.prefs.setBoolPref(pref, false);
 
     return reallyClose;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1753,19 +1753,23 @@ BrowserGlue.prototype = {
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
     // 1. There is only 1 tab open in 1 window
-    // 2. browser.warnOnQuit or browser.warnOnClose == false
+    // 2. browser.warnOnQuit == false
     // 3. The browser is currently in Private Browsing mode
     // 4. The browser will be restarted.
+    // 5. The user has automatic session restore enabled and
+    //    browser.sessionstore.warnOnQuit is not set to true.
+    // 6. The user doesn't have automatic session restore enabled
+    //    and browser.tabs.warnOnClose is not set to true.
     //
     // Otherwise, we will show the "closing multiple tabs" dialog.
     //
     // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
     // "the last window is closing but we're not quitting (a non-browser window is open)"
     // and also "we're quitting by closing the last window".
 
     if (aQuitType == "restart" || aQuitType == "os-restart")
@@ -1787,38 +1791,75 @@ BrowserGlue.prototype = {
 
     if (pagecount < 2)
       return;
 
     if (!aQuitType)
       aQuitType = "quit";
 
     // browser.warnOnQuit is a hidden global boolean to override all quit prompts
-    // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
-    if (!Services.prefs.getBoolPref("browser.warnOnQuit") ||
-        !Services.prefs.getBoolPref("browser.tabs.warnOnClose"))
+    if (!Services.prefs.getBoolPref("browser.warnOnQuit"))
       return;
 
+    // If we're going to automatically restore the session, only warn if the user asked for that.
+    let sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 ||
+                                Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
+    // In the sessionWillBeRestored case, we only check the sessionstore-specific pref:
+    if (sessionWillBeRestored) {
+      if (!Services.prefs.getBoolPref("browser.sessionstore.warnOnQuit", false)) {
+        return;
+      }
+    // Otherwise, we check browser.tabs.warnOnClose
+    } else if (!Services.prefs.getBoolPref("browser.tabs.warnOnClose")) {
+      return;
+    }
+
     let win = BrowserWindowTracker.getTopWindow();
 
-    // warnAboutClosingTabs checks browser.tabs.warnOnClose and returns if it's
-    // ok to close the window. It doesn't actually close the window.
-    if (windowcount == 1) {
-      aCancelQuit.data =
-        !win.gBrowser.warnAboutClosingTabs(pagecount, win.gBrowser.closingTabsEnum.ALL);
-    } else {
-      // More than 1 window. Compose our own message.
+    let warningMessage;
+    // More than 1 window. Compose our own message.
+    if (windowcount > 1) {
       let tabSubstring = gTabbrowserBundle.GetStringFromName("tabs.closeWarningMultipleWindowsTabSnippet");
       tabSubstring = PluralForm.get(pagecount, tabSubstring).replace(/#1/, pagecount);
-      let windowString = gTabbrowserBundle.GetStringFromName("tabs.closeWarningMultipleWindows");
+
+      let stringID = sessionWillBeRestored ? "tabs.closeWarningMultipleWindowsSessionRestore"
+                                           : "tabs.closeWarningMultipleWindows";
+      let windowString = gTabbrowserBundle.GetStringFromName(stringID);
       windowString = PluralForm.get(windowcount, windowString).replace(/#1/, windowcount);
-      windowString = windowString.replace(/%(?:1\$)?S/i, tabSubstring);
-      aCancelQuit.data =
-        !win.gBrowser.warnAboutClosingTabs(pagecount, win.gBrowser.closingTabsEnum.ALL, windowString);
+      warningMessage = windowString.replace(/%(?:1\$)?S/i, tabSubstring);
+    } else {
+      let stringID = sessionWillBeRestored ? "tabs.closeWarningMultipleSessionRestore"
+                                           : "tabs.closeWarningMultiple";
+      warningMessage = gTabbrowserBundle.GetStringFromName(stringID);
+      warningMessage = PluralForm.get(pagecount, warningMessage).replace("#1", pagecount);
     }
+
+    let warnOnClose = {value: true};
+    let titleId = AppConstants.platform == "win" ? "tabs.closeAndQuitTitleTabsWin"
+                                                 : "tabs.closeAndQuitTitleTabs";
+    let flags = (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+      (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1);
+    // Only display the checkbox in the non-sessionrestore case.
+    let checkboxLabel = !sessionWillBeRestored ?
+      gTabbrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null;
+
+    // buttonPressed will be 0 for closing, 1 for cancel (don't close/quit)
+    let buttonPressed = Services.prompt.confirmEx(win,
+      gTabbrowserBundle.GetStringFromName(titleId),
+      warningMessage, flags,
+      gTabbrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
+      null, null,
+      checkboxLabel,
+      warnOnClose);
+    // If the user has unticked the box, and has confirmed closing, stop showing
+    // the warning.
+    if (!sessionWillBeRestored && buttonPressed == 0 && !warnOnClose.value) {
+      Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+    }
+    aCancelQuit.data = buttonPressed != 0;
   },
 
   _showUpdateNotification: function BG__showUpdateNotification() {
     Services.prefs.clearUserPref("app.update.postupdate");
 
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     try {
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -72,16 +72,17 @@ if (AppConstants.MOZ_DEV_EDITION) {
   ChromeUtils.defineModuleGetter(this, "FxAccounts",
     "resource://gre/modules/FxAccounts.jsm");
 }
 
 Preferences.addAll([
   // Startup
   { id: "browser.startup.page", type: "int" },
   { id: "browser.privatebrowsing.autostart", type: "bool" },
+  { id: "browser.sessionstore.warnOnQuit", type: "bool" },
 
   // Downloads
   { id: "browser.download.useDownloadDir", type: "bool" },
   { id: "browser.download.folderList", type: "int" },
   { id: "browser.download.dir", type: "file" },
 
   /* Tab preferences
   Preferences:
@@ -730,24 +731,32 @@ var gMainPane = {
    * on the value of the browser.privatebrowsing.autostart pref.
    */
   updateBrowserStartupUI() {
     const pbAutoStartPref = Preferences.get("browser.privatebrowsing.autostart");
     const startupPref = Preferences.get("browser.startup.page");
 
     let newValue;
     let checkbox = document.getElementById("browserRestoreSession");
+    let warnOnQuitCheckbox = document.getElementById("browserRestoreSessionQuitWarning");
     if (pbAutoStartPref.value || startupPref.locked) {
       checkbox.setAttribute("disabled", "true");
+      warnOnQuitCheckbox.setAttribute("disabled", "true");
     } else {
       checkbox.removeAttribute("disabled");
     }
     newValue = pbAutoStartPref.value ? false : startupPref.value === this.STARTUP_PREF_RESTORE_SESSION;
     if (checkbox.checked !== newValue) {
       checkbox.checked = newValue;
+      let warnOnQuitPref = Preferences.get("browser.sessionstore.warnOnQuit");
+      if (newValue && !warnOnQuitPref.locked && !pbAutoStartPref.value) {
+        warnOnQuitCheckbox.removeAttribute("disabled");
+      } else {
+        warnOnQuitCheckbox.setAttribute("disabled", "true");
+      }
     }
   },
 
   initBrowserLocale() {
     gMainPane.setBrowserLocales(Services.locale.appLocaleAsBCP47);
   },
 
   /**
@@ -885,24 +894,30 @@ var gMainPane = {
     this.showConfirmLanguageChangeMessageBar(locales);
   },
 
   onBrowserRestoreSessionChange(event) {
     const value = event.target.checked;
     const startupPref = Preferences.get("browser.startup.page");
     let newValue;
 
+    let warnOnQuitCheckbox = document.getElementById("browserRestoreSessionQuitWarning");
     if (value) {
       // We need to restore the blank homepage setting in our other pref
       if (startupPref.value === this.STARTUP_PREF_BLANK) {
         Preferences.get("browser.startup.homepage").value = "about:blank";
       }
       newValue = this.STARTUP_PREF_RESTORE_SESSION;
+      let warnOnQuitPref = Preferences.get("browser.sessionstore.warnOnQuit");
+      if (!warnOnQuitPref.locked) {
+        warnOnQuitCheckbox.removeAttribute("disabled");
+      }
     } else {
       newValue = this.STARTUP_PREF_HOMEPAGE;
+      warnOnQuitCheckbox.setAttribute("disabled", "true");
     }
     startupPref.value = newValue;
   },
 
   // TABS
 
   /*
    * Preferences:
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -41,16 +41,22 @@
       </deck>
     </hbox>
   </vbox>
 #endif
 
   <vbox id="startupPageBox">
     <checkbox id="browserRestoreSession"
               data-l10n-id="startup-restore-previous-session"/>
+    <hbox class="indent">
+      <checkbox id="browserRestoreSessionQuitWarning"
+                preference="browser.sessionstore.warnOnQuit"
+                disabled="true"
+                data-l10n-id="startup-restore-warn-on-quit"/>
+    </hbox>
   </vbox>
 
 #ifdef HAVE_SHELL_SERVICE
   <vbox id="defaultBrowserBox">
     <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
               data-l10n-id="always-check-default"/>
     <deck id="setDefaultPane">
       <hbox align="center" class="indent">
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -157,16 +157,19 @@ is-not-default = { -brand-short-name } i
 set-as-my-default-browser =
     .label = Make Default…
     .accesskey = D
 
 startup-restore-previous-session =
     .label = Restore previous session
     .accesskey = s
 
+startup-restore-warn-on-quit =
+    .label = Warn you when quitting the browser
+
 disable-extension =
     .label = Disable Extension
 
 tabs-group-header = Tabs
 
 ctrl-tab-recently-used-order =
     .label = Ctrl+Tab cycles through tabs in recently used order
     .accesskey = T
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties
+++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties
@@ -1,35 +1,52 @@
 # 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/.
 
 tabs.emptyTabTitle=New Tab
 tabs.emptyPrivateTabTitle=Private Browsing
 tabs.closeTab=Close Tab
 tabs.close=Close
-tabs.closeWarningTitle=Confirm close
+tabs.closeTitleTabs=Close tabs?
+tabs.closeAndQuitTitleTabs=Quit and close tabs?
+tabs.closeAndQuitTitleTabsWin=Exit and close tabs?
 # LOCALIZATION NOTE (tabs.closeWarningMultiple):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple tabs.
 tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
+# LOCALIZATION NOTE (tabs.closeWarningMultipleSessionRestore):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The singular form is not considered since this string is used only for
+# multiple tabs.
+tabs.closeWarningMultipleSessionRestore=;You are about to close #1 tabs. These tabs will be restored when you restart. Are you sure you want to continue?
 tabs.closeButtonMultiple=Close tabs
 tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs
 
 # LOCALIZATION NOTE (tabs.closeWarningMultipleWindows):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
-# The singular form is not considered since this string is used only for
+# The forms for 0 or 1 items are not considered since this string is used only for
 # multiple windows. The %S replacement form will be replaced with the contents
 # of tabs.closeWarningMultipleWindowsTabSnippet, which will contain the number
 # of tabs in these windows.
 # Note that every one of these plural forms must contain one instance of '%S'.
 tabs.closeWarningMultipleWindows=;You are about to close #1 windows %S. Are you sure you want to continue?
+# LOCALIZATION NOTE (tabs.closeWarningMultipleWindowsSessionRestore):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The forms for 0 or 1 items are not considered since this string is used only for
+# multiple windows. The %S replacement form will be replaced with the contents
+# of tabs.closeWarningMultipleWindowsTabSnippet, which will contain the number
+# of tabs in these windows.
+# Note that every one of these plural forms must contain one instance of '%S'.
+tabs.closeWarningMultipleWindowsSessionRestore=;You are about to close #1 windows %S. These tabs will be restored when you restart. Are you sure you want to continue?
 
 # LOCALIZATION NOTE (tabs.closeWarningMultipleWindowsTabSnippet):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple windows which must contain multiple tabs (in total).
 # This string will be inserted in tabs.closeWarningMultipleWindows
 tabs.closeWarningMultipleWindowsTabSnippet=;with #1 tabs