Bug 498648 - Start private browsing while editing a message, cancel, doesn't cancel private browsing; r=bz,jst,ehsan
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -119,16 +119,19 @@ PrivateBrowsingService.prototype = {
_alreadyChangingMode: false,
// Whether the private browsing mode has been started automatically (ie. always-on)
_autoStarted: false,
// 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",
classID: Components.ID("{c31f4883-839b-45f6-82ad-a6a9bc5ad599}"),
_xpcom_categories: [
{ category: "command-line-handler", entry: "m-privatebrowsing" },
{ category: "app-startup", service: true }
],
@@ -203,22 +206,30 @@ PrivateBrowsingService.prototype = {
// separation between private and non-private sessions
if (browserWindow) {
// set an empty session to transition from/to pb mode, see bug 476463
ss.setBrowserState(blankState);
// 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();
}
}
}
else
this._saveSession = false;
},
_onAfterPrivateBrowsingModeChange: function PBS__onAfterPrivateBrowsingModeChange() {
@@ -293,16 +304,30 @@ PrivateBrowsingService.prototype = {
},
_getBrowserWindow: function PBS__getBrowserWindow() {
return Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
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).
getEnumerator("Browser:page-info");
while (pageInfoEnum.hasMoreElements()) {
let win = pageInfoEnum.getNext();
win.close();
}
@@ -396,16 +421,18 @@ PrivateBrowsingService.prototype = {
if (!this._canEnterPrivateBrowsingMode())
return;
}
else {
if (!this._canLeavePrivateBrowsingMode())
return;
}
+ this._ensureCanCloseWindows();
+
this._autoStarted = this._prefs.getBoolPref("browser.privatebrowsing.autostart");
this._inPrivateBrowsing = val != false;
let data = val ? "enter" : "exit";
let quitting = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
quitting.data = this._quitting;
@@ -417,19 +444,26 @@ PrivateBrowsingService.prototype = {
this._onBeforePrivateBrowsingModeChange();
this._obs.notifyObservers(quitting, "private-browsing", data);
// load the appropriate session
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;
}
},
/**
* Whether private browsing has been started automatically.
*/
get autoStarted PBS_get_autoStarted() {
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1865,17 +1865,17 @@ nsHTMLDocument::OpenCommon(const nsACStr
// Stop current loads targeted at the window this document is in.
if (mScriptGlobalObject) {
nsCOMPtr<nsIContentViewer> cv;
shell->GetContentViewer(getter_AddRefs(cv));
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
// exception.
return NS_OK;
}
}
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6144,17 +6144,17 @@ nsDocShell::CreateAboutBlankContentViewe
if (mContentViewer) {
// We've got a content viewer already. Make sure the user
// permits us to discard the current document and replace it
// with about:blank. And also ensure we fire the unload events
// 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.
return NS_ERROR_FAILURE;
}
mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
@@ -7934,17 +7934,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
// Hold onto |this| until we return, to prevent a crash from happening.
// (bug#331040)
nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
// Check if the page doesn't want to be unloaded. The javascript:
// 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
// load.
return NS_OK;
}
}
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -8,28 +8,48 @@ interface nsIPrintSettings;
%{ C++
class nsIWidget;
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
{
[noscript] void init(in nsIWidgetPtr aParentWidget,
[const] in nsIntRectRef aBounds);
attribute nsISupports container;
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);
/**
* All users of a content viewer are responsible for calling both
* close() and destroy(), in that order.
*
* close() should be called when the load of a new page for the next
* content viewer begins, and destroy() should be called when the next
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5535,17 +5535,17 @@ nsGlobalWindow::Close()
// Close() as soon as it is possible for the window to close.
// This allows us to not close the window while printing is happening.
nsCOMPtr<nsIContentViewer> cv;
mDocShell->GetContentViewer(getter_AddRefs(cv));
if (!mInClose && !mIsClosed && cv) {
PRBool canClose;
- rv = cv->PermitUnload(&canClose);
+ rv = cv->PermitUnload(PR_FALSE, &canClose);
if (NS_SUCCEEDED(rv) && !canClose)
return NS_OK;
rv = cv->RequestWindowClose(&canClose);
if (NS_SUCCEEDED(rv) && !canClose)
return NS_OK;
}
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -779,17 +779,17 @@ nsJSChannel::EvaluateScript()
NS_QueryNotificationCallbacks(mStreamChannel, docShell);
if (docShell) {
nsCOMPtr<nsIContentViewer> cv;
docShell->GetContentViewer(getter_AddRefs(cv));
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
// return from the javascript: URL...
mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED;
}
}
}
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -492,16 +492,17 @@ protected:
/* character set member data */
PRInt32 mHintCharsetSource;
nsCString mHintCharset;
nsCString mDefaultCharacterSet;
nsCString mForceCharacterSet;
nsCString mPrevDocCharacterSet;
PRPackedBool mIsPageMode;
+ PRPackedBool mCallerIsClosingWindow;
};
//------------------------------------------------------------------
// DocumentViewerImpl
//------------------------------------------------------------------
// Class IDs
static NS_DEFINE_CID(kViewManagerCID, NS_VIEW_MANAGER_CID);
@@ -523,16 +524,17 @@ NS_NewDocumentViewer(nsIDocumentViewer**
}
void DocumentViewerImpl::PrepareToStartLoad()
{
mEnableRendering = PR_TRUE;
mStopped = PR_FALSE;
mLoaded = PR_FALSE;
mDeferredWindowClose = PR_FALSE;
+ mCallerIsClosingWindow = PR_FALSE;
#ifdef NS_PRINTING
mPrintIsPending = PR_FALSE;
mPrintDocIsFullyLoaded = PR_FALSE;
mClosingWhilePrinting = PR_FALSE;
// Make sure we have destroyed it and cleared the data member
if (mPrintEngine) {
@@ -1078,21 +1080,21 @@ DocumentViewerImpl::LoadComplete(nsresul
mCachedPrintWebProgressListner = nsnull;
}
#endif
return rv;
}
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;
}
// First, get the script global object from the document...
nsPIDOMWindow *window = mDocument->GetWindow();
if (!window) {
// This is odd, but not fatal
@@ -1188,25 +1190,57 @@ DocumentViewerImpl::PermitUnload(PRBool
for (PRInt32 i = 0; i < childCount && *aPermitUnload; ++i) {
nsCOMPtr<nsIDocShellTreeItem> item;
docShellNode->GetChildAt(i, getter_AddRefs(item));
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
if (docShell) {
nsCOMPtr<nsIContentViewer> cv;
- docShell->GetContentViewer(getter_AddRefs(cv));
-
- if (cv) {
- cv->PermitUnload(aPermitUnload);
+ docShell->GetContentViewer(getter_AddRefs(cv));
+
+ if (cv) {
+ cv->PermitUnload(aCallerClosesWindow, aPermitUnload);
}
}
}
}
+ if (aCallerClosesWindow && *aPermitUnload)
+ mCallerIsClosingWindow = PR_TRUE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DocumentViewerImpl::ResetCloseWindow()
+{
+ mCallerIsClosingWindow = PR_FALSE;
+
+ nsCOMPtr<nsIDocShellTreeNode> docShellNode(do_QueryReferent(mContainer));
+ if (docShellNode) {
+ PRInt32 childCount;
+ docShellNode->GetChildCount(&childCount);
+
+ for (PRInt32 i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> item;
+ docShellNode->GetChildAt(i, getter_AddRefs(item));
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
+
+ if (docShell) {
+ nsCOMPtr<nsIContentViewer> cv;
+ docShell->GetContentViewer(getter_AddRefs(cv));
+
+ if (cv) {
+ cv->ResetCloseWindow();
+ }
+ }
+ }
+ }
return NS_OK;
}
NS_IMETHODIMP
DocumentViewerImpl::PageHide(PRBool aIsUnload)
{
mEnableRendering = PR_FALSE;