diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 94f66ba93ed..f69a01bb79d 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1176,7 +1176,7 @@ pref("devtools.profiler.ui.show-platform-data", false); pref("devtools.netmonitor.enabled", true); // The default Network Monitor UI settings -pref("devtools.netmonitor.panes-network-details-width", 450); +pref("devtools.netmonitor.panes-network-details-width", 550); pref("devtools.netmonitor.panes-network-details-height", 450); pref("devtools.netmonitor.statistics", true); pref("devtools.netmonitor.filters", "[\"all\"]"); diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 3850d1ac7d7..c251625b955 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -14,6 +14,10 @@ %endif } +#main-window[customize-entered] { + min-width: -moz-fit-content; +} + searchbar { -moz-binding: url("chrome://browser/content/search/search.xml#searchbar"); } diff --git a/browser/base/content/newtab/newTab.js b/browser/base/content/newtab/newTab.js index af2f6a52ffb..a56a391bb01 100644 --- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -13,7 +13,7 @@ Cu.import("resource://gre/modules/PageThumbs.jsm"); Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm"); Cu.import("resource://gre/modules/NewTabUtils.jsm"); -Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js"); +Cu.import("resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm"); diff --git a/browser/base/content/newtab/transformations.js b/browser/base/content/newtab/transformations.js index 7976b12f962..6ff123bd401 100644 --- a/browser/base/content/newtab/transformations.js +++ b/browser/base/content/newtab/transformations.js @@ -181,7 +181,7 @@ let gTransformation = { let deferred = Promise.defer(); batch.push(deferred.promise); - let cb = function () deferred.resolve(); + let cb = deferred.resolve; if (!cells[aIndex]) // The site disappeared from the grid, hide it. @@ -194,8 +194,9 @@ let gTransformation = { this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb}); }, this); - let wait = Promise.promised(function () callback && callback()); - wait.apply(null, batch); + if (callback) { + Promise.all(batch).then(callback); + } }, /** diff --git a/browser/base/content/newtab/updater.js b/browser/base/content/newtab/updater.js index 7b483e037f2..66924323f36 100644 --- a/browser/base/content/newtab/updater.js +++ b/browser/base/content/newtab/updater.js @@ -147,8 +147,7 @@ let gUpdater = { }); }); - let wait = Promise.promised(aCallback); - wait.apply(null, batch); + Promise.all(batch).then(aCallback); }, /** @@ -180,7 +179,6 @@ let gUpdater = { gTransformation.showSite(site, function () deferred.resolve()); }); - let wait = Promise.promised(aCallback); - wait.apply(null, batch); + Promise.all(batch).then(aCallback); } }; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 53913233764..7e8d24241b5 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1101,6 +1101,22 @@ this._tabAttrModified(oldTab); this._tabAttrModified(this.mCurrentTab); + 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(); + // NB: This code assumes that the beforeunload prompt + // is the top-most prompt on the tab. + promptBox.removePrompt(prompts[prompts.length - 1]); + } + // Adjust focus oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused); do { @@ -1879,7 +1895,6 @@
test for bug 626455, tab1"); - gBrowser.addTab(TEST_URL); - afterAllTabsLoaded(testStayOnPage); + let tab = gBrowser.addTab(TEST_URL); + afterAllTabsLoaded(() => testStayOnPage(tab)); }); } -function testStayOnPage() { - whenDialogOpened(function (dialog) { +function testStayOnPage(blockingTab) { + let browser = blockingTab.linkedBrowser; + waitForOnBeforeUnloadDialog(browser, function (btnLeave, btnStay) { // stay on page - dialog.cancelDialog(); + btnStay.click(); executeSoon(function () { showTabView(function () { is(gBrowser.tabs.length, 1, "The total number of tab is 1 when staying on the page"); - let location = gBrowser.browsers[0].currentURI.spec; - isnot(location.indexOf("onbeforeunload"), -1, - "The open tab is the expected one"); + // The other initial tab has been closed when trying to close the tab + // group. The only tab left is the one with the onbeforeunload dialog. + let url = gBrowser.browsers[0].currentURI.spec; + ok(url.contains("onbeforeunload"), "The open tab is the expected one"); is(contentWindow.GroupItems.getActiveGroupItem(), activeGroup, "Active group is still the same"); @@ -50,7 +52,7 @@ function testStayOnPage() { "Only one group is open"); // start the next test - testLeavePage(); + testLeavePage(gBrowser.tabs[0]); }); }); }); @@ -58,12 +60,11 @@ function testStayOnPage() { closeGroupItem(activeGroup); } -function testLeavePage() { - let dialogsAccepted = 0; - - whenDialogOpened(function onDialogOpened(dialog) { +function testLeavePage(blockingTab) { + let browser = blockingTab.linkedBrowser; + waitForOnBeforeUnloadDialog(browser, function (btnLeave, btnStay) { // Leave page - dialog.acceptDialog(); + btnLeave.click(); }); whenGroupClosed(activeGroup, finishTest); @@ -85,6 +86,8 @@ function finishTest() { is(contentWindow.GroupItems.groupItems.length, 1, "Only one group is open"); + contentWindow = null; + activeGroup = null; finish(); } @@ -95,29 +98,3 @@ function whenGroupClosed(group, callback) { callback(); }); } - -// ---------- -function whenDialogOpened(callback) { - let wm = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Ci.nsIWindowMediator); - - let listener = { - onCloseWindow: function () {}, - onWindowTitleChange: function () {}, - - onOpenWindow: function (xulWin) { - let domWin = xulWin.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - - whenWindowLoaded(domWin, function () { - let dialog = domWin.document.querySelector("dialog"); - if (dialog) { - wm.removeListener(listener); - callback(dialog); - } - }); - } - }; - - wm.addListener(listener); -} diff --git a/browser/components/tabview/test/browser_tabview_bug649006.js b/browser/components/tabview/test/browser_tabview_bug649006.js index 6ec546d5382..2b8daaa97a8 100644 --- a/browser/components/tabview/test/browser_tabview_bug649006.js +++ b/browser/components/tabview/test/browser_tabview_bug649006.js @@ -81,6 +81,11 @@ function test4() { hideTabView(function() { is(gBrowser.tabs.length, 1, "Total number of tabs is 1 after all tests"); + + contentWindow = null; + contentElement = null; + groupItem = null; + finish(); }); } diff --git a/browser/components/tabview/test/head.js b/browser/components/tabview/test/head.js index 727d41318d3..d9ded0b960c 100644 --- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -1,6 +1,8 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + // Some tests here assume that all restored tabs are loaded without waiting for // the user to bring them to the foreground. We ensure this by resetting the // related preference (see the "firefox.js" defaults file for details). @@ -418,3 +420,17 @@ function promiseWindowClosed(win) { win.close(); return deferred.promise; } + +// ---------- +function waitForOnBeforeUnloadDialog(browser, callback) { + browser.addEventListener("DOMWillOpenModalDialog", function onModalDialog() { + browser.removeEventListener("DOMWillOpenModalDialog", onModalDialog, true); + + executeSoon(() => { + let stack = browser.parentNode; + let dialogs = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"); + let {button0, button1} = dialogs[0].ui; + callback(button0, button1); + }); + }, true); +} diff --git a/browser/components/tabview/ui.js b/browser/components/tabview/ui.js index c86649dc401..2e653d3f608 100644 --- a/browser/components/tabview/ui.js +++ b/browser/components/tabview/ui.js @@ -527,11 +527,15 @@ let UI = { if (!this.isTabViewVisible() || this._isChangingVisibility) return; - this._isChangingVisibility = true; - // another tab might be select if user decides to stay on a page when // a onclose confirmation prompts. GroupItems.removeHiddenGroups(); + + // We need to set this after removing the hidden groups because doing so + // might show prompts which will cause us to be called again, and we'd get + // stuck if we prevent re-entrancy before doing that. + this._isChangingVisibility = true; + TabItems.pausePainting(); this._reorderTabsOnHide.forEach(function(groupItem) { diff --git a/browser/devtools/framework/gDevTools.jsm b/browser/devtools/framework/gDevTools.jsm index befdb4aefc5..0515d4da636 100644 --- a/browser/devtools/framework/gDevTools.jsm +++ b/browser/devtools/framework/gDevTools.jsm @@ -317,7 +317,7 @@ DevTools.prototype = { closeToolbox: function DT_closeToolbox(target) { let toolbox = this._toolboxes.get(target); if (toolbox == null) { - return; + return promise.reject(null); } return toolbox.destroy(); }, diff --git a/browser/devtools/layoutview/test/browser_editablemodel.js b/browser/devtools/layoutview/test/browser_editablemodel.js index 472c807ba6e..175148df208 100644 --- a/browser/devtools/layoutview/test/browser_editablemodel.js +++ b/browser/devtools/layoutview/test/browser_editablemodel.js @@ -22,7 +22,8 @@ let test = asyncTest(function*() { inspector.sidebar.select("layoutview"); yield inspector.sidebar.once("layoutview-ready"); yield runTests(); - yield gDevTools.closeToolbox(toolbox); + // TODO: Closing the toolbox in this test leaks - bug 994314 + // yield gDevTools.closeToolbox(target); }); addTest("Test that editing margin dynamically updates the document, pressing escape cancels the changes", diff --git a/browser/devtools/layoutview/test/browser_editablemodel_allproperties.js b/browser/devtools/layoutview/test/browser_editablemodel_allproperties.js index 82ea704cdcd..136a2fb4ef4 100644 --- a/browser/devtools/layoutview/test/browser_editablemodel_allproperties.js +++ b/browser/devtools/layoutview/test/browser_editablemodel_allproperties.js @@ -1,3 +1,4 @@ + /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ @@ -22,7 +23,8 @@ let test = asyncTest(function*() { inspector.sidebar.select("layoutview"); yield inspector.sidebar.once("layoutview-ready"); yield runTests(); - yield gDevTools.closeToolbox(toolbox); + // TODO: Closing the toolbox in this test leaks - bug 994314 + // yield gDevTools.closeToolbox(target); }); addTest("When all properties are set on the node editing one should work", diff --git a/browser/devtools/layoutview/test/browser_editablemodel_border.js b/browser/devtools/layoutview/test/browser_editablemodel_border.js index 9f08fce2895..e98ba97026d 100644 --- a/browser/devtools/layoutview/test/browser_editablemodel_border.js +++ b/browser/devtools/layoutview/test/browser_editablemodel_border.js @@ -22,7 +22,8 @@ let test = asyncTest(function*() { inspector.sidebar.select("layoutview"); yield inspector.sidebar.once("layoutview-ready"); yield runTests(); - yield gDevTools.closeToolbox(toolbox); + // TODO: Closing the toolbox in this test leaks - bug 994314 + // yield gDevTools.closeToolbox(target); }); addTest("Test that adding a border applies a border style when necessary", diff --git a/browser/devtools/layoutview/test/browser_editablemodel_stylerules.js b/browser/devtools/layoutview/test/browser_editablemodel_stylerules.js index dad50221336..dece035b46d 100644 --- a/browser/devtools/layoutview/test/browser_editablemodel_stylerules.js +++ b/browser/devtools/layoutview/test/browser_editablemodel_stylerules.js @@ -22,7 +22,8 @@ let test = asyncTest(function*() { inspector.sidebar.select("layoutview"); yield inspector.sidebar.once("layoutview-ready"); yield runTests(); - yield gDevTools.closeToolbox(toolbox); + // TODO: Closing the toolbox in this test leaks - bug 994314 + // yield gDevTools.closeToolbox(target); }); addTest("Test that entering units works", diff --git a/browser/devtools/scratchpad/scratchpad.xul b/browser/devtools/scratchpad/scratchpad.xul index f3bd4d0f9f9..da8d863605b 100644 --- a/browser/devtools/scratchpad/scratchpad.xul +++ b/browser/devtools/scratchpad/scratchpad.xul @@ -184,6 +184,34 @@ +
+ - -