Bug 1332447 part 2 - Implementation of the tabstrip hidding API.
MozReview-Commit-ID: Il6lCTrDqOU
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -72,16 +72,92 @@ let tabListener = {
this.initTabReady();
this.tabReadyPromises.set(nativeTab, deferred);
}
}
return deferred.promise;
},
};
+let tabsVisilityTracker = new EventEmitter();
+let tabVisibilityManager = {
+ // Set of extension contexts that requested the tabstrip to be hidden.
+ tabHiddingContexts: new Set(),
+
+ get tabsVisibility() {
+ return this.tabHiddingContexts.size == 0;
+ },
+
+ set tabsVisibility(isVisible) {
+ this._updateCurrentWindowsTabsVisibility(isVisible);
+
+ if (!isVisible) {
+ tabsVisilityTracker.emit("visibilityChange", true);
+
+ this._hideTabs = (window) => {
+ // The titlebar height is calculated dynamically, let TabsInTitlebar
+ // do its job first then collapse the tabstrip on the next tick.
+ Promise.resolve().then(() => {
+ this._updateTabsVisibility(window, false);
+ });
+ };
+
+ windowTracker.addOpenListener(this._hideTabs);
+ } else {
+ tabsVisilityTracker.emit("visibilityChange", false);
+ windowTracker.removeOpenListener(this._hideTabs);
+ }
+ },
+
+ _updateCurrentWindowsTabsVisibility(isVisible) {
+ for (let window of windowTracker.browserWindows()) {
+ this._updateTabsVisibility(window, isVisible);
+ }
+ },
+
+ _updateTabsVisibility(window, isVisible) {
+ let toolbar = window.document.getElementById("TabsToolbar");
+ if (isVisible) {
+ toolbar.classList.remove("webext-collapsed");
+ } else {
+ toolbar.classList.add("webext-collapsed");
+ }
+ },
+
+ // Do not call directly. Called by contexts when they shut down.
+ close(context) {
+ this.tabHiddingContexts.delete(context);
+ if (this.tabHiddingContexts.size == 0) {
+ this.tabsVisibility = true;
+ }
+ },
+
+ setTabsVisibility(context, visible) {
+ if (!visible) {
+ if (this.tabHiddingContexts.has(context)) {
+ return;
+ }
+ let wasVisible = this.tabsVisibility;
+ this.tabHiddingContexts.add(context);
+ context.callOnClose(this);
+ // First context asking the tabstrip to be hidden.
+ if (wasVisible) {
+ this.tabsVisibility = false;
+ }
+ } else {
+ // Visible true always takes priority: clear everyone.
+ for (let context of this.tabHiddingContexts) {
+ context.forgetOnClose(this);
+ }
+ this.tabHiddingContexts.clear();
+ this.tabsVisibility = true;
+ }
+ },
+};
+
this.tabs = class extends ExtensionAPI {
getAPI(context) {
let {extension} = context;
let {tabManager} = extension;
function getTabOrActive(tabId) {
if (tabId !== null) {
@@ -314,16 +390,27 @@ this.tabs = class extends ExtensionAPI {
windowTracker.removeListener("TabAttrModified", listener);
windowTracker.removeListener("TabPinned", listener);
windowTracker.removeListener("TabUnpinned", listener);
windowTracker.removeListener("TabBrowserInserted", listener);
tabTracker.off("tab-isarticle", isArticleChangeListener);
};
}).api(),
+ onVisibilityChanged: new EventManager(context, "tabs.onVisibilityChanged", fire => {
+ let listener = (eventName, hidden) => {
+ fire.async(hidden);
+ };
+
+ tabsVisilityTracker.on("visibilityChange", listener);
+ return () => {
+ tabsVisibilityTracker.off("visibilityChange", listener);
+ };
+ }).api(),
+
create(createProperties) {
return new Promise((resolve, reject) => {
let window = createProperties.windowId !== null ?
windowTracker.getWindow(createProperties.windowId, context) :
windowTracker.topWindow;
if (!window.gBrowser) {
let obs = (finishedWindow, topic, data) => {
@@ -933,13 +1020,21 @@ this.tabs = class extends ExtensionAPI {
let tab = await promiseTabWhenReady(tabId);
if (!tab.isInReaderMode && !tab.isArticle) {
throw new ExtensionError("The specified tab cannot be placed into reader mode.");
}
tab = getTabOrActive(tabId);
tab.linkedBrowser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
},
+
+ getTabsVisibility() {
+ return tabVisibilityManager.tabsVisibility;
+ },
+
+ setTabsVisibility(visible) {
+ return tabVisibilityManager.setTabsVisibility(context, visible);
+ }
},
};
return self;
}
};
--- a/browser/components/extensions/schemas/tabs.json
+++ b/browser/components/extensions/schemas/tabs.json
@@ -7,17 +7,18 @@
"namespace": "manifest",
"types": [
{
"$extend": "OptionalPermission",
"choices": [{
"type": "string",
"enum": [
"activeTab",
- "tabs"
+ "tabs",
+ "tabsVisibility"
]
}]
}
]
},
{
"namespace": "tabs",
"description": "Use the <code>browser.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
@@ -1187,16 +1188,44 @@
{
"type": "string",
"name": "status",
"description": "Save status: saved, replaced, canceled, not_saved, not_replaced."
}
]
}
]
+ },
+ {
+ "name": "getTabsVisibility",
+ "type": "function",
+ "description": "Query the visibility of the tabstrip.",
+ "async": "callback",
+ "parameters": [
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": [
+ {
+ "name": "hidden",
+ "type": "boolean",
+ "description": "True if the tabstrip is hidden."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "setTabsVisibility",
+ "type": "function",
+ "description": "Sets the visibility of the tabstrip.",
+ "permissions": ["tabsVisibility"],
+ "parameters": [{
+ "type": "boolean"
+ }]
}
],
"events": [
{
"name": "onCreated",
"type": "function",
"description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
"parameters": [
@@ -1468,12 +1497,22 @@
"name": "ZoomChangeInfo",
"properties": {
"tabId": {"type": "integer", "minimum": 0},
"oldZoomFactor": {"type": "number"},
"newZoomFactor": {"type": "number"},
"zoomSettings": {"$ref": "ZoomSettings"}
}
}]
+ },
+ {
+ "name": "onVisibilityChanged",
+ "type": "function",
+ "description": "Fired when the tabstrip visibility changes.",
+ "parameters": [{
+ "type": "boolean",
+ "name": "hidden",
+ "description": "True if the tabstrip is currently invisible."
+ }]
}
]
}
]
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -114,16 +114,17 @@ webextPerms.description.notifications=Di
webextPerms.description.pkcs11=Provide cryptographic authentication services
webextPerms.description.privacy=Read and modify privacy settings
webextPerms.description.proxy=Control browser proxy settings
webextPerms.description.sessions=Access recently closed tabs
webextPerms.description.tabs=Access browser tabs
webextPerms.description.topSites=Access browsing history
webextPerms.description.unlimitedStorage=Store unlimited amount of client-side data
webextPerms.description.webNavigation=Access browser activity during navigation
+webextPerms.description.tabsVisibility=Changes the visibility of the tab strip
webextPerms.hostDescription.allUrls=Access your data for all websites
# LOCALIZATION NOTE (webextPerms.hostDescription.wildcard)
# %S will be replaced by the DNS domain for which a webextension
# is requesting access (e.g., mozilla.org)
webextPerms.hostDescription.wildcard=Access your data for sites in the %S domain
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -67,16 +67,20 @@
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
border-top: 1px solid var(--tabs-border) !important;
background-clip: padding-box;
/* Position the toolbar above the bottom of background tabs */
position: relative;
z-index: 1;
}
+#TabsToolbar.webext-collapsed > * {
+ visibility: collapse;
+}
+
#nav-bar {
padding-top: 2px;
padding-bottom: 2px;
}
#browser-bottombox {
/* opaque for layers optimization */
background-color: -moz-Dialog;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -154,16 +154,24 @@
}
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
/* The toolbar buttons that animate are only visible when the #TabsToolbar is not collapsed.
The animations use position:absolute and require a positioned #nav-bar. */
position: relative;
}
+#TabsToolbar.webext-collapsed {
+ min-height: 27px;
+}
+
+#TabsToolbar.webext-collapsed > * {
+ visibility: collapse;
+}
+
#PersonalToolbar:not(:-moz-lwtheme):-moz-window-inactive,
#nav-bar:not(:-moz-lwtheme):-moz-window-inactive {
background-color: -moz-mac-chrome-inactive;
}
/* ----- BOOKMARK TOOLBAR ----- */
#nav-bar-customization-target > #wrapper-personal-bookmarks > #personal-bookmarks {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -238,16 +238,46 @@
}
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
/* Position the toolbar above the bottom of background tabs */
position: relative;
z-index: 1;
}
+#TabsToolbar.webext-collapsed > * {
+ visibility: collapse;
+}
+
+#TabsToolbar.webext-collapsed {
+ min-height: 31.5px;
+}
+
+@media (-moz-os-version: windows-win7) {
+ #TabsToolbar.webext-collapsed {
+ min-height: 20px;
+ }
+}
+
+@media (-moz-os-version: windows-win8) {
+ #TabsToolbar.webext-collapsed {
+ min-height: 22px;
+ }
+}
+
+@media (-moz-windows-classic) {
+ #TabsToolbar.webext-collapsed {
+ min-height: 16px;
+ }
+}
+
+#toolbar-menubar:not([inactive]) + #TabsToolbar {
+ min-height: 0 !important;
+}
+
#nav-bar {
border-top: 1px solid var(--tabs-border) !important;
}
@media (-moz-windows-compositor: 0) {
#TabsToolbar[collapsed="true"] + #nav-bar {
border-top-style: none !important;
}
}