diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 3b4da6bc52a..2a4eda272c8 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5009,6 +5009,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function (aWindow) {
return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
},
+
+ canClose() {
+ return CanCloseWindow();
+ },
}
function getTogglableToolbars() {
@@ -6565,6 +6569,26 @@ var IndexedDBPromptHelper = {
}
};
+function CanCloseWindow()
+{
+ // Avoid redundant calls to canClose from showing multiple
+ // PermitUnload dialogs.
+ if (window.skipNextCanClose) {
+ return true;
+ }
+
+ for (let browser of gBrowser.browsers) {
+ let {permitUnload, timedOut} = browser.permitUnload();
+ if (timedOut) {
+ return true;
+ }
+ if (!permitUnload) {
+ return false;
+ }
+ }
+ return true;
+}
+
function WindowIsClosing()
{
if (TabView.isVisible()) {
@@ -6575,27 +6599,19 @@ function WindowIsClosing()
if (!closeWindow(false, warnAboutClosingWindow))
return false;
- // Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
- if (gMultiProcessBrowser)
+ // In theory we should exit here and the Window's internal Close
+ // method should trigger canClose on nsBrowserAccess. However, by
+ // that point it's too late to be able to show a prompt for
+ // PermitUnload. So we do it here, when we still can.
+ if (CanCloseWindow()) {
+ // This flag ensures that the later canClose call does nothing.
+ // It's only needed to make tests pass, since they detect the
+ // prompt even when it's not actually shown.
+ window.skipNextCanClose = true;
return true;
-
- for (let browser of gBrowser.browsers) {
- let ds = browser.docShell;
- // Passing true to permitUnload indicates we plan on closing the window.
- // This means that once unload is permitted, all further calls to
- // permitUnload will be ignored. This avoids getting multiple prompts
- // to unload the page.
- if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
- // ... however, if the user aborts closing, we need to undo that,
- // to ensure they get prompted again when we next try to close the window.
- // We do this on the window's toplevel docshell instead of on the tab, so
- // that all tabs we iterated before will get this reset.
- window.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
- return false;
- }
}
- return true;
+ return false;
}
/**
diff --git a/browser/base/content/chatWindow.xul b/browser/base/content/chatWindow.xul
index aab0b7005ef..61b87e2c910 100644
--- a/browser/base/content/chatWindow.xul
+++ b/browser/base/content/chatWindow.xul
@@ -131,6 +131,11 @@ chatBrowserAccess.prototype = {
isTabContentWindow: function (aWindow) {
return this.contentWindow == aWindow;
},
+
+ canClose() {
+ let {BrowserUtils} = Cu.import("resource://gre/modules/BrowserUtils.jsm", {});
+ return BrowserUtils.canCloseWindow(window);
+ },
};
diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js
index 6110af47e92..f97ab580f73 100644
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -1,4 +1,4 @@
-// -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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/. */
@@ -567,28 +567,17 @@ Sanitizer.prototype = {
openWindows: {
privateStateForNewWindow: "non-private",
_canCloseWindow: function(aWindow) {
- // Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
- if (!aWindow.gMultiProcessBrowser) {
- // Cargo-culted out of browser.js' WindowIsClosing because we don't care
- // about TabView or the regular 'warn me before closing windows with N tabs'
- // stuff here, and more importantly, we want to set aCallerClosesWindow to true
- // when calling into permitUnload:
- for (let browser of aWindow.gBrowser.browsers) {
- let ds = browser.docShell;
- // 'true' here means we will be closing the window soon, so please don't dispatch
- // another onbeforeunload event when we do so. If unload is *not* permitted somewhere,
- // we will reset the flag that this triggers everywhere so that we don't interfere
- // with the browser after all:
- if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
- return false;
- }
- }
+ if (aWindow.CanCloseWindow()) {
+ // We already showed PermitUnload for the window, so let's
+ // make sure we don't do it again when we actually close the
+ // window.
+ aWindow.skipNextCanClose = true;
+ return true;
}
- return true;
},
_resetAllWindowClosures: function(aWindowList) {
for (let win of aWindowList) {
- win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
+ win.skipNextCanClose = false;
}
},
clear: Task.async(function*() {
diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml
index bca8db37c9c..ad93bcb4e88 100644
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1156,25 +1156,29 @@
this._tabAttrModified(this.mCurrentTab, ["selected"]);
if (oldBrowser != newBrowser &&
- oldBrowser.docShell &&
- oldBrowser.docShell.contentViewer.inPermitUnload) {
- // Since the user is switching away from a tab that has
- // a beforeunload prompt active, we remove the prompt.
- // This prevents confusing user flows like the following:
- // 1. User attempts to close Firefox
- // 2. User switches tabs (ingoring a beforeunload prompt)
- // 3. User returns to tab, presses "Leave page"
- let promptBox = this.getTabModalPromptBox(oldBrowser);
- let prompts = promptBox.listPrompts();
- // There might not be any prompts here if the tab was closed
- // while in an onbeforeunload prompt, which will have
- // destroyed aforementioned prompt already, so check there's
- // something to remove, first:
- if (prompts.length) {
- // NB: This code assumes that the beforeunload prompt
- // is the top-most prompt on the tab.
- prompts[prompts.length - 1].abortPrompt();
- }
+ oldBrowser.getInPermitUnload) {
+ oldBrowser.getInPermitUnload(inPermitUnload => {
+ if (!inPermitUnload) {
+ return;
+ }
+ // Since the user is switching away from a tab that has
+ // a beforeunload prompt active, we remove the prompt.
+ // This prevents confusing user flows like the following:
+ // 1. User attempts to close Firefox
+ // 2. User switches tabs (ingoring a beforeunload prompt)
+ // 3. User returns to tab, presses "Leave page"
+ let promptBox = this.getTabModalPromptBox(oldBrowser);
+ let prompts = promptBox.listPrompts();
+ // There might not be any prompts here if the tab was closed
+ // while in an onbeforeunload prompt, which will have
+ // destroyed aforementioned prompt already, so check there's
+ // something to remove, first:
+ if (prompts.length) {
+ // NB: This code assumes that the beforeunload prompt
+ // is the top-most prompt on the tab.
+ prompts[prompts.length - 1].abortPrompt();
+ }
+ });
}
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
@@ -2103,6 +2107,7 @@
if (aParams) {
var animate = aParams.animate;
var byMouse = aParams.byMouse;
+ var skipPermitUnload = aParams.skipPermitUnload;
}
// Handle requests for synchronously removing an already
@@ -2115,7 +2120,7 @@
var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
- if (!this._beginRemoveTab(aTab, false, null, true))
+ if (!this._beginRemoveTab(aTab, false, null, true, skipPermitUnload))
return;
if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
@@ -2163,6 +2168,7 @@
+
diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini
index b85f09e86d7..1e75dfe31c6 100644
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -149,7 +149,6 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
[browser_backButtonFitts.js]
skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
[browser_beforeunload_duplicate_dialogs.js]
-skip-if = e10s # bug 967873 means permitUnload doesn't work in e10s mode
[browser_blob-channelname.js]
[browser_bookmark_titles.js]
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index aec35dd88e9..66688848f6e 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7805,7 +7805,7 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
mTiming->NotifyBeforeUnload();
bool okToUnload;
- rv = mContentViewer->PermitUnload(false, &okToUnload);
+ rv = mContentViewer->PermitUnload(&okToUnload);
if (NS_SUCCEEDED(rv) && !okToUnload) {
// The user chose not to unload the page, interrupt the load.
@@ -10143,7 +10143,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
// protocol handler deals with this for javascript: URLs.
if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
bool okToUnload;
- rv = mContentViewer->PermitUnload(false, &okToUnload);
+ rv = mContentViewer->PermitUnload(&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 6d53ecd59db..6484b0a60d6 100644
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -31,7 +31,7 @@ class nsDOMNavigationTiming;
[ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
[ref] native nsIContentViewerTArray(nsTArray >);
-[scriptable, builtinclass, uuid(fbd04c99-e149-473f-8a68-44f53d82f98b)]
+[scriptable, builtinclass, uuid(91b6c1f3-fc5f-43a9-88f4-9286bd19387f)]
interface nsIContentViewer : nsISupports
{
[noscript] void init(in nsIWidgetPtr aParentWidget,
@@ -45,12 +45,8 @@ interface nsIContentViewer : nsISupports
/**
* 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);
+ boolean permitUnload();
/**
* Exposes whether we're blocked in a call to permitUnload.
@@ -62,8 +58,7 @@ interface nsIContentViewer : nsISupports
* track of whether the user has responded to a prompt.
* Used internally by the scriptable version to ensure we only prompt once.
*/
- [noscript,nostdcall] boolean permitUnloadInternal(in boolean aCallerClosesWindow,
- inout boolean aShouldPrompt);
+ [noscript,nostdcall] boolean permitUnloadInternal(inout boolean aShouldPrompt);
/**
* Exposes whether we're in the process of firing the beforeunload event.
@@ -71,16 +66,6 @@ interface nsIContentViewer : nsISupports
*/
readonly attribute boolean beforeUnloadFiring;
- /**
- * 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/docshell/test/browser/browser.ini b/docshell/test/browser/browser.ini
index b6215120f4b..5d94575cd8c 100644
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -83,7 +83,6 @@ skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
[browser_loadURI.js]
[browser_multiple_pushState.js]
[browser_onbeforeunload_navigation.js]
-skip-if = e10s
[browser_search_notification.js]
[browser_timelineMarkers-01.js]
[browser_timelineMarkers-02.js]
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 66005461bcf..23b89048cd6 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7998,6 +7998,17 @@ nsGlobalWindow::CanClose()
{
MOZ_ASSERT(IsOuterWindow());
+ if (mIsChrome) {
+ nsCOMPtr bwin;
+ nsIDOMChromeWindow* chromeWin = static_cast(this);
+ chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
+
+ bool canClose = true;
+ if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
+ return canClose;
+ }
+ }
+
if (!mDocShell) {
return true;
}
@@ -8011,7 +8022,7 @@ nsGlobalWindow::CanClose()
mDocShell->GetContentViewer(getter_AddRefs(cv));
if (cv) {
bool canClose;
- nsresult rv = cv->PermitUnload(false, &canClose);
+ nsresult rv = cv->PermitUnload(&canClose);
if (NS_SUCCEEDED(rv) && !canClose)
return false;
diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp
index 6b936460170..f8c82189e8c 100644
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1522,7 +1522,7 @@ nsHTMLDocument::Open(JSContext* cx,
if (cv) {
bool okToUnload;
- if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && !okToUnload) {
+ if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
// We don't want to unload, so stop here, but don't throw an
// exception.
nsCOMPtr ret = this;
diff --git a/dom/interfaces/base/nsIBrowserDOMWindow.idl b/dom/interfaces/base/nsIBrowserDOMWindow.idl
index d0203f3ae9c..d94ff6e0ae0 100644
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -17,7 +17,7 @@ interface nsIOpenURIInFrameParams : nsISupports
attribute boolean isPrivate;
};
-[scriptable, uuid(99f5a347-722c-4337-bd38-f14ec94801b3)]
+[scriptable, uuid(31da1ce2-aec4-4c26-ac66-d622935c3bf4)]
/**
* The C++ source has access to the browser script source through
@@ -99,6 +99,14 @@ interface nsIBrowserDOMWindow : nsISupports
* @return whether the window is the main content window for any
* currently open tab in this toplevel browser window.
*/
- boolean isTabContentWindow(in nsIDOMWindow aWindow);
+ boolean isTabContentWindow(in nsIDOMWindow aWindow);
+
+ /**
+ * This function is responsible for calling
+ * nsIContentViewer::PermitUnload on each frame in the window. It
+ * returns true if closing the window is allowed. See canClose() in
+ * BrowserUtils.jsm for a simple implementation of this method.
+ */
+ boolean canClose();
};
diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp
index 02eb109c60e..991fbe214ba 100644
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -749,7 +749,7 @@ nsJSChannel::EvaluateScript()
if (cv) {
bool okToUnload;
- if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) &&
+ if (NS_SUCCEEDED(cv->PermitUnload(&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 30d4d179bda..a2c0e6f22d3 100644
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -404,7 +404,6 @@ protected:
nsCString mForceCharacterSet;
bool mIsPageMode;
- bool mCallerIsClosingWindow;
bool mInitializedForPrintPreview;
bool mHidden;
};
@@ -455,7 +454,6 @@ void nsDocumentViewer::PrepareToStartLoad()
mLoaded = false;
mAttachedToParent = false;
mDeferredWindowClose = false;
- mCallerIsClosingWindow = false;
#ifdef NS_PRINTING
mPrintIsPending = false;
@@ -1051,18 +1049,15 @@ nsDocumentViewer::LoadComplete(nsresult aStatus)
}
NS_IMETHODIMP
-nsDocumentViewer::PermitUnload(bool aCallerClosesWindow,
- bool *aPermitUnload)
+nsDocumentViewer::PermitUnload(bool *aPermitUnload)
{
bool shouldPrompt = true;
- return PermitUnloadInternal(aCallerClosesWindow, &shouldPrompt,
- aPermitUnload);
+ return PermitUnloadInternal(&shouldPrompt, aPermitUnload);
}
nsresult
-nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
- bool *aShouldPrompt,
+nsDocumentViewer::PermitUnloadInternal(bool *aShouldPrompt,
bool *aPermitUnload)
{
AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
@@ -1071,7 +1066,6 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
if (!mDocument
|| mInPermitUnload
- || mCallerIsClosingWindow
|| mInPermitUnloadPrompt) {
return NS_OK;
}
@@ -1247,16 +1241,12 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
docShell->GetContentViewer(getter_AddRefs(cv));
if (cv) {
- cv->PermitUnloadInternal(aCallerClosesWindow, aShouldPrompt,
- aPermitUnload);
+ cv->PermitUnloadInternal(aShouldPrompt, aPermitUnload);
}
}
}
}
- if (aCallerClosesWindow && *aPermitUnload)
- mCallerIsClosingWindow = true;
-
return NS_OK;
}
@@ -1274,35 +1264,6 @@ nsDocumentViewer::GetInPermitUnload(bool* aInEvent)
return NS_OK;
}
-NS_IMETHODIMP
-nsDocumentViewer::ResetCloseWindow()
-{
- mCallerIsClosingWindow = false;
-
- nsCOMPtr docShell(mContainer);
- if (docShell) {
- int32_t childCount;
- docShell->GetChildCount(&childCount);
-
- for (int32_t i = 0; i < childCount; ++i) {
- nsCOMPtr item;
- docShell->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;
-}
-
NS_IMETHODIMP
nsDocumentViewer::PageHide(bool aIsUnload)
{
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js
index 2c67be1e994..50cca76f2b7 100644
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -61,6 +61,9 @@ if (AppConstants.MOZ_SAFE_BROWSING) {
"resource://gre/modules/SafeBrowsing.jsm");
}
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+ "resource://gre/modules/BrowserUtils.jsm");
+
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
@@ -3491,6 +3494,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function(aWindow) {
return BrowserApp.getBrowserForWindow(aWindow) != null;
},
+
+ canClose() {
+ return BrowserUtils.canCloseWindow(window);
+ },
};
diff --git a/services/fxaccounts/FxAccountsOAuthClient.jsm b/services/fxaccounts/FxAccountsOAuthClient.jsm
index 097d6eddd0a..c59f1a86913 100644
--- a/services/fxaccounts/FxAccountsOAuthClient.jsm
+++ b/services/fxaccounts/FxAccountsOAuthClient.jsm
@@ -195,6 +195,25 @@ this.FxAccountsOAuthClient.prototype = {
};
}
+ // if the message asked to close the tab
+ if (data.closeWindow && target) {
+ // for e10s reasons the best way is to use the TabBrowser to close the tab.
+ let tabbrowser = target.getTabBrowser();
+
+ if (tabbrowser) {
+ let tab = tabbrowser.getTabForBrowser(target);
+
+ if (tab) {
+ tabbrowser.removeTab(tab);
+ log.debug("OAuth flow closed the tab.");
+ } else {
+ log.debug("OAuth flow failed to close the tab. Tab not found in TabBrowser.");
+ }
+ } else {
+ log.debug("OAuth flow failed to close the tab. TabBrowser not found.");
+ }
+ }
+
if (err) {
log.debug(err.message);
if (this.onError) {
@@ -214,25 +233,6 @@ this.FxAccountsOAuthClient.prototype = {
// onComplete will be called for this client only once
// calling onComplete again will result in a failure of the OAuth flow
this.tearDown();
-
- // if the message asked to close the tab
- if (data.closeWindow && target) {
- // for e10s reasons the best way is to use the TabBrowser to close the tab.
- let tabbrowser = target.getTabBrowser();
-
- if (tabbrowser) {
- let tab = tabbrowser.getTabForBrowser(target);
-
- if (tab) {
- tabbrowser.removeTab(tab);
- log.debug("OAuth flow closed the tab.");
- } else {
- log.debug("OAuth flow failed to close the tab. Tab not found in TabBrowser.");
- }
- } else {
- log.debug("OAuth flow failed to close the tab. TabBrowser not found.");
- }
- }
break;
}
}
diff --git a/toolkit/components/startup/tests/browser/browser.ini b/toolkit/components/startup/tests/browser/browser.ini
index 2204935bc90..0c02f73b688 100644
--- a/toolkit/components/startup/tests/browser/browser.ini
+++ b/toolkit/components/startup/tests/browser/browser.ini
@@ -4,7 +4,5 @@ support-files =
beforeunload.html
[browser_bug511456.js]
-skip-if = e10s # Bug ?????? - test touches content (uses a WindowWatcher in the parent process to try and observe content created alerts etc)
[browser_bug537449.js]
-skip-if = e10s # Bug ?????? - test touches content (uses a WindowWatcher in the parent process to try and observe content created alerts etc)
[browser_crash_detection.js]
diff --git a/toolkit/content/browser-child.js b/toolkit/content/browser-child.js
index 7d9c191056b..18c3930370e 100644
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -577,6 +577,22 @@ var AutoCompletePopup = {
}
}
+addMessageListener("InPermitUnload", msg => {
+ let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
+ sendAsyncMessage("InPermitUnload", {id: msg.data.id, inPermitUnload});
+});
+
+addMessageListener("PermitUnload", msg => {
+ sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "start"});
+
+ let permitUnload = true;
+ if (docShell && docShell.contentViewer) {
+ permitUnload = docShell.contentViewer.permitUnload();
+ }
+
+ sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "end", permitUnload});
+});
+
// We may not get any responses to Browser:Init if the browser element
// is torn down too quickly.
var outerWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml
index 002876a1360..6d64b39acf1 100644
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1082,8 +1082,8 @@
window.addEventListener("keypress", this, true);
window.addEventListener("keyup", this, true);
]]>
-
-
+