diff --git a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js index 9af7527725a6..ad206dfda94a 100644 --- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js +++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js @@ -124,6 +124,9 @@ PrivateBrowsingService.prototype = { // List of view source window URIs for restoring later _viewSrcURLs: [], + // List of nsIXULWindows we are going to be closing during the transition + _windowsToClose: [], + // XPCOM registration classDescription: "PrivateBrowsing Service", contractID: "@mozilla.org/privatebrowsing;1", @@ -208,12 +211,20 @@ PrivateBrowsingService.prototype = { // just in case the only remaining window after setBrowserState is different. // it probably shouldn't be with the current sessionstore impl, but we shouldn't // rely on behaviour the API doesn't guarantee - let browser = this._getBrowserWindow().gBrowser; + browserWindow = this._getBrowserWindow(); + let browser = browserWindow.gBrowser; // this ensures a clean slate from which to transition into or out of // private browsing browser.addTab(); + browser.getBrowserForTab(browser.tabContainer.firstChild).stop(); browser.removeTab(browser.tabContainer.firstChild); + browserWindow.getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIXULWindow) + .docShell.contentViewer.resetCloseWindow(); } } } @@ -298,6 +309,20 @@ PrivateBrowsingService.prototype = { getMostRecentWindow("navigator:browser"); }, + _ensureCanCloseWindows: function PBS__ensureCanCloseWindows() { + let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); + let windowsEnum = windowMediator.getXULWindowEnumerator("navigator:browser"); + + while (windowsEnum.hasMoreElements()) { + let win = windowsEnum.getNext().QueryInterface(Ci.nsIXULWindow); + if (win.docShell.contentViewer.permitUnload(true)) + this._windowsToClose.push(win); + else + throw Cr.NS_ERROR_ABORT; + } + }, + _closePageInfoWindows: function PBS__closePageInfoWindows() { let pageInfoEnum = Cc["@mozilla.org/appshell/window-mediator;1"]. getService(Ci.nsIWindowMediator). @@ -401,6 +426,8 @@ PrivateBrowsingService.prototype = { return; } + this._ensureCanCloseWindows(); + this._autoStarted = this._prefs.getBoolPref("browser.privatebrowsing.autostart"); this._inPrivateBrowsing = val != false; @@ -422,9 +449,16 @@ PrivateBrowsingService.prototype = { this._onAfterPrivateBrowsingModeChange(); } } catch (ex) { - Cu.reportError("Exception thrown while processing the " + - "private browsing mode change request: " + ex.toString()); + // We aborted the transition to/from private browsing, we must restore the + // beforeunload handling on all the windows for which we switched it off. + for (let i = 0; i < this._windowsToClose.length; i++) + this._windowsToClose[i].docShell.contentViewer.resetCloseWindow(); + // We don't log an error when the transition is canceled from beforeunload + if (ex != Cr.NS_ERROR_ABORT) + Cu.reportError("Exception thrown while processing the " + + "private browsing mode change request: " + ex.toString()); } finally { + this._windowsToClose = []; this._alreadyChangingMode = false; } }, diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 62fe8284383e..639b4511ff34 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -1870,7 +1870,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) if (cv) { PRBool okToUnload; - rv = cv->PermitUnload(&okToUnload); + rv = cv->PermitUnload(PR_FALSE, &okToUnload); if (NS_SUCCEEDED(rv) && !okToUnload) { // We don't want to unload, so stop here, but don't throw an diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index b08ba613ea01..31a0598b2f2f 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -6149,7 +6149,7 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, // in the current document. PRBool okToUnload; - rv = mContentViewer->PermitUnload(&okToUnload); + rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload); if (NS_SUCCEEDED(rv) && !okToUnload) { // The user chose not to unload the page, interrupt the load. @@ -7939,7 +7939,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, // protocol handler deals with this for javascript: URLs. if (!bIsJavascript && mContentViewer) { PRBool okToUnload; - rv = mContentViewer->PermitUnload(&okToUnload); + rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload); if (NS_SUCCEEDED(rv) && !okToUnload) { // The user chose not to unload the page, interrupt the diff --git a/docshell/base/nsIContentViewer.idl b/docshell/base/nsIContentViewer.idl index 87ce75361be0..6c33003f804c 100644 --- a/docshell/base/nsIContentViewer.idl +++ b/docshell/base/nsIContentViewer.idl @@ -13,7 +13,7 @@ struct nsIntRect; [ptr] native nsIWidgetPtr(nsIWidget); [ref] native nsIntRectRef(nsIntRect); -[scriptable, uuid(c9aba5da-7d8b-46a8-87cd-9ab7e16480b8)] +[scriptable, uuid(08665a60-b398-11de-8a39-0800200c9a66)] interface nsIContentViewer : nsISupports { @@ -24,7 +24,27 @@ interface nsIContentViewer : nsISupports void loadStart(in nsISupports aDoc); void loadComplete(in unsigned long aStatus); - boolean permitUnload(); + + /** + * Checks if the document wants to prevent unloading by firing beforeunload on + * the document, and if it does, prompts the user. The result is returned. + * + * @param aCallerClosesWindow indicates that the current caller will close the + * window. If the method returns true, all subsequent calls will be + * ignored. + */ + boolean permitUnload([optional] in boolean aCallerClosesWindow); + + /** + * Works in tandem with permitUnload, if the caller decides not to close the + * window it indicated it will, it is the caller's responsibility to reset + * that with this method. + * + * @Note this method is only meant to be called on documents for which the + * caller has indicated that it will close the window. If that is not the case + * the behavior of this method is undefined. + */ + void resetCloseWindow(); void pageHide(in boolean isUnload); /** diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 0221541922ed..ad292cefb041 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -5540,7 +5540,7 @@ nsGlobalWindow::Close() if (!mInClose && !mIsClosed && cv) { PRBool canClose; - rv = cv->PermitUnload(&canClose); + rv = cv->PermitUnload(PR_FALSE, &canClose); if (NS_SUCCEEDED(rv) && !canClose) return NS_OK; diff --git a/dom/src/jsurl/nsJSProtocolHandler.cpp b/dom/src/jsurl/nsJSProtocolHandler.cpp index 04d52700b06b..962d2911cfa1 100644 --- a/dom/src/jsurl/nsJSProtocolHandler.cpp +++ b/dom/src/jsurl/nsJSProtocolHandler.cpp @@ -784,7 +784,7 @@ nsJSChannel::EvaluateScript() if (cv) { PRBool okToUnload; - if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && + if (NS_SUCCEEDED(cv->PermitUnload(PR_FALSE, &okToUnload)) && !okToUnload) { // The user didn't want to unload the current // page, translate this into an undefined diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 977b8b944693..cf16dd60ade3 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -497,6 +497,7 @@ protected: nsCString mPrevDocCharacterSet; PRPackedBool mIsPageMode; + PRPackedBool mCallerIsClosingWindow; }; @@ -528,6 +529,7 @@ void DocumentViewerImpl::PrepareToStartLoad() mStopped = PR_FALSE; mLoaded = PR_FALSE; mDeferredWindowClose = PR_FALSE; + mCallerIsClosingWindow = PR_FALSE; #ifdef NS_PRINTING mPrintIsPending = PR_FALSE; @@ -1083,11 +1085,11 @@ DocumentViewerImpl::LoadComplete(nsresult aStatus) } NS_IMETHODIMP -DocumentViewerImpl::PermitUnload(PRBool *aPermitUnload) +DocumentViewerImpl::PermitUnload(PRBool aCallerClosesWindow, PRBool *aPermitUnload) { *aPermitUnload = PR_TRUE; - if (!mDocument || mInPermitUnload) { + if (!mDocument || mInPermitUnload || mCallerIsClosingWindow) { return NS_OK; } @@ -1193,15 +1195,47 @@ DocumentViewerImpl::PermitUnload(PRBool *aPermitUnload) if (docShell) { nsCOMPtr cv; - docShell->GetContentViewer(getter_AddRefs(cv)); + docShell->GetContentViewer(getter_AddRefs(cv)); - if (cv) { - cv->PermitUnload(aPermitUnload); + if (cv) { + cv->PermitUnload(aCallerClosesWindow, aPermitUnload); } } } } + if (aCallerClosesWindow && *aPermitUnload) + mCallerIsClosingWindow = PR_TRUE; + + return NS_OK; +} + +NS_IMETHODIMP +DocumentViewerImpl::ResetCloseWindow() +{ + mCallerIsClosingWindow = PR_FALSE; + + nsCOMPtr docShellNode(do_QueryReferent(mContainer)); + if (docShellNode) { + PRInt32 childCount; + docShellNode->GetChildCount(&childCount); + + for (PRInt32 i = 0; i < childCount; ++i) { + nsCOMPtr item; + docShellNode->GetChildAt(i, getter_AddRefs(item)); + + nsCOMPtr docShell(do_QueryInterface(item)); + + if (docShell) { + nsCOMPtr cv; + docShell->GetContentViewer(getter_AddRefs(cv)); + + if (cv) { + cv->ResetCloseWindow(); + } + } + } + } return NS_OK; }