diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 7e635ae9b51..3530e3a4c8f 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -187,6 +187,7 @@ pref("browser.urlbar.matchOnlyTyped", false); pref("browser.chrome.site_icons", true); pref("browser.chrome.favicons", true); pref("browser.formfill.enable", true); +pref("browser.warnOnQuit", true); pref("browser.download.useDownloadDir", true); pref("browser.download.folderList", 0); diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index fc2b6cee709..c24e077a917 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -774,8 +774,6 @@ function BrowserStartup() { gBrowser = document.getElementById("content"); - window.tryToClose = WindowIsClosing; - var uriToLoad = null; // Check for window.arguments[0]. If present, use that for uriToLoad. if ("arguments" in window && window.arguments[0]) @@ -1215,6 +1213,14 @@ function BrowserShutdown() BrowserOffline.uninit(); + // Store current window position/size into the window attributes + // for persistence. + var win = document.documentElement; + win.setAttribute("x", window.screenX); + win.setAttribute("y", window.screenY); + win.setAttribute("height", window.outerHeight); + win.setAttribute("width", window.outerWidth); + var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(); var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator); var enumerator = windowManagerInterface.getEnumerator(null); @@ -1926,38 +1932,13 @@ function BrowserCloseTabOrWindow() } #endif - BrowserCloseWindow(); + closeWindow(true); } function BrowserTryToCloseWindow() { - //give tryToClose a chance to veto if it is defined - if (typeof(window.tryToClose) != "function" || window.tryToClose()) - BrowserCloseWindow(); -} - -function BrowserCloseWindow() -{ - // This code replicates stuff in BrowserShutdown(). It is here because - // window.screenX and window.screenY have real values. We need - // to fix this eventually but by replicating the code here, we - // provide a means of saving position (it just requires that the - // user close the window via File->Close (vs. close box). - - // Get the current window position/size. - var x = window.screenX; - var y = window.screenY; - var h = window.outerHeight; - var w = window.outerWidth; - - // Store these into the window attributes (for persistence). - var win = document.getElementById( "main-window" ); - win.setAttribute( "x", x ); - win.setAttribute( "y", y ); - win.setAttribute( "height", h ); - win.setAttribute( "width", w ); - - closeWindow(true); + if (WindowIsClosing()) + window.close(); // WindowIsClosing does all the necessary checks } function loadURI(uri, referrer, postData, allowThirdPartyFixup) @@ -5100,7 +5081,7 @@ function WindowIsClosing() var browser = getBrowser(); var cn = browser.tabContainer.childNodes; var numtabs = cn.length; - var reallyClose = browser.warnAboutClosingTabs(true); + var reallyClose = true; for (var i = 0; reallyClose && i < numtabs; ++i) { var ds = browser.getBrowserForTab(cn[i]).docShell; @@ -5109,10 +5090,16 @@ function WindowIsClosing() reallyClose = false; } - if (reallyClose) - return closeWindow(false); + if (!reallyClose) + return false; - return reallyClose; + // closeWindow takes a second optional function argument to open up a + // window closing warning dialog if we're not quitting. (Quitting opens + // up another dialog so we don't need to.) + return closeWindow(false, + function () { + return browser.warnAboutClosingTabs(true); + }); } var MailIntegration = { diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index a07d62db4d3..63650662959 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -45,6 +45,8 @@ function BrowserGlue() { } BrowserGlue.prototype = { + _saveSession: false, + QueryInterface: function(iid) { xpcomCheckInterfaces(iid, kServiceIIds, Components.results.NS_ERROR_NO_INTERFACE); @@ -74,6 +76,16 @@ BrowserGlue.prototype = { cs.logStringMessage(null); // clear the console (in case it's open) cs.reset(); break; + case "quit-application-requested": + this._onQuitRequest(subject); + break; + case "quit-application-granted": + if (this._saveSession) { + var prefService = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + prefService.setBoolPref("browser.sessionstore.resume_session_once", true); + } + break; } } , @@ -88,6 +100,8 @@ BrowserGlue.prototype = { osvr.addObserver(this, "xpcom-shutdown", false); osvr.addObserver(this, "final-ui-startup", false); osvr.addObserver(this, "browser:purge-session-history", false); + osvr.addObserver(this, "quit-application-requested", false); + osvr.addObserver(this, "quit-application-granted", false); }, // cleanup (called on application shutdown) @@ -101,6 +115,8 @@ BrowserGlue.prototype = { osvr.removeObserver(this, "xpcom-shutdown"); osvr.removeObserver(this, "final-ui-startup"); osvr.removeObserver(this, "browser:purge-session-history"); + osvr.removeObserver(this, "quit-application-requested"); + osvr.removeObserver(this, "quit-application-granted"); }, // profile startup handler (contains profile initialization routines) @@ -169,6 +185,92 @@ BrowserGlue.prototype = { } }, + _onQuitRequest: function(aCancelQuit) + { + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var windowcount = 0; + var pagecount = 0; + var browserEnum = wm.getEnumerator("navigator:browser"); + while (browserEnum.hasMoreElements()) { + windowcount++; + + var browser = browserEnum.getNext(); + var tabbrowser = browser.document.getElementById("content"); + if (tabbrowser) + pagecount += tabbrowser.browsers.length; + } + + this._saveSession = false; + if (pagecount < 2) + return; + + var prefService = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + var showPrompt = true; + try { + if (prefService.getIntPref("browser.startup.page") == 3 || + prefService.getBoolPref("browser.sessionstore.resume_session_once")) + showPrompt = false; + else + showPrompt = prefService.getBoolPref("browser.warnOnQuit"); + } catch (ex) {} + + var buttonChoice = 0; + if (showPrompt) { + var bundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); + var quitBundle = bundleService.createBundle("chrome://browser/locale/quitDialog.properties"); + var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties"); + + var appName = brandBundle.GetStringFromName("brandShortName"); + var quitDialogTitle = quitBundle.formatStringFromName("quitDialogTitle", + [appName], 1); + var quitTitle = quitBundle.GetStringFromName("quitTitle"); + var cancelTitle = quitBundle.GetStringFromName("cancelTitle"); + var saveTitle = quitBundle.GetStringFromName("saveTitle"); + var neverAskText = quitBundle.GetStringFromName("neverAsk"); + + if (windowcount == 1) + var message = quitBundle.formatStringFromName("messageNoWindows", + [appName], 1); + else + var message = quitBundle.formatStringFromName("message", + [appName], 1); + + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + + var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 + + promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 + + promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2 + + promptService.BUTTON_POS_0_DEFAULT; + var neverAsk = {value:false}; + buttonChoice = promptService.confirmEx(null, quitDialogTitle, message, + flags, quitTitle, cancelTitle, saveTitle, + neverAskText, neverAsk); + + switch (buttonChoice) { + case 0: + if (neverAsk.value) + prefService.setBoolPref("browser.warnOnQuit", false); + break; + case 1: + aCancelQuit.QueryInterface(Components.interfaces.nsISupportsPRBool); + aCancelQuit.data = true; + break; + case 2: + // could also set browser.warnOnQuit to false here, + // but not setting it is a little safer. + if (neverAsk.value) + prefService.setIntPref("browser.startup.page", 3); + break; + } + + this._saveSession = buttonChoice == 2; + } + }, + // returns the (cached) Sanitizer constructor get Sanitizer() { diff --git a/browser/locales/en-US/chrome/browser/quitDialog.properties b/browser/locales/en-US/chrome/browser/quitDialog.properties new file mode 100644 index 00000000000..66200e915ec --- /dev/null +++ b/browser/locales/en-US/chrome/browser/quitDialog.properties @@ -0,0 +1,8 @@ +quitDialogTitle=Quit %S + +quitTitle=&Quit +cancelTitle=&Cancel +saveTitle=&Save and quit +neverAsk=Do not ask next time +message=Do you want %S to save your tabs and windows for the next time it starts? +messageNoWindows=Do you want %S to save your tabs for the next time it starts? diff --git a/browser/locales/jar.mn b/browser/locales/jar.mn index 4a27f8f61bd..a92c3d5e94f 100644 --- a/browser/locales/jar.mn +++ b/browser/locales/jar.mn @@ -15,6 +15,7 @@ locale/browser/pageInfo.properties (%chrome/browser/pageInfo.properties) locale/browser/pageReport.dtd (%chrome/browser/pageReport.dtd) locale/browser/pageReportFirstTime.dtd (%chrome/browser/pageReportFirstTime.dtd) + locale/browser/quitDialog.properties (%chrome/browser/quitDialog.properties) * locale/browser/safeMode.dtd (%chrome/browser/safeMode.dtd) locale/browser/sanitize.dtd (%chrome/browser/sanitize.dtd) locale/browser/search.properties (%chrome/browser/search.properties) diff --git a/toolkit/content/globalOverlay.js b/toolkit/content/globalOverlay.js index 6a74279a990..57b7e738e5b 100644 --- a/toolkit/content/globalOverlay.js +++ b/toolkit/content/globalOverlay.js @@ -1,4 +1,4 @@ -function closeWindow(aClose) +function closeWindow(aClose, aPromptFunction) { var windowCount = 0; var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] @@ -17,6 +17,8 @@ function closeWindow(aClose) if (windowCount == 1 && !canQuitApplication()) return false; #endif + if (windowCount != 1 && typeof(aPromptFunction) == "function" && !aPromptFunction()) + return false; if (aClose) window.close();