Bug 572160 - Put tabs in the title bar when the window is maximized. r=gavin ui-r=faaborg a=b
authorDão Gottwald <dao@mozilla.com>
Fri, 07 Jan 2011 21:28:02 +0100
changeset 60222 019ae3c92eb4fac495920cc8fe3fa667f114825e
parent 60221 8ca24ad994a264ddcf2f3bf3c74bc21f26e14421
child 60223 dc0cff820b221e5bc517d77a2372800a0236144b
push id17884
push userdgottwald@mozilla.com
push dateFri, 07 Jan 2011 20:35:40 +0000
treeherdermozilla-central@019ae3c92eb4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin, faaborg, b
bugs572160
milestone2.0b9pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 572160 - Put tabs in the title bar when the window is maximized. r=gavin ui-r=faaborg a=b
browser/app/profile/firefox.js
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/tabbrowser.xml
browser/base/content/tabview/ui.js
browser/themes/winstripe/browser/browser.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -376,16 +376,17 @@ pref("browser.tabs.warnOnClose", true);
 pref("browser.tabs.warnOnOpen", true);
 pref("browser.tabs.maxOpenBeforeWarn", 15);
 pref("browser.tabs.loadInBackground", true);
 pref("browser.tabs.opentabfor.middleclick", true);
 pref("browser.tabs.loadDivertedInBackground", false);
 pref("browser.tabs.loadBookmarksInBackground", false);
 pref("browser.tabs.tabClipWidth", 140);
 pref("browser.tabs.animate", true);
+pref("browser.tabs.drawInTitlebar", true);
 
 // Where to show tab close buttons:
 // 0  on active tab only
 // 1  on all tabs until tabClipWidth is reached, then active tab only
 // 2  no close buttons at all
 // 3  at the end of the tabstrip
 pref("browser.tabs.closeButtons", 1);
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -82,23 +82,44 @@ toolbar[printpreview="true"] {
   -moz-box-ordinal-group: 100;
 }
 
 #TabsToolbar[tabsontop="true"] {
   -moz-box-ordinal-group: 10;
 }
 
 %ifdef CAN_DRAW_IN_TITLEBAR
-#main-window[inFullscreen] > #titlebar {
+#main-window[inFullscreen] > #titlebar,
+#main-window[inFullscreen] .titlebar-placeholder,
+#main-window:not([tabsintitlebar]) .titlebar-placeholder {
   display: none;
 }
 
 #titlebar {
   -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
 }
