Bug 1332447 Part 2 - Implementation of the per-browser tabstrip hiding API; r?bwinton draft
authorManish Goregaokar <manishearth@gmail.com>
Wed, 11 Oct 2017 17:07:12 -0400
changeset 678971 9d1fd423c467fed84914f6f5005caca5ef934bbe
parent 678970 997e5972386a19189fb429aa2a46a8f87d9a0830
child 678972 52ab8259affc606d0b3cd2c619fff647f0bf5af8
push id84092
push userbmo:manishearth@gmail.com
push dateThu, 12 Oct 2017 06:22:32 +0000
reviewersbwinton
bugs1332447
milestone58.0a1
Bug 1332447 Part 2 - Implementation of the per-browser tabstrip hiding API; r?bwinton MozReview-Commit-ID: 86GKiqCYUvS
browser/components/extensions/ext-browser.js
browser/components/extensions/ext-tabs.js
browser/components/extensions/ext-windows.js
browser/components/extensions/schemas/tabs.json
browser/components/extensions/schemas/windows.json
browser/locales/en-US/chrome/browser/browser.properties
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -719,17 +719,79 @@ class Tab extends TabBase {
         result.favIconUrl = tabData.image;
       }
     }
 
     return result;
   }
 }
 
+
+class TabVisibilityManager {
+  constructor(win) {
+    this.window = win;
+    this.tabHidingContexts = new Set();
+  }
+
+  get tabsVisibility() {
+    return this.tabHidingContexts.size == 0;
+  }
+
+  set tabsVisibility(isVisible) {
+    this._updateTabsVisibility(isVisible);
+    let id = windowTracker.getId(this.window.window);
+  }
+
+
+  _updateTabsVisibility(isVisible) {
+    let toolbar = this.window.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.tabHidingContexts.delete(context);
+    if (this.tabHidingContexts.size == 0) {
+      this.tabsVisibility = true;
+    }
+  }
+
+  setTabsVisibility(context, visible) {
+    if (!visible) {
+      if (this.tabHidingContexts.has(context)) {
+        return;
+      }
+      let wasVisible = this.tabsVisibility;
+      this.tabHidingContexts.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.tabHidingContexts) {
+        context.forgetOnClose(this);
+      }
+      this.tabHidingContexts.clear();
+      this.tabsVisibility = true;
+    }
+  }
+};
+
 class Window extends WindowBase {
+  constructor(extension, window, id) {
+    super(extension, window, id);
+    this.tabVisibilityManager = new TabVisibilityManager(this);
+  }
+
   /**
    * Update the geometry of the browser window.
    *
    * @param {Object} options
    *        An object containing new values for the window's geometry.
    * @param {integer} [options.left]
    *        The new pixel distance of the left side of the browser window from
    *        the left of the screen.
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -932,14 +932,14 @@ this.tabs = class extends ExtensionAPI {
         async toggleReaderMode(tabId) {
           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");
-        },
+        }
       },
     };
     return self;
   }
 };
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -235,12 +235,22 @@ this.windows = class extends ExtensionAP
           return new Promise(resolve => {
             let listener = () => {
               windowTracker.removeListener("domwindowclosed", listener);
               resolve();
             };
             windowTracker.addListener("domwindowclosed", listener);
           });
         },
+
+        getTabsVisibility(windowId) {
+          let win = windowManager.get(windowId, context);
+          return Promise.resolve(win.tabVisibilityManager.tabsVisibility);
+        },
+
+        setTabsVisibility(windowId, visible) {
+          let win = windowManager.get(windowId, context);
+          win.tabVisibilityManager.setTabsVisibility(context, visible);
+        },
       },
     };
   }
 };
--- 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.",
--- a/browser/components/extensions/schemas/windows.json
+++ b/browser/components/extensions/schemas/windows.json
@@ -448,16 +448,56 @@
           },
           {
             "type": "function",
             "name": "callback",
             "optional": true,
             "parameters": []
           }
         ]
+      },
+      {
+        "name": "getTabsVisibility",
+        "type": "function",
+        "async": "callback",
+        "permissions": ["tabsVisibility"],
+        "description": "Query the visibility of the tabstrip.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "windowId",
+            "minimum": 0
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "description": "Called with the window's current tab visibility state when fetched",
+            "parameters": [
+              {
+                "type": "boolean",
+                "name": "hidden",
+                "description": "Whether or not the tab strip is currently hidden"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "setTabsVisibility",
+        "type": "function",
+        "permissions": ["tabsVisibility"],
+        "description": "Sets the visibility of the tabstrip.",
+        "parameters": [{
+          "type": "integer",
+          "name": "windowId",
+          "minimum": 0
+        },
+        {
+          "type": "boolean"
+        }]
       }
     ],
     "events": [
       {
         "name": "onCreated",
         "type": "function",
         "description": "Fired when a window is created.",
         "filters": [
@@ -512,12 +552,12 @@
         "parameters": [
           {
             "type": "integer",
             "name": "windowId",
             "minimum": -1,
             "description": "ID of the newly focused window."
           }
         ]
-      }
+       }
     ]
   }
 ]
--- 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
@@ -253,16 +253,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;
   }
 }