Bug 1408061 - Show hidden tabs in all tabs menu r?dao
MozReview-Commit-ID: FpbeT1FwEWe
--- 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
@@ -696,16 +696,21 @@
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="&hiddenTabs.label;">
+ <menupopup id="alltabs_hiddenTabsMenu"/>
+ </menu>
+ <menuseparator id="alltabs-popup-separator-3"/>
</menupopup>
</toolbarbutton>
#ifdef CAN_DRAW_IN_TITLEBAR
<hbox class="titlebar-placeholder" type="post-tabs"
ordinal="1000"
skipintoolbarset="true"/>
#endif
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -3268,25 +3268,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");
}
@@ -3294,16 +3296,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
@@ -410,16 +410,26 @@
} else {
this.removeAttribute("closebuttons");
}
});
});
]]></body>
</method>
+ <method name="_updateHiddenTabsStatus">
+ <body><![CDATA[
+ if (gBrowser.visibleTabs.length < gBrowser.tabs.length) {
+ this.setAttribute("hashiddentabs", "true");
+ } 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>
@@ -2146,16 +2156,29 @@
</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 fragment = document.createDocumentFragment();
+
+ for (let tab of gBrowser.tabs) {
+ if (tab.hidden) {
+ fragment.appendChild(this._createTabMenuItem(tab));
+ }
+ }
+
+ event.target.textContent = "";
+ event.target.appendChild(fragment);
+
+ 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,
@@ -2168,16 +2191,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();
@@ -2193,30 +2220,35 @@
]]></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);
+ menuItem.remove();
}
if (menuItem.hasAttribute("usercontextid")) {
- this.removeChild(menuItem);
+ menuItem.remove();
}
}
- var tabcontainer = gBrowser.tabContainer;
- tabcontainer.removeEventListener("TabAttrModified", this);
- tabcontainer.removeEventListener("TabClose", this);
+
+ if (container == this) {
+ gBrowser.tabContainer.removeEventListener("TabAttrModified", this);
+ gBrowser.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,21 @@ 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 allTabsButton = win.document.getElementById("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 +124,19 @@ 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 allTabsButton = win.document.getElementById("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/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -55,16 +55,17 @@ can reach it easily. -->
<!ENTITY moveToNewWindow.label "Move to New Window">
<!ENTITY moveToNewWindow.accesskey "W">
<!ENTITY bookmarkAllTabs.label "Bookmark All Tabs…">
<!ENTITY bookmarkAllTabs.accesskey "T">
<!ENTITY undoCloseTab.label "Undo Close Tab">
<!ENTITY undoCloseTab.accesskey "U">
<!ENTITY closeTab.label "Close Tab">
<!ENTITY closeTab.accesskey "c">
+<!ENTITY hiddenTabs.label "Hidden Tabs">
<!ENTITY listAllTabs.label "List all tabs">
<!ENTITY tabCmd.label "New Tab">
<!ENTITY tabCmd.accesskey "T">
<!ENTITY tabCmd.commandkey "t">
<!-- LOCALIZATION NOTE (openLocationCmd.label): "Open Location" is only
displayed on OS X, and only on windows that aren't main browser windows, or