+
+#main-window[tabsintitlebar] #TabsToolbar {
+  -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbar-drag");
+}
+
+#titlebar-spacer,
+#main-window[tabsintitlebar]:not([inFullscreen]) .tabbrowser-arrowscrollbox > scrollbox > .scrollbox-innerbox {
+  pointer-events: none;
+}
+
+.tabbrowser-tab,
+.tabs-newtab-button {
+  pointer-events: auto;
+}
+
+#main-window[tabsintitlebar] #appmenu-button-container,
+#main-window[tabsintitlebar] #titlebar-buttonbox {
+  position: relative;
+}
 %endif
 
 toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] {
   display: none;
 }
 
 #main-window[disablechrome] #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
   visibility: collapse;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1281,17 +1281,18 @@ function BrowserStartup() {
   CombinedStopReload.init();
 
   allTabs.readPref();
 
   TabsOnTop.syncCommand();
 
   BookmarksMenuButton.init();
 
-  // initialize the private browsing UI
+  TabsInTitlebar.init();
+
   gPrivateBrowsingUI.init();
 
   setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
 }
 
 function HandleAppCommandEvent(evt) {
   evt.stopPropagation();
   switch (evt.command) {
@@ -1672,16 +1673,17 @@ function BrowserShutdown()
   }
 
   TabView.uninit();
   BrowserOffline.uninit();
   OfflineApps.uninit();
   gPrivateBrowsingUI.uninit();
   IndexedDBPromptHelper.uninit();
   AddonManager.removeAddonListener(AddonsMgrListener);
+  TabsInTitlebar.uninit();
 
   var enumerator = Services.wm.getEnumerator(null);
   enumerator.getNext();
   if (!enumerator.hasMoreElements()) {
     document.persist("sidebar-box", "sidebarcommand");
     document.persist("sidebar-box", "width");
     document.persist("sidebar-box", "src");
     document.persist("sidebar-title", "value");
@@ -2742,29 +2744,29 @@ var PrintPreviewListener = {
     gBrowser.selectedTab = this._tabBeforePrintPreview;
     this._tabBeforePrintPreview = null;
     gInPrintPreviewMode = false;
     this._toggleAffectedChrome();
     gBrowser.removeTab(this._printPreviewTab);
     this._printPreviewTab = null;
   },
   _toggleAffectedChrome: function () {
-#ifdef MENUBAR_CAN_AUTOHIDE
-    updateAppButtonDisplay();
-#endif
-
     gNavToolbox.hidden = gInPrintPreviewMode;
 
     if (gInPrintPreviewMode)
       this._hideChrome();
     else
       this._showChrome();
 
     if (this._chromeState.sidebarOpen)
       toggleSidebar(this._sidebarCommand);
+
+#ifdef MENUBAR_CAN_AUTOHIDE
+    updateAppButtonDisplay();
+#endif
   },
   _hideChrome: function () {
     this._chromeState = {};
 
     var sidebar = document.getElementById("sidebar-box");
     this._chromeState.sidebarOpen = !sidebar.hidden;
     this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
 
@@ -3454,16 +3456,18 @@ function BrowserCustomizeToolbar()
   if (splitter)
     splitter.parentNode.removeChild(splitter);
 
   CombinedStopReload.uninit();
 
   PlacesToolbarHelper.customizeStart();
   BookmarksMenuButton.customizeStart();
 
+  TabsInTitlebar.allowedBy("customizing-toolbars", false);
+
   var customizeURL = "chrome://global/content/customizeToolbar.xul";
   gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
 
   if (gCustomizeSheet) {
     var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
     var panel = document.getElementById("customizeToolbarSheetPopup");
     sheetFrame.hidden = false;
     sheetFrame.toolbox = gNavToolbox;
@@ -3524,16 +3528,18 @@ function BrowserToolboxCustomizeDone(aTo
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
     PlacesStarButton.updateState();
   }
 
+  TabsInTitlebar.allowedBy("customizing-toolbars", true);
+
   // Re-enable parts of the UI we disabled during the dialog
   var menubar = document.getElementById("main-menubar");
   for (var i = 0; i < menubar.childNodes.length; ++i)
     menubar.childNodes[i].setAttribute("disabled", false);
   var cmd = document.getElementById("cmd_CustomizeToolbars");
   cmd.removeAttribute("disabled");
 
   // make sure to re-enable click-and-hold
@@ -4756,42 +4762,154 @@ var TabsOnTop = {
   },
   syncCommand: function () {
     let enabled = this.enabled;
     document.getElementById("cmd_ToggleTabsOnTop")
             .setAttribute("checked", enabled);
     document.documentElement.setAttribute("tabsontop", enabled);
     document.getElementById("TabsToolbar").setAttribute("tabsontop", enabled);
     gBrowser.tabContainer.setAttribute("tabsontop", enabled);
+    TabsInTitlebar.allowedBy("tabs-on-top", enabled);
   },
   get enabled () {
     return gNavToolbox.getAttribute("tabsontop") == "true";
   },
   set enabled (val) {
     gNavToolbox.setAttribute("tabsontop", !!val);
     this.syncCommand();
 
     return val;
   }
 }
 
+var TabsInTitlebar = {
+  init: function () {
+#ifdef CAN_DRAW_IN_TITLEBAR
+    this._readPref();
+    Services.prefs.addObserver(this._prefName, this, false);
+
+    // Don't trust the initial value of the sizemode attribute; wait for the resize event.
+    this.allowedBy("sizemode", false);
+    window.addEventListener("resize", function (event) {
+      if (event.target != window)
+        return;
+      let sizemode = document.documentElement.getAttribute("sizemode");
+      TabsInTitlebar.allowedBy("sizemode",
+                               sizemode == "maximized" || sizemode == "fullscreen");
+    }, false);
+
+    this._initialized = true;
+#endif
+  },
+
+  allowedBy: function (condition, allow) {
+#ifdef CAN_DRAW_IN_TITLEBAR
+    if (allow) {
+      if (condition in this._disallowed) {
+        delete this._disallowed[condition];
+        this._update();
+      }
+    } else {
+      if (!(condition in this._disallowed)) {
+        this._disallowed[condition] = null;
+        this._update();
+      }
+    }
+#endif
+  },
+
+#ifdef CAN_DRAW_IN_TITLEBAR
+  observe: function (subject, topic, data) {
+    if (topic == "nsPref:changed")
+      this._readPref();
+  },
+
+  _initialized: false,
+  _disallowed: {},
+  _prefName: "browser.tabs.drawInTitlebar",
+
+  _readPref: function () {
+    this.allowedBy("pref",
+                   Services.prefs.getBoolPref(this._prefName));
+  },
+
+  _update: function () {
+    if (!this._initialized)
+      return;
+
+    let allowed = true;
+    for (let something in this._disallowed) {
+      allowed = false;
+      break;
+    }
+
+    let docElement = document.documentElement;
+    if (allowed == (docElement.getAttribute("tabsintitlebar") == "true"))
+      return;
+
+    function $(id) document.getElementById(id);
+    let titlebar = $("titlebar");
+
+    if (allowed) {
+      let availTop = screen.availTop;
+      function top(ele)    ele.boxObject.screenY - availTop;
+      function bottom(ele) top(ele) + rect(ele).height;
+      function rect(ele)   ele.getBoundingClientRect();
+
+      let tabsToolbar       = $("TabsToolbar");
+      let appmenuButtonBox  = $("appmenu-button-container");
+      let captionButtonsBox = $("titlebar-buttonbox");
+
+      this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
+      this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
+
+      let maxMargin = top(gNavToolbox);
+      let tabsBottom = maxMargin + rect(tabsToolbar).height;
+      let titlebarBottom = Math.max(bottom(appmenuButtonBox), bottom(captionButtonsBox));
+      let distance = tabsBottom - titlebarBottom;
+      titlebar.style.marginBottom = - Math.min(distance, maxMargin) + "px";
+
+      docElement.setAttribute("tabsintitlebar", "true");
+    } else {
+      docElement.removeAttribute("tabsintitlebar");
+
+      titlebar.style.marginBottom = "";
+    }
+  },
+
+  _sizePlaceholder: function (type, width) {
+    Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
+                  function (node) { node.width = width; });
+  },
+#endif
+
+  uninit: function () {
+#ifdef CAN_DRAW_IN_TITLEBAR
+    this._initialized = false;
+    Services.prefs.removeObserver(this._prefName, this);
+#endif
+  }
+};
+
 #ifdef MENUBAR_CAN_AUTOHIDE
 function updateAppButtonDisplay() {
   var displayAppButton =
     !gInPrintPreviewMode &&
     window.menubar.visible &&
     document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
 
 #ifdef CAN_DRAW_IN_TITLEBAR
   document.getElementById("titlebar").hidden = !displayAppButton;
 
   if (displayAppButton)
     document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
   else
     document.documentElement.removeAttribute("chromemargin");
+
+  TabsInTitlebar.allowedBy("drawing-in-titlebar", displayAppButton);
 #else
   document.getElementById("appmenu-toolbar-button").hidden =
     !displayAppButton;
 #endif
 }
 #endif
 
 #ifdef CAN_DRAW_IN_TITLEBAR
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -480,16 +480,21 @@
              accesskey="&menubarCmd.accesskey;"
 #endif
              context="toolbar-context-menu">
       <toolbaritem id="menubar-items" align="center">
 # The entire main menubar is placed into browser-menubar.inc, so that it can be shared by
 # hiddenWindow.xul.
 #include browser-menubar.inc
       </toolbaritem>
+
+#ifdef CAN_DRAW_IN_TITLEBAR
+      <hbox class="titlebar-placeholder" type="appmenu-button" ordinal="0"/>
+      <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
+#endif
     </toolbar>
 
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
              fullscreentoolbar="true" mode="icons" customizable="true"
 #ifdef WINCE
              iconsize="small" defaulticonsize="small"
              defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,home-button,bookmarks-menu-button-container,navigator-throbber,fullscreenflex,window-controls"
@@ -845,16 +850,20 @@
                      observes="tabviewGroupsNumber"/>
 
       <toolbarbutton id="tabs-closebutton"
                      class="close-button tabs-closebutton"
                      command="cmd_close"
                      label="&closeTab.label;"
                      tooltiptext="&closeTab.label;"/>
 
+#ifdef CAN_DRAW_IN_TITLEBAR
+      <hbox class="titlebar-placeholder" type="appmenu-button" ordinal="0"/>
+      <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
+#endif
     </toolbar>
 
     <toolbarpalette id="BrowserToolbarPalette">
 
 # Update primaryToolbarButtons in browser/themes/browserShared.inc when adding
 # or removing default items with the toolbarbutton-1 class.
 
       <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2633,16 +2633,19 @@
                            this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"));
           tab.setAttribute("crop", "end");
           tab.setAttribute("validate", "never");
           tab.setAttribute("onerror", "this.removeAttribute('image');");
           this.adjustTabstrip();
 
           Services.prefs.addObserver("browser.tabs.", this._prefObserver, false);
           window.addEventListener("resize", this, false);
+
+          if (window.TabsInTitlebar)
+            TabsInTitlebar.allowedBy("tabs-visible", this.visible);
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           Services.prefs.removeObserver("browser.tabs.", this._prefObserver);
         ]]>
       </destructor>
