From b55bb2b76ffc5c36c2f9f59c25a487072009d7c9 Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Thu, 14 Jul 2016 11:44:39 +0100 Subject: [PATCH] Bug 1272256 - Adding in longpress new tab button container menu r=Gijs MozReview-Commit-ID: 5KECDW34G8M --HG-- extra : rebase_source : 95cd57a27dc0ac76e1a0ac15afa59ba04a2e3c8a --- .../tests/mochitest/tree/test_tabbrowser.xul | 11 +- browser/base/content/browser.css | 11 ++ browser/base/content/browser.js | 134 ++++++++++-------- browser/base/content/tabbrowser.xml | 115 ++++++++++++--- .../test/browser/browser.ini | 1 + .../test/browser/browser_newtabButton.js | 34 +++++ 6 files changed, 220 insertions(+), 86 deletions(-) create mode 100644 browser/components/contextualidentity/test/browser/browser_newtabButton.js diff --git a/accessible/tests/mochitest/tree/test_tabbrowser.xul b/accessible/tests/mochitest/tree/test_tabbrowser.xul index e163a8e4fe2b..1e25fdb9c838 100644 --- a/accessible/tests/mochitest/tree/test_tabbrowser.xul +++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul @@ -90,6 +90,15 @@ ); } else { SimpleTest.ok(true, "Testing Firefox tabbrowser UI."); + let newTabChildren = []; + if (SpecialPowers.getBoolPref("privacy.userContext.enabled")) { + newTabChildren = [ + { + role: ROLE_MENUPOPUP, + children: [] + } + ]; + } // NB: The (3) buttons are not visible, unless manually hovered, // probably due to size reduction in this test. @@ -119,7 +128,7 @@ { // xul:toolbarbutton ("Open a new tab") role: ROLE_PUSHBUTTON, - children: [] + children: newTabChildren } // "List all tabs" dropdown // XXX: This child(?) is not present in this test. diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 045763d87124..e4bde6f8b07f 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -119,6 +119,16 @@ tabbrowser { visibility: hidden; /* temporary space to keep a tab's close button under the cursor */ } +.tabs-newtab-button > .toolbarbutton-menu-dropmarker, +#new-tab-button > .toolbarbutton-menu-dropmarker { + display: none; +} + +.tabs-newtab-button > .toolbarbutton-icon, +#new-tab-button > .toolbarbutton-icon { + margin-inline-end: 0; +} + .tabbrowser-tab { -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab"); } @@ -177,6 +187,7 @@ tabbrowser { transition: transform 200ms ease-out; } +.new-tab-popup, #alltabs-popup { -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup"); } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index af049cf82f39..933a5116f69a 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -263,66 +263,6 @@ function UpdateBackForwardCommands(aWebNavigation) { * XXXmano: should this live in toolbarbutton.xml? */ function SetClickAndHoldHandlers() { - var timer; - - function openMenu(aButton) { - cancelHold(aButton); - aButton.firstChild.hidden = false; - aButton.open = true; - } - - function mousedownHandler(aEvent) { - if (aEvent.button != 0 || - aEvent.currentTarget.open || - aEvent.currentTarget.disabled) - return; - - // Prevent the menupopup from opening immediately - aEvent.currentTarget.firstChild.hidden = true; - - aEvent.currentTarget.addEventListener("mouseout", mouseoutHandler, false); - aEvent.currentTarget.addEventListener("mouseup", mouseupHandler, false); - timer = setTimeout(openMenu, 500, aEvent.currentTarget); - } - - function mouseoutHandler(aEvent) { - let buttonRect = aEvent.currentTarget.getBoundingClientRect(); - if (aEvent.clientX >= buttonRect.left && - aEvent.clientX <= buttonRect.right && - aEvent.clientY >= buttonRect.bottom) - openMenu(aEvent.currentTarget); - else - cancelHold(aEvent.currentTarget); - } - - function mouseupHandler(aEvent) { - cancelHold(aEvent.currentTarget); - } - - function cancelHold(aButton) { - clearTimeout(timer); - aButton.removeEventListener("mouseout", mouseoutHandler, false); - aButton.removeEventListener("mouseup", mouseupHandler, false); - } - - function clickHandler(aEvent) { - if (aEvent.button == 0 && - aEvent.target == aEvent.currentTarget && - !aEvent.currentTarget.open && - !aEvent.currentTarget.disabled) { - let cmdEvent = document.createEvent("xulcommandevent"); - cmdEvent.initCommandEvent("command", true, true, window, 0, - aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, - aEvent.metaKey, null); - aEvent.currentTarget.dispatchEvent(cmdEvent); - } - } - - function _addClickAndHoldListenersOnElement(aElm) { - aElm.addEventListener("mousedown", mousedownHandler, true); - aElm.addEventListener("click", clickHandler, true); - } - // Bug 414797: Clone the back/forward buttons' context menu into both buttons. let popup = document.getElementById("backForwardMenu").cloneNode(true); popup.removeAttribute("id"); @@ -332,13 +272,83 @@ function SetClickAndHoldHandlers() { let backButton = document.getElementById("back-button"); backButton.setAttribute("type", "menu"); backButton.appendChild(popup); - _addClickAndHoldListenersOnElement(backButton); + addClickAndHoldListenersOnElement(backButton); let forwardButton = document.getElementById("forward-button"); popup = popup.cloneNode(true); forwardButton.setAttribute("type", "menu"); forwardButton.appendChild(popup); - _addClickAndHoldListenersOnElement(forwardButton); + addClickAndHoldListenersOnElement(forwardButton); +} + +let holdTimersMap = new Map(); +function holdMousedownHandler(aEvent) { + if (aEvent.button != 0 || + aEvent.currentTarget.open || + aEvent.currentTarget.disabled) + return; + + // Prevent the menupopup from opening immediately + aEvent.currentTarget.firstChild.hidden = true; + + aEvent.currentTarget.addEventListener("mouseout", holdMouseoutHandler, false); + aEvent.currentTarget.addEventListener("mouseup", holdMouseupHandler, false); + holdTimersMap.set(aEvent.currentTarget, setTimeout(holdOpenMenu, 500, aEvent.currentTarget)); +} + +function holdClickHandler(aEvent) { + if (aEvent.button == 0 && + aEvent.target == aEvent.currentTarget && + !aEvent.currentTarget.open && + !aEvent.currentTarget.disabled) { + let cmdEvent = document.createEvent("xulcommandevent"); + cmdEvent.initCommandEvent("command", true, true, window, 0, + aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, + aEvent.metaKey, null); + aEvent.currentTarget.dispatchEvent(cmdEvent); + } + // This is here to cancel the XUL default event + // dom.click() triggers a command even if there is a click handler + // however this can now be prevented with preventDefault(). + aEvent.preventDefault(); +} + +function holdOpenMenu(aButton) { + cancelHold(aButton); + aButton.firstChild.hidden = false; + aButton.open = true; +} + +function holdMouseoutHandler(aEvent) { + let buttonRect = aEvent.currentTarget.getBoundingClientRect(); + if (aEvent.clientX >= buttonRect.left && + aEvent.clientX <= buttonRect.right && + aEvent.clientY >= buttonRect.bottom) + holdOpenMenu(aEvent.currentTarget); + else + cancelHold(aEvent.currentTarget); +} + +function holdMouseupHandler(aEvent) { + cancelHold(aEvent.currentTarget); +} + +function cancelHold(aButton) { + clearTimeout(holdTimersMap.get(aButton)); + aButton.removeEventListener("mouseout", holdMouseoutHandler, false); + aButton.removeEventListener("mouseup", holdMouseupHandler, false); +} + +function removeClickAndHoldListenersOnElement(aElm) { + aElm.removeEventListener("mousedown", holdMousedownHandler, true); + aElm.removeEventListener("click", holdClickHandler, true); +} + +function addClickAndHoldListenersOnElement(aElm) { + holdTimersMap.delete(aElm); + + aElm.addEventListener("mousedown", holdMousedownHandler, true); + aElm.addEventListener("click", holdClickHandler, true); } const gSessionHistoryObserver = { diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index b1c01cc259cb..b09705411d8e 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -4939,7 +4939,7 @@ - + + + + + + + document.getAnonymousElementByAttribute(this, "anonid", "newtab_undoCloseTab"); + + document.getElementById(this.getAttribute("tabbrowser")); @@ -4986,6 +4998,54 @@ null null + + + + + + + let root = document.documentElement; @@ -6673,30 +6733,36 @@ } let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled"); - document.getElementById("alltabs-popup-separator-1").hidden = !containersEnabled; - let containersTab = document.getElementById("alltabs_containersTab"); - containersTab.hidden = !containersEnabled; - if (PrivateBrowsingUtils.isWindowPrivate(window)) { - containersTab.setAttribute("disabled", "true"); + if (event.target.getAttribute('anonid') == "newtab-popup" || + event.target.id == "newtab-popup") { + createUserContextMenu(event); + } else { + document.getElementById("alltabs-popup-separator-1").hidden = !containersEnabled; + let containersTab = document.getElementById("alltabs_containersTab"); + + containersTab.hidden = !containersEnabled; + if (PrivateBrowsingUtils.isWindowPrivate(window)) { + containersTab.setAttribute("disabled", "true"); + } + + document.getElementById("alltabs_undoCloseTab").disabled = + SessionStore.getClosedTabCount(window) == 0; + + var tabcontainer = gBrowser.tabContainer; + + // Listen for changes in the tab bar. + tabcontainer.addEventListener("TabAttrModified", this, false); + tabcontainer.addEventListener("TabClose", this, false); + tabcontainer.mTabstrip.addEventListener("scroll", this, false); + + let tabs = gBrowser.visibleTabs; + for (var i = 0; i < tabs.length; i++) { + if (!tabs[i].pinned) + this._createTabMenuItem(tabs[i]); + } + this._updateTabsVisibilityStatus(); } - - document.getElementById("alltabs_undoCloseTab").disabled = - SessionStore.getClosedTabCount(window) == 0; - - var tabcontainer = gBrowser.tabContainer; - - // Listen for changes in the tab bar. - tabcontainer.addEventListener("TabAttrModified", this, false); - tabcontainer.addEventListener("TabClose", this, false); - tabcontainer.mTabstrip.addEventListener("scroll", this, false); - - let tabs = gBrowser.visibleTabs; - for (var i = 0; i < tabs.length; i++) { - if (!tabs[i].pinned) - this._createTabMenuItem(tabs[i]); - } - this._updateTabsVisibilityStatus(); ]]> @@ -6712,6 +6778,9 @@ menuItem.tab.mCorrespondingMenuitem = null; this.removeChild(menuItem); } + if (menuItem.hasAttribute("usercontextid")) { + this.removeChild(menuItem); + } } var tabcontainer = gBrowser.tabContainer; tabcontainer.mTabstrip.removeEventListener("scroll", this, false); diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini index dc65a66bce44..0e9b3191d245 100644 --- a/browser/components/contextualidentity/test/browser/browser.ini +++ b/browser/components/contextualidentity/test/browser/browser.ini @@ -13,6 +13,7 @@ skip-if = (debug && (os == "win" || os == "linux")) # intermittent negative leak [browser_eme.js] [browser_favicon.js] [browser_forgetaboutsite.js] +[browser_newtabButton.js] [browser_usercontext.js] [browser_usercontextid_tabdrop.js] skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276 diff --git a/browser/components/contextualidentity/test/browser/browser_newtabButton.js b/browser/components/contextualidentity/test/browser/browser_newtabButton.js new file mode 100644 index 000000000000..c74844cf63f8 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/browser_newtabButton.js @@ -0,0 +1,34 @@ +"use strict"; + +// Testing that when the user opens the add tab menu and clicks menu items +// the correct context id is opened + +add_task(function* test() { + yield SpecialPowers.pushPrefEnv({"set": [ + ["privacy.userContext.enabled", true] + ]}); + + let newTab = document.getElementById('tabbrowser-tabs'); + let newTabButton = document.getAnonymousElementByAttribute(newTab, "anonid", "tabs-newtab-button"); + ok(newTabButton, "New tab button exists"); + ok(!newTabButton.hidden, "New tab button is visible"); + let popup = document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup"); + + for (let i = 1; i <= 4; i++) { + let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown"); + EventUtils.synthesizeMouseAtCenter(newTabButton, {type: "mousedown"}); + + yield popupShownPromise; + let contextIdItem = popup.querySelector(`menuitem[usercontextid="${i}"]`); + + ok(contextIdItem, `User context id ${i} exists`); + + let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser); + EventUtils.synthesizeMouseAtCenter(contextIdItem, {}); + + let tab = yield waitForTabPromise; + + is(tab.getAttribute('usercontextid'), i, `New tab has UCI equal ${i}`); + yield BrowserTestUtils.removeTab(tab); + } +});