Bug 1272416 - Adding in a new alltabs dropdown when containers are enabled and tabs are not overflowing
MozReview-Commit-ID: AznBD9VNedA
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -129,16 +129,17 @@ tabbrowser {
.tabbrowser-tabs {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
}
#tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
#tabbrowser-tabs:not([overflow="true"]) + #new-tab-button,
#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
+#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-alltabs-few-button,
#TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
#TabsToolbar[customizing="true"] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
visibility: collapse;
}
#tabbrowser-tabs:not([overflow="true"])[using-closing-tabs-spacer] ~ #alltabs-button {
visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
}
@@ -196,16 +197,17 @@ tabbrowser {
z-index: 2;
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
}
.tabbrowser-tabs[movingtab] > .tabbrowser-tab[fadein]:not([selected]) {
transition: transform 200ms ease-out;
}
+.alltabs-few-popup,
#alltabs-popup {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
}
toolbar[printpreview="true"] {
-moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
}
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4889,22 +4889,41 @@
<children/>
<xul:toolbarbutton class="tabs-newtab-button"
anonid="tabs-newtab-button"
command="cmd_newNavigatorTab"
onclick="checkForMiddleClick(this, event);"
onmouseover="document.getBindingParent(this)._enterNewTab();"
onmouseout="document.getBindingParent(this)._leaveNewTab();"
tooltip="dynamic-shortcut-tooltip"/>
+
+ <xul:toolbarbutton anonid="tabs-alltabs-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-few-button"
+ type="menu"
+ removable="false">
+ <xul:menupopup anonid="alltabs-popup"
+ class="alltabs-few-popup"
+ position="after_end">
+ <xul:menuitem anonid="tabs-alltabs-containersLabel"
+ disabled="true" />
+ <xul:menuseparator anonid="alltabs-popup-separator-1"/>
+ <xul:menuitem anonid="alltabs_undoCloseTab"
+ class="menuitem-iconic"
+ key="key_undoCloseTab"
+ observes="History:UndoCloseTab"/>
+ <xul:menuseparator anonid="alltabs-popup-separator-2"/>
+ </xul:menupopup>
+ </xul:toolbarbutton>
+
<xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
style="width: 0;"/>
</xul:arrowscrollbox>
</content>
- <implementation implements="nsIDOMEventListener">
+ <implementation implements="nsIDOMEventListener, nsIObserver">
<constructor>
<![CDATA[
this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
var tab = this.firstChild;
tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
tab.setAttribute("crop", "end");
tab.setAttribute("onerror", "this.removeAttribute('image');");
@@ -4913,19 +4932,43 @@
window.addEventListener("load", this, false);
try {
this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled");
} catch (ex) {
this._tabAnimationLoggingEnabled = false;
}
this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
+ this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
+ Services.prefs.addObserver("privacy.userContext.enabled", this, false);
+
+ let alltabsButton = document.getAnonymousElementByAttribute(this, "anonid", "tabs-alltabs-button");
+ let mainAlltabsButton = document.getElementById("alltabs-button");
+ alltabsButton.setAttribute("tooltiptext", mainAlltabsButton.getAttribute("tooltiptext"));
+ let mainLabel = document.getElementById("alltabs_containersTab");
+ this.alltabsFewContainersLabel.label = mainLabel.label;
+ let mainUndo = document.getElementById("alltabs_undoCloseTab");
+ this.alltabsFewUndoCloseTab.label = mainUndo.label;
]]>
</constructor>
+ <destructor>
+ <![CDATA[
+ Services.prefs.removeObserver("privacy.userContext.enabled", this);
+ ]]>
+ </destructor>
+
+ <field name="alltabsFewContainersLabel" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "tabs-alltabs-containersLabel");
+ </field>
+
+ <field name="alltabsFewUndoCloseTab" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "alltabs_undoCloseTab");
+ </field>
+
<field name="tabbrowser" readonly="true">
document.getElementById(this.getAttribute("tabbrowser"));
</field>
<field name="tabbox" readonly="true">
this.tabbrowser.mTabBox;
</field>
@@ -4949,16 +4992,31 @@
<property name="_isCustomizing" readonly="true">
<getter>
let root = document.documentElement;
return root.getAttribute("customizing") == "true" ||
root.getAttribute("customize-exiting") == "true";
</getter>
</property>
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body><![CDATA[
+ switch(aTopic) {
+ case "nsPref:changed":
+ // This is the only pref observed.
+ let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
+ document.getAnonymousElementByAttribute(this, "anonid", "tabs-alltabs-button").hidden = !containersEnabled;
+ break;
+ }
+ ]]></body>
+ </method>
+
<method name="_setPositionalAttributes">
<body><![CDATA[
let visibleTabs = this.tabbrowser.visibleTabs;
if (!visibleTabs.length)
return;
let selectedIndex = visibleTabs.indexOf(this.selectedItem);
@@ -6596,37 +6654,52 @@
if (aMenuitem.firstChild)
aMenuitem.firstChild.remove();
if (aTab.hasAttribute("muted"))
addEndImage().setAttribute("muted", "true");
else if (aTab.hasAttribute("soundplaying"))
addEndImage().setAttribute("soundplaying", "true");
]]></body>
</method>
+
</implementation>
<handlers>
<handler event="popupshowing">
<![CDATA[
if (event.target.getAttribute('id') == "alltabs_containersMenuTab") {
createUserContextMenu(event);
return;
}
-
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");
+ let undoTab;
+
+ if (event.target.getAttribute('anonid') == "alltabs-popup") {
+ let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
+ let containersLabel = gBrowser.tabContainer.alltabsFewContainersLabel;
+ containersLabel.hidden = !containersEnabled;
+ if (containersEnabled) {
+ createUserContextMenuAfter(containersLabel);
+ }
+
+ undoTab = gBrowser.tabContainer.alltabsFewUndoCloseTab;
+ } 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");
+ }
+
+ undoTab = document.getElementById("alltabs_undoCloseTab");
}
- document.getElementById("alltabs_undoCloseTab").disabled =
- SessionStore.getClosedTabCount(window) == 0;
+ undoTab.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);
@@ -6646,16 +6719,19 @@
// clear out the menu popup and remove the listeners
for (let i = this.childNodes.length - 1; i > 0; i--) {
let menuItem = this.childNodes[i];
if (menuItem.tab) {
menuItem.tab.mCorrespondingMenuitem = null;
this.removeChild(menuItem);
}
+ if (menuItem.hasAttribute("usercontextid")) {
+ this.removeChild(menuItem);
+ }
}
var tabcontainer = gBrowser.tabContainer;
tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
tabcontainer.removeEventListener("TabAttrModified", this, false);
tabcontainer.removeEventListener("TabClose", this, false);
]]></handler>
<handler event="DOMMenuItemActive">
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -409,16 +409,30 @@ function checkForMiddleClick(node, event
// Populate a menu with user-context menu items. This method should be called
// by onpopupshowing passing the event as first argument. addCommandAttribute
// param is used to set the 'command' attribute in the new menuitem elements.
function createUserContextMenu(event, addCommandAttribute = true) {
while (event.target.hasChildNodes()) {
event.target.removeChild(event.target.firstChild);
}
+ const docfrag = createUserContextItems(addCommandAttribute);
+
+ event.target.appendChild(docfrag);
+ return true;
+}
+
+function createUserContextMenuAfter(item, addCommandAttribute = true) {
+ const docfrag = createUserContextItems(addCommandAttribute);
+
+ item.parentNode.insertBefore(docfrag, item.nextSibling);
+ return true;
+}
+
+function createUserContextItems(addCommandAttribute = true) {
let bundle = document.getElementById("bundle_browser");
let docfrag = document.createDocumentFragment();
ContextualIdentityService.getIdentities().forEach(identity => {
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("usercontextid", identity.userContextId);
menuitem.setAttribute("label", bundle.getString(identity.label));
menuitem.setAttribute("accesskey", bundle.getString(identity.accessKey));
@@ -427,19 +441,17 @@ function createUserContextMenu(event, ad
if (addCommandAttribute) {
menuitem.setAttribute("command", "Browser:NewUserContextTab");
}
menuitem.setAttribute("image", identity.icon);
docfrag.appendChild(menuitem);
});
-
- event.target.appendChild(docfrag);
- return true;
+ return docfrag;
}
// Closes all popups that are ancestors of the node.
function closeMenus(node)
{
if ("tagName" in node) {
if (node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
&& (node.tagName == "menupopup" || node.tagName == "popup"))
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -5,15 +5,16 @@ support-files =
file_reflect_cookie_into_title.html
serviceworker.html
worker.js
[browser_aboutURLs.js]
[browser_usercontext.js]
[browser_usercontextid_tabdrop.js]
skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
+[browser_alltabsButton.js]
[browser_windowName.js]
tags = openwindow
[browser_windowOpen.js]
tags = openwindow
[browser_serviceworkers.js]
[browser_broadcastchannel.js]
[browser_blobUrl.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_alltabsButton.js
@@ -0,0 +1,34 @@
+"use strict";
+
+// Testing that when the user opens the all 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 allTabs = document.getElementById('tabbrowser-tabs');
+ let allTabsButton = document.getAnonymousElementByAttribute(allTabs, "anonid", "tabs-alltabs-button");
+ ok(allTabsButton, "All tabs button exists");
+ ok(!allTabsButton.hidden, "All tabs button is visible");
+ let popup = document.getAnonymousElementByAttribute(allTabs, "anonid", "alltabs-popup");
+
+ for (let i = 1; i <= 4; i++) {
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(allTabsButton, {});
+
+ 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);
+ }
+});
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1627,29 +1627,33 @@ html|span.ac-emphasize-text-url {
background-color: Highlight;
transition: none;
}
#TabsToolbar .toolbarbutton-1 {
margin-bottom: var(--tab-toolbar-navbar-overlap);
}
+.tabs-alltabs-few-button,
#alltabs-button {
list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
}
+#TabsToolbar[brighttext] .tabs-alltabs-few-button,
#TabsToolbar[brighttext] > #alltabs-button,
#TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
list-style-image: url("chrome://browser/skin/tabbrowser/alltabs-inverted.png");
}
+#TabsToolbar .tabs-alltabs-few-button > .toolbarbutton-icon,
#alltabs-button > .toolbarbutton-icon {
padding: 9px 6px 6px;
}
+.tabs-alltabs-few-button > .toolbarbutton-menu-dropmarker,
#alltabs-button > .toolbarbutton-menu-dropmarker {
display: none;
}
/* All tabs menupopup */
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2916,62 +2916,75 @@ toolbarbutton.chevron > .toolbarbutton-m
}
#TabsToolbar > #new-tab-button > .toolbarbutton-icon,
#TabsToolbar > toolbarpaletteitem > #new-tab-button > .toolbarbutton-icon {
width: 18px;
}
}
+.tabs-alltabs-few-button,
#alltabs-button {
list-style-image: url(chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png);
-moz-image-region: rect(0, 17px, 20px, 0);
}
+#TabsToolbar[brighttext] .tabs-alltabs-few-button,
#TabsToolbar[brighttext] #alltabs-button {
list-style-image: url(chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon-inverted.png);
}
+.tabs-alltabs-few-button:not([disabled="true"]):hover,
#alltabs-button:not([disabled="true"]):hover {
-moz-image-region: rect(0, 34px, 20px, 17px);
}
+.tabs-alltabs-few-button[open="true"]:not([disabled="true"]),
+.tabs-alltabs-few-button:not([disabled="true"]):hover:active,
#alltabs-button[open="true"]:not([disabled="true"]),
#alltabs-button:not([disabled="true"]):hover:active {
-moz-image-region: rect(0, 51px, 20px, 34px);
}
@media (min-resolution: 2dppx) {
+ .tabs-alltabs-few-button,
#alltabs-button {
list-style-image: url(chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon@2x.png);
-moz-image-region: rect(0, 34px, 40px, 0);
}
+ #TabsToolbar[brighttext] .tabs-alltabs-few-button,
#TabsToolbar[brighttext] #alltabs-button {
list-style-image: url(chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon-inverted@2x.png);
}
+ .tabs-alltabs-few-button:not([disabled="true"]):hover,
#alltabs-button:not([disabled="true"]):hover {
-moz-image-region: rect(0, 68px, 40px, 34px);
}
+ .tabs-alltabs-few-button[open="true"]:not([disabled="true"]),
+ .tabs-alltabs-few-button:not([disabled="true"]):hover:active,
#alltabs-button[open="true"]:not([disabled="true"]),
#alltabs-button:not([disabled="true"]):hover:active {
-moz-image-region: rect(0, 102px, 40px, 68px);
}
+ .tabs-alltabs-few-button > .toolbarbutton-icon,
#alltabs-button > .toolbarbutton-icon {
width: 17px;
}
}
+.tabs-alltabs-few-button > .toolbarbutton-menu-dropmarker,
#alltabs-button > .toolbarbutton-menu-dropmarker {
display: none;
}
+.tabs-alltabs-few-button > .toolbarbutton-icon,
#alltabs-button > .toolbarbutton-icon {
margin-inline-end: 2px;
}
/* All Tabs Menupopup */
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2118,29 +2118,33 @@ html|span.ac-emphasize-text-url {
#TabsToolbar > toolbarpaletteitem > #new-tab-button > .toolbarbutton-icon {
width: 16px;
}
#TabsToolbar > #new-tab-button {
width: 26px;
}
+.tabs-alltabs-few-button,
#alltabs-button {
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
}
+#TabsToolbar[brighttext] .tabs-alltabs-few-button,
#TabsToolbar[brighttext] > #alltabs-button,
#TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
}
+.tabs-alltabs-few-button > .toolbarbutton-icon,
#alltabs-button > .toolbarbutton-icon {
margin: 0 2px;
}
+.tabs-alltabs-few-button > .toolbarbutton-menu-dropmarker,
#alltabs-button > .toolbarbutton-menu-dropmarker {
display: none;
}
/* All tabs menupopup */
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}