@@ -2695,25 +2698,31 @@
 
       <field name="_container" readonly="true"><![CDATA[
         this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this;
       ]]></field>
 
       <property name="visible"
                 onget="return !this._container.collapsed;">
         <setter><![CDATA[
+          if (val == this.visible)
+            return val;
+
           this._container.collapsed = !val;
 
           if (val)
             this.tabbrowser.enterTabbedMode();
 
           document.getElementById("menu_closeWindow").hidden = !val;
           document.getElementById("menu_close").setAttribute("label",
             this.tabbrowser.mStringBundle.getString(val ? "tabs.closeTab" : "tabs.close"));
 
+          if (window.TabsInTitlebar)
+            TabsInTitlebar.allowedBy("tabs-visible", val);
+
           return val;
         ]]></setter>
       </property>
 
       <method name="updateVisibility">
         <body><![CDATA[
           if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1 &&
               window.toolbar.visible)
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -413,16 +413,17 @@ let UI = {
     });
     this._reorderTabItemsOnShow = [];
 
 #ifdef XP_WIN
     // Restore the full height when showing TabView
     gTabViewFrame.style.marginTop = "";
 #endif
     gTabViewDeck.selectedIndex = 1;
+    gWindow.TabsInTitlebar.allowedBy("tabview-open", false);
     gTabViewFrame.contentWindow.focus();
 
     gBrowser.updateTitlebar();
 #ifdef XP_MACOSX
     this._setActiveTitleColor(true);
 #endif
     let event = document.createEvent("Events");
     event.initEvent("tabviewshown", true, false);
