Bug 1408061 - Show hidden tabs in all tabs menu r?dao
MozReview-Commit-ID: A2eZsBq2Slf
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -127,17 +127,17 @@ panelview[mainview] > .panel-header {
}
.tab-stack {
/* Without this, pinned tabs get a bit too tall when the tabstrip overflows. */
vertical-align: top;
}
}
-#tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
+#tabbrowser-tabs:not([overflow="true"]):not([hashiddentabs]) ~ #alltabs-button,
#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow="true"]) ~ #new-tab-button,
#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > .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 {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -590,16 +590,35 @@
<menuitem label="&selectAllCmd.label;"
accesskey="&selectAllCmd.accesskey;"
cmd="cmd_selectAll"/>
<menuseparator/>
<menuitem label="&syncSyncNowItem.label;"
accesskey="&syncSyncNowItem.accesskey;"
id="syncedTabsRefreshFilter"/>
</menupopup>
+
+ <menupopup id="alltabs-popup"
+ position="after_end">
+ <menuitem id="alltabs_undoCloseTab"
+ key="key_undoCloseTab"
+ label="&undoCloseTab.label;"
+ observes="History:UndoCloseTab"/>
+ <menuseparator id="alltabs-popup-separator-1"/>
+ <menu id="alltabs_containersTab"
+ label="&newUserContext.label;">
+ <menupopup id="alltabs_containersMenuTab" />
+ </menu>
+ <menuseparator id="alltabs-popup-separator-2"/>
+ <menu id="alltabs_hiddenTabs"
+ label="Hidden Tabs">
+ <menupopup id="alltabs_hiddenTabsMenu" />
+ </menu>
+ <menuseparator id="alltabs-popup-separator-3"/>
+ </menupopup>
</popupset>
<box id="appMenu-viewCache" hidden="true"/>
#ifdef CAN_DRAW_IN_TITLEBAR
<vbox id="titlebar">
<hbox id="titlebar-content">
<spacer id="titlebar-spacer" flex="1"/>
<hbox id="titlebar-buttonbox-container">
@@ -681,33 +700,20 @@
ondragenter="newTabButtonObserver.onDragOver(event)"
ondragexit="newTabButtonObserver.onDragExit(event)"
cui-areatype="toolbar"
removable="true"/>
<toolbarbutton id="alltabs-button"
class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
type="menu"
+ popup="alltabs-popup"
label="&listAllTabs.label;"
tooltiptext="&listAllTabs.label;"
- removable="false">
- <menupopup id="alltabs-popup"
- position="after_end">
- <menuitem id="alltabs_undoCloseTab"
- key="key_undoCloseTab"
- label="&undoCloseTab.label;"
- observes="History:UndoCloseTab"/>
- <menuseparator id="alltabs-popup-separator-1"/>
- <menu id="alltabs_containersTab"
- label="&newUserContext.label;">
- <menupopup id="alltabs_containersMenuTab" />
- </menu>
- <menuseparator id="alltabs-popup-separator-2"/>
- </menupopup>
- </toolbarbutton>
+ removable="false"/>
#ifdef CAN_DRAW_IN_TITLEBAR
<hbox class="titlebar-placeholder" type="post-tabs"
ordinal="1000"
skipintoolbarset="true"/>
#endif
<button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;"
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -3269,25 +3269,27 @@ window._gBrowser = {
showOnlyTheseTabs(aTabs) {
for (let tab of this.tabs) {
if (!aTabs.includes(tab))
this.hideTab(tab);
else
this.showTab(tab);
}
+ this.tabContainer._updateHiddenTabsStatus();
this.tabContainer._handleTabSelect(true);
},
showTab(aTab) {
if (aTab.hidden) {
aTab.removeAttribute("hidden");
this._visibleTabs = null; // invalidate cache
this.tabContainer._updateCloseButtons();
+ this.tabContainer._updateHiddenTabsStatus();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabShow", true, false);
aTab.dispatchEvent(event);
SessionStore.deleteTabValue(aTab, "hiddenBy");
}
@@ -3295,16 +3297,17 @@ window._gBrowser = {
hideTab(aTab, aSource) {
if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
!aTab.closing && !aTab._sharingState) {
aTab.setAttribute("hidden", "true");
this._visibleTabs = null; // invalidate cache
this.tabContainer._updateCloseButtons();
+ this.tabContainer._updateHiddenTabsStatus();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabHide", true, false);
aTab.dispatchEvent(event);
if (aSource) {
SessionStore.setTabValue(aTab, "hiddenBy", aSource);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -454,16 +454,26 @@
} else {
this.removeAttribute("closebuttons");
}
});
});
]]></body>
</method>
+ <method name="_updateHiddenTabsStatus">
+ <body><![CDATA[
+ if (gBrowser.visibleTabs.length < gBrowser.tabs.length) {
+ this.setAttribute("hashiddentabs", "");
+ } else {
+ this.removeAttribute("hashiddentabs");
+ }
+ ]]></body>
+ </method>
+
<method name="_handleTabSelect">
<parameter name="aInstant"/>
<body><![CDATA[
if (this.getAttribute("overflow") == "true")
this.arrowScrollbox.ensureElementIsVisible(this.selectedItem, aInstant);
this.selectedItem._notselectedsinceload = false;
]]></body>
@@ -2190,16 +2200,27 @@
</implementation>
<handlers>
<handler event="popupshowing">
<![CDATA[
if (event.target.getAttribute("id") == "alltabs_containersMenuTab") {
createUserContextMenu(event, {useAccessKeys: false});
return;
+ } else if (event.target.getAttribute("id") == "alltabs_hiddenTabsMenu") {
+ let menu = event.target;
+ menu.textContent = "";
+
+ gBrowser.tabs.forEach(tab => {
+ if (tab.hidden) {
+ menu.appendChild(this._createTabMenuItem(tab, menu));
+ }
+ });
+
+ return;
}
let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
if (event.target.getAttribute("anonid") == "newtab-popup" ||
event.target.id == "newtab-popup") {
createUserContextMenu(event, {
useAccessKeys: false,
@@ -2212,16 +2233,20 @@
containersTab.hidden = !containersEnabled;
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
containersTab.setAttribute("disabled", "true");
}
document.getElementById("alltabs_undoCloseTab").disabled =
SessionStore.getClosedTabCount(window) == 0;
+ let showHiddenTabs = gBrowser.visibleTabs.length < gBrowser.tabs.length;
+ document.getElementById("alltabs_hiddenTabs").hidden = !showHiddenTabs;
+ document.getElementById("alltabs-popup-separator-3").hidden = !showHiddenTabs;
+
var tabcontainer = gBrowser.tabContainer;
// Listen for changes in the tab bar.
tabcontainer.addEventListener("TabAttrModified", this);
tabcontainer.addEventListener("TabClose", this);
let tabs = gBrowser.visibleTabs;
let fragment = document.createDocumentFragment();
@@ -2237,30 +2262,37 @@
]]></handler>
<handler event="popuphidden">
<![CDATA[
if (event.target.getAttribute("id") == "alltabs_containersMenuTab") {
return;
}
+ // This could be the visible or hidden tabs menu container.
+ let container = event.target;
+
// clear out the menu popup and remove the listeners
- for (let i = this.childNodes.length - 1; i > 0; i--) {
- let menuItem = this.childNodes[i];
+ for (let i = container.childNodes.length - 1; i > 0; i--) {
+ let menuItem = container.childNodes[i];
if (menuItem.tab) {
menuItem.tab.mCorrespondingMenuitem = null;
- this.removeChild(menuItem);
+ container.removeChild(menuItem);
}
if (menuItem.hasAttribute("usercontextid")) {
+ // TODO: Should this be container?
this.removeChild(menuItem);
}
}
- var tabcontainer = gBrowser.tabContainer;
- tabcontainer.removeEventListener("TabAttrModified", this);
- tabcontainer.removeEventListener("TabClose", this);
+
+ if (container == this) {
+ var tabcontainer = gBrowser.tabContainer;
+ tabcontainer.removeEventListener("TabAttrModified", this);
+ tabcontainer.removeEventListener("TabClose", this);
+ }
]]></handler>
<handler event="DOMMenuItemActive">
<![CDATA[
var tab = event.target.tab;
if (tab) {
let overLink = tab.linkedBrowser.currentURI.displaySpec;
if (overLink == "about:blank")
--- a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
@@ -96,16 +96,23 @@ add_task(async function test_tabs_showhi
};
// Set up a test session with 2 windows and 5 tabs.
let oldState = SessionStore.getBrowserState();
let restored = TestUtils.topicObserved("sessionstore-browser-state-restored");
SessionStore.setBrowserState(JSON.stringify(sessData));
await restored;
+ for (let win of BrowserWindowIterator()) {
+ let tabbrowserTabs = win.document.getElementById("tabbrowser-tabs");
+ let allTabsButton = win.document.getAnonymousElementByAttribute(
+ tabbrowserTabs, "anonid", "tabs-alltabs-button");
+ is(getComputedStyle(allTabsButton).visibility, "collapse", "The all tabs button is hidden");
+ }
+
// Attempt to hide all the tabs, however the active tab in each window cannot
// be hidden, so the result will be 3 hidden tabs.
extension.sendMessage("hideall");
await extension.awaitMessage("hidden");
// We have 2 windows in this session. Otherwin is the non-current window.
// In each window, the first tab will be the selected tab and should not be
// hidden. The rest of the tabs should be hidden at this point. Hidden
@@ -119,16 +126,21 @@ add_task(async function test_tabs_showhi
let tabs = Array.from(win.gBrowser.tabs.values());
ok(!tabs[0].hidden, "first tab not hidden");
for (let i = 1; i < tabs.length; i++) {
ok(tabs[i].hidden, "tab hidden value is correct");
let id = SessionStore.getTabValue(tabs[i], "hiddenBy");
is(id, extension.id, "tab hiddenBy value is correct");
await TabStateFlusher.flush(tabs[i].linkedBrowser);
}
+
+ let tabbrowserTabs = win.document.getElementById("tabbrowser-tabs");
+ let allTabsButton = win.document.getAnonymousElementByAttribute(
+ tabbrowserTabs, "anonid", "tabs-alltabs-button");
+ is(getComputedStyle(allTabsButton).visibility, "visible", "The all tabs button is visible");
}
// Close the other window then restore it to test that the tabs are
// restored with proper hidden state, and the correct extension id.
await BrowserTestUtils.closeWindow(otherwin);
otherwin = SessionStore.undoCloseWindow(0);
await BrowserTestUtils.waitForEvent(otherwin, "load");
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -704,17 +704,17 @@
.tabs-newtab-button,
#TabsToolbar > #new-tab-button ,
#TabsToolbar > toolbarpaletteitem > #new-tab-button {
list-style-image: url(chrome://browser/skin/tabbrowser/newtab.svg);
}
/* All tabs button and menupopup */
-#alltabs-button {
+.tabs-alltabs-button {
list-style-image: url(chrome://global/skin/icons/arrow-dropdown-16.svg);
}
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
-moz-context-properties: fill;
fill: currentColor;
}