diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js
index b6ffeb375af7..f919129fbdbe 100644
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1015,7 +1015,6 @@ pref("apz.allow_zooming", true);
// Gaia relies heavily on scroll events for now, so lets fire them
// more often than the default value (100).
-pref("apz.asyncscroll.throttle", 40);
pref("apz.pan_repaint_interval", 16);
// APZ physics settings, tuned by UX designers
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 3b4da6bc52a7..2a4eda272c8c 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 aab0b7005ef3..61b87e2c9109 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 6110af47e922..f97ab580f73b 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 317efad7bd1e..ad93bcb4e882 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 @@