@@ -479,16 +480,17 @@ let UI = {
 
 #ifdef XP_WIN
     // Push the top of TabView frame to behind the tabbrowser, so glass can show
     // XXX bug 586679: avoid shrinking the iframe and squishing iframe contents
     // as well as avoiding the flash of black as we animate out
     gTabViewFrame.style.marginTop = gBrowser.boxObject.y + "px";
 #endif
     gTabViewDeck.selectedIndex = 0;
+    gWindow.TabsInTitlebar.allowedBy("tabview-open", true);
     gBrowser.contentWindow.focus();
 
     gBrowser.updateTitlebar();
 #ifdef XP_MACOSX
     this._setActiveTitleColor(false);
 #endif
     let event = document.createEvent("Events");
     event.initEvent("tabviewhidden", true, false);
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -69,16 +69,41 @@
 }
 
 #navigator-toolbox > toolbar:not(:-moz-lwtheme) {
   -moz-appearance: none;
   border-style: none;
   background-color: -moz-Dialog;
 }
 
+%ifdef WINSTRIPE_AERO
+@media not all and (-moz-windows-compositor) {
+%endif
+  #main-window[tabsintitlebar] #titlebar-content:not(:-moz-lwtheme),
+  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme) {
+    background-color: ActiveCaption;
+    color: CaptionText;
+  }
+  #main-window[tabsintitlebar] #titlebar-content:not(:-moz-lwtheme):-moz-window-inactive,
+  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive {
+    background-color: InactiveCaption;
+    color: InactiveCaptionText;
+  }
+
+  #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
+    visibility: hidden;
+  }
+  #main-window[tabsintitlebar] #titlebar-content:-moz-lwtheme {
+    -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
+    visibility: visible;
+  }
+%ifdef WINSTRIPE_AERO
+}
+%endif
+
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar)[iconsize="small"],
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar)[defaulticonsize="small"]:not([iconsize]) {
   padding-top: 1px;
   padding-bottom: 1px;
 }
 
 #nav-bar:not(:-moz-lwtheme),
 #nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme),
@@ -371,16 +396,24 @@
 #titlebar-buttonbox {
   -moz-appearance: -moz-window-button-box;
 }
 
 #main-window[sizemode="maximized"] #titlebar-buttonbox {
   -moz-appearance: -moz-window-button-box-maximized;
 }
 
+.titlebar-placeholder[type="appmenu-button"] {
+  margin-left: 4px;
+}
+
+.titlebar-placeholder[type="caption-buttons"] {
+  margin-left: 10px;
+}
+
 /* titlebar command buttons */
 
 #titlebar-min {
   -moz-appearance: -moz-window-button-minimize;
 }
 
 #titlebar-max {
   -moz-appearance: -moz-window-button-maximize;