diff --git a/browser/base/content/browser-loop.js b/browser/base/content/browser-loop.js index ebc964fc95e5..9b57337134dd 100644 --- a/browser/base/content/browser-loop.js +++ b/browser/base/content/browser-loop.js @@ -24,48 +24,53 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel * the panel to the button which triggers it. * @param {String} [tabId] Identifier of the tab to select when the panel is * opened. Example: 'rooms', 'contacts', etc. + * @return {Promise} */ openCallPanel: function(event, tabId = null) { - let callback = iframe => { - // Helper function to show a specific tab view in the panel. - function showTab() { - if (!tabId) { + return new Promise((resolve) => { + let callback = iframe => { + // Helper function to show a specific tab view in the panel. + function showTab() { + if (!tabId) { + resolve(); + return; + } + + let win = iframe.contentWindow; + let ev = new win.CustomEvent("UIAction", Cu.cloneInto({ + detail: { + action: "selectTab", + tab: tabId + } + }, win)); + win.dispatchEvent(ev); + resolve(); + } + + // If the panel has been opened and initialized before, we can skip waiting + // for the content to load - because it's already there. + if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") { + showTab(); return; } - let win = iframe.contentWindow; - let ev = new win.CustomEvent("UIAction", Cu.cloneInto({ - detail: { - action: "selectTab", - tab: tabId - } - }, win)); - win.dispatchEvent(ev); - } + iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() { + iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true); + injectLoopAPI(iframe.contentWindow); + iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() { + iframe.contentWindow.removeEventListener("loopPanelInitialized", + loopPanelInitialized); + showTab(); + }); + }, true); + }; - // If the panel has been opened and initialized before, we can skip waiting - // for the content to load - because it's already there. - if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") { - showTab(); - return; - } + // Used to clear the temporary "login" state from the button. + Services.obs.notifyObservers(null, "loop-status-changed", null); - iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() { - iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true); - injectLoopAPI(iframe.contentWindow); - iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() { - iframe.contentWindow.removeEventListener("loopPanelInitialized", - loopPanelInitialized); - showTab(); - }); - }, true); - }; - - // Used to clear the temporary "login" state from the button. - Services.obs.notifyObservers(null, "loop-status-changed", null); - - PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node, - "loop", null, "about:looppanel", null, callback); + PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node, + "loop", null, "about:looppanel", null, callback); + }); }, /** diff --git a/browser/modules/UITour.jsm b/browser/modules/UITour.jsm index e321c6028ba5..203465088b8c 100644 --- a/browser/modules/UITour.jsm +++ b/browser/modules/UITour.jsm @@ -111,15 +111,15 @@ this.UITour = { }, widgetName: "PanelUI-customize", }], + ["devtools", {query: "#developer-button"}], ["help", {query: "#PanelUI-help"}], ["home", {query: "#home-button"}], - ["loop", {query: "#loop-button"}], - ["devtools", {query: "#developer-button"}], - ["webide", {query: "#webide-button"}], ["forget", { query: "#panic-button", widgetName: "panic-button", - allowAdd: true }], + allowAdd: true, + }], + ["loop", {query: "#loop-button"}], ["privateWindow", {query: "#privatebrowsing-button"}], ["quit", {query: "#PanelUI-quit"}], ["search", { @@ -188,6 +188,7 @@ this.UITour = { query: "#urlbar", widgetName: "urlbar-container", }], + ["webide", {query: "#webide-button"}], ]), init: function() { @@ -714,8 +715,6 @@ this.UITour = { teardownTour: function(aWindow, aWindowClosing = false) { log.debug("teardownTour: aWindowClosing = " + aWindowClosing); aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this); - aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations); - aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations); aWindow.removeEventListener("SSWindowClosing", this); let originTabs = this.originTabs.get(aWindow); @@ -732,8 +731,15 @@ this.UITour = { this.hideInfo(aWindow); // Ensure the menu panel is hidden before calling recreatePopup so popup events occur. this.hideMenu(aWindow, "appMenu"); + this.hideMenu(aWindow, "loop"); } + // Clean up panel listeners after we may have called hideMenu above. + aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations); + aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations); + let loopPanel = aWindow.document.getElementById("loop-notification-panel"); + loopPanel.removeEventListener("popuphidden", this.onLoopPanelHidden); + this.endUrlbarCapture(aWindow); this.removePinnedTab(aWindow); this.resetTheme(); @@ -867,7 +873,7 @@ this.UITour = { */ _setAppMenuStateForAnnotation: function(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) { log.debug("_setAppMenuStateForAnnotation:", aAnnotationType); - log.debug("_setAppMenuStateForAnnotation: Menu is exptected to be:", aShouldOpenForHighlight ? "open" : "closed"); + log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed"); // If the panel is in the desired state, we're done. let panelIsOpen = aWindow.PanelUI.panel.state != "closed"; @@ -1238,6 +1244,26 @@ this.UITour = { } else if (aMenuName == "bookmarks") { let menuBtn = aWindow.document.getElementById("bookmarks-menu-button"); openMenuButton(menuBtn); + } else if (aMenuName == "loop") { + let toolbarButton = aWindow.LoopUI.toolbarButton; + if (!toolbarButton || !toolbarButton.node) { + return; + } + + let panel = aWindow.document.getElementById("loop-notification-panel"); + panel.setAttribute("noautohide", true); + if (panel.state != "open") { + this.recreatePopup(panel); + } + + // An event object is expected but we don't want to toggle the panel with a click if the panel + // is already open. + aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }).then(() => { + if (aOpenCallback) { + aOpenCallback(); + } + }); + panel.addEventListener("popuphidden", this.onLoopPanelHidden); } else if (aMenuName == "searchEngines") { this.getTarget(aWindow, "searchProvider").then(target => { openMenuButton(target.node); @@ -1258,6 +1284,9 @@ this.UITour = { } else if (aMenuName == "bookmarks") { let menuBtn = aWindow.document.getElementById("bookmarks-menu-button"); closeMenuButton(menuBtn); + } else if (aMenuName == "loop") { + let panel = aWindow.document.getElementById("loop-notification-panel"); + panel.hidePopup(); } else if (aMenuName == "searchEngines") { let menuBtn = this.targets.get("searchProvider").query(aWindow.document); closeMenuButton(menuBtn); @@ -1289,6 +1318,11 @@ this.UITour = { UITour.appMenuOpenForAnnotation.clear(); }, + onLoopPanelHidden: function(aEvent) { + aEvent.target.removeAttribute("noautohide"); + UITour.recreatePopup(aEvent.target); + }, + recreatePopup: function(aPanel) { // After changing popup attributes that relate to how the native widget is created // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect. diff --git a/browser/modules/test/browser.ini b/browser/modules/test/browser.ini index 48233a782134..86d4768cfa7c 100644 --- a/browser/modules/test/browser.ini +++ b/browser/modules/test/browser.ini @@ -28,6 +28,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly [browser_UITour_annotation_size_attributes.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly. +[browser_UITour_loop.js] +skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly. [browser_UITour_modalDialog.js] run-if = os == "mac" # modal dialog disabling only working on OS X skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly diff --git a/browser/modules/test/browser_UITour2.js b/browser/modules/test/browser_UITour2.js index cba2d6aee257..064b69203359 100644 --- a/browser/modules/test/browser_UITour2.js +++ b/browser/modules/test/browser_UITour2.js @@ -89,7 +89,7 @@ let tests = [ yield addPinnedTabPromise(); is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab"); }), - taskify(function* test_menu() { + taskify(function* test_bookmarks_menu() { let bookmarksMenuButton = document.getElementById("bookmarks-menu-button"); ise(bookmarksMenuButton.open, false, "Menu should initially be closed"); diff --git a/browser/modules/test/browser_UITour_loop.js b/browser/modules/test/browser_UITour_loop.js new file mode 100644 index 000000000000..705fb2bcf4fd --- /dev/null +++ b/browser/modules/test/browser_UITour_loop.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gTestTab; +let gContentAPI; +let gContentWindow; +let loopButton; +let loopPanel = document.getElementById("loop-notification-panel"); + +Components.utils.import("resource:///modules/UITour.jsm"); + +function test() { + UITourTest(); +} + +let tests = [ + taskify(function* test_menu_show_hide() { + ise(loopButton.open, false, "Menu should initially be closed"); + gContentAPI.showMenu("loop"); + + yield waitForConditionPromise(() => { + return loopButton.open; + }, "Menu should be visible after showMenu()"); + + ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel"); + ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen"); + is(loopPanel.state, "open", "The panel should be open"); + ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open"); + + gContentAPI.hideMenu("loop"); + yield waitForConditionPromise(() => { + return !loopButton.open; + }, "Menu should be hidden after hideMenu()"); + + checkLoopPanelIsHidden(); + }), + // Test the menu was cleaned up in teardown. + taskify(function* setup_menu_cleanup() { + gContentAPI.showMenu("loop"); + + yield waitForConditionPromise(() => { + return loopButton.open; + }, "Menu should be visible after showMenu()"); + + // Leave it open so it gets torn down and we can test below that teardown was succesful. + }), + taskify(function* test_menu_cleanup() { + // Test that the open menu from above was torn down fully. + checkLoopPanelIsHidden(); + }), +]; + +function checkLoopPanelIsHidden() { + ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up"); + ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen"); + isnot(loopPanel.state, "open", "The panel shouldn't be open"); + is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed"); +} + +if (Services.prefs.getBoolPref("loop.enabled")) { + loopButton = window.LoopUI.toolbarButton.node; +} else { + ok(true, "Loop is disabled so skip the UITour Loop tests"); + tests = []; +}