summaryrefslogtreecommitdiffstats
path: root/application/basilisk/base/content/browser-tabsintitlebar.js
diff options
context:
space:
mode:
Diffstat (limited to 'application/basilisk/base/content/browser-tabsintitlebar.js')
-rw-r--r--application/basilisk/base/content/browser-tabsintitlebar.js310
1 files changed, 310 insertions, 0 deletions
diff --git a/application/basilisk/base/content/browser-tabsintitlebar.js b/application/basilisk/base/content/browser-tabsintitlebar.js
new file mode 100644
index 000000000..7f2e0a358
--- /dev/null
+++ b/application/basilisk/base/content/browser-tabsintitlebar.js
@@ -0,0 +1,310 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Note: the file browser-tabsintitlebar-stub.js is used instead of
+// this one on platforms which don't have CAN_DRAW_IN_TITLEBAR defined.
+
+var TabsInTitlebar = {
+ init() {
+ if (this._initialized) {
+ return;
+ }
+ this._readPref();
+ Services.prefs.addObserver(this._prefName, this, false);
+
+ // We need to update the appearance of the titlebar when the menu changes
+ // from the active to the inactive state. We can't, however, rely on
+ // DOMMenuBarInactive, because the menu fires this event and then removes
+ // the inactive attribute after an event-loop spin.
+ //
+ // Because updating the appearance involves sampling the heights and margins
+ // of various elements, it's important that the layout be more or less
+ // settled before updating the titlebar. So instead of listening to
+ // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
+ // watch the "invalid" attribute directly.
+ let menu = document.getElementById("toolbar-menubar");
+ this._menuObserver = new MutationObserver(this._onMenuMutate);
+ this._menuObserver.observe(menu, {attributes: true});
+
+ this.onAreaReset = function(aArea) {
+ if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
+ this._update(true);
+ };
+ this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
+ if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
+ this._update(true);
+ };
+ CustomizableUI.addListener(this);
+
+ addEventListener("resolutionchange", this, false);
+
+ this._initialized = true;
+ if (this._updateOnInit) {
+ // We don't need to call this with 'true', even if original calls
+ // (before init()) did, because this will be the first call and so
+ // we will update anyway.
+ this._update();
+ }
+ },
+
+ allowedBy(condition, allow) {
+ if (allow) {
+ if (condition in this._disallowed) {
+ delete this._disallowed[condition];
+ this._update(true);
+ }
+ } else if (!(condition in this._disallowed)) {
+ this._disallowed[condition] = null;
+ this._update(true);
+ }
+ },
+
+ updateAppearance: function updateAppearance(aForce) {
+ this._update(aForce);
+ },
+
+ get enabled() {
+ return document.documentElement.getAttribute("tabsintitlebar") == "true";
+ },
+
+ observe(subject, topic, data) {
+ if (topic == "nsPref:changed")
+ this._readPref();
+ },
+
+ handleEvent(aEvent) {
+ if (aEvent.type == "resolutionchange" && aEvent.target == window) {
+ this._update(true);
+ }
+ },
+
+ _onMenuMutate(aMutations) {
+ for (let mutation of aMutations) {
+ if (mutation.attributeName == "inactive" ||
+ mutation.attributeName == "autohide") {
+ TabsInTitlebar._update(true);
+ return;
+ }
+ }
+ },
+
+ _initialized: false,
+ _updateOnInit: false,
+ _disallowed: {},
+ _prefName: "browser.tabs.drawInTitlebar",
+ _lastSizeMode: null,
+
+ _readPref() {
+ this.allowedBy("pref",
+ Services.prefs.getBoolPref(this._prefName));
+ },
+
+ _update(aForce = false) {
+ let $ = id => document.getElementById(id);
+ let rect = ele => ele.getBoundingClientRect();
+ let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
+
+ if (window.fullScreen)
+ return;
+
+ // In some edgecases it is possible for this to fire before we've initialized.
+ // Don't run now, but don't forget to run it when we do initialize.
+ if (!this._initialized) {
+ this._updateOnInit = true;
+ return;
+ }
+
+ if (!aForce) {
+ // _update is called on resize events, because the window is not ready
+ // after sizemode events. However, we only care about the event when the
+ // sizemode is different from the last time we updated the appearance of
+ // the tabs in the titlebar.
+ let sizemode = document.documentElement.getAttribute("sizemode");
+ if (this._lastSizeMode == sizemode) {
+ return;
+ }
+ let oldSizeMode = this._lastSizeMode;
+ this._lastSizeMode = sizemode;
+ // Don't update right now if we are leaving fullscreen, since the UI is
+ // still changing in the consequent "fullscreen" event. Code there will
+ // call this function again when everything is ready.
+ // See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
+ if (oldSizeMode == "fullscreen") {
+ return;
+ }
+ }
+
+ let allowed = (Object.keys(this._disallowed)).length == 0;
+
+ let titlebar = $("titlebar");
+ let titlebarContent = $("titlebar-content");
+ let menubar = $("toolbar-menubar");
+
+ if (allowed) {
+ // We set the tabsintitlebar attribute first so that our CSS for
+ // tabsintitlebar manifests before we do our measurements.
+ document.documentElement.setAttribute("tabsintitlebar", "true");
+ updateTitlebarDisplay();
+
+ // Try to avoid reflows in this code by calculating dimensions first and
+ // then later set the properties affecting layout together in a batch.
+
+ // Get the full height of the tabs toolbar:
+ let tabsToolbar = $("TabsToolbar");
+ let tabsStyles = window.getComputedStyle(tabsToolbar);
+ let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
+ // Buttons first:
+ let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
+
+ let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
+#ifdef XP_MACOSX
+ secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
+ // No need to look up the menubar stuff on OS X:
+ menuHeight = 0;
+ fullMenuHeight = 0;
+#else
+ // Otherwise, get the height and margins separately for the menubar
+ menuHeight = rect(menubar).height;
+ menuStyles = window.getComputedStyle(menubar);
+ fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
+#endif
+
+ // And get the height of what's in the titlebar:
+ let titlebarContentHeight = rect(titlebarContent).height;
+
+ // Begin setting CSS properties which will cause a reflow
+
+ // If the menubar is around (menuHeight is non-zero), try to adjust
+ // its full height (i.e. including margins) to match the titlebar,
+ // by changing the menubar's bottom padding
+ if (menuHeight) {
+ // Calculate the difference between the titlebar's height and that of the menubar
+ let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
+ let paddingBottom;
+ // The titlebar is bigger:
+ if (menuTitlebarDelta > 0) {
+ fullMenuHeight += menuTitlebarDelta;
+ // If there is already padding on the menubar, we need to add that
+ // to the difference so the total padding is correct:
+ if ((paddingBottom = menuStyles.paddingBottom)) {
+ menuTitlebarDelta += parseFloat(paddingBottom);
+ }
+ menubar.style.paddingBottom = menuTitlebarDelta + "px";
+ // The menubar is bigger, but has bottom padding we can remove:
+ } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
+ let existingPadding = parseFloat(paddingBottom);
+ // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
+ let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
+ menubar.style.paddingBottom = desiredPadding + "px";
+ // We've changed the menu height now:
+ fullMenuHeight += desiredPadding - existingPadding;
+ }
+ }
+
+ // Next, we calculate how much we need to stretch the titlebar down to
+ // go all the way to the bottom of the tab strip, if necessary.
+ let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
+
+ if (tabAndMenuHeight > titlebarContentHeight) {
+ // We need to increase the titlebar content's outer height (ie including margins)
+ // to match the tab and menu height:
+ let extraMargin = tabAndMenuHeight - titlebarContentHeight;
+#ifndef XP_MACOSX
+ titlebarContent.style.marginBottom = extraMargin + "px";
+#endif
+
+ titlebarContentHeight += extraMargin;
+ } else {
+ titlebarContent.style.removeProperty("margin-bottom");
+ }
+
+ // Then add a negative margin to the titlebar, so that the following elements
+ // will overlap it by the lesser of the titlebar height or the tabstrip+menu.
+ let minTitlebarOrTabsHeight = Math.min(titlebarContentHeight, tabAndMenuHeight);
+ titlebar.style.marginBottom = "-" + minTitlebarOrTabsHeight + "px";
+
+ // Finally, size the placeholders:
+#ifdef XP_MACOSX
+ this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+#endif
+ this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
+
+ } else {
+ document.documentElement.removeAttribute("tabsintitlebar");
+ updateTitlebarDisplay();
+
+#ifdef XP_MACOSX
+ let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
+ this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+#endif
+
+ // Reset the margins and padding that might have been modified:
+ titlebarContent.style.marginTop = "";
+ titlebarContent.style.marginBottom = "";
+ titlebar.style.marginBottom = "";
+ menubar.style.paddingBottom = "";
+ }
+
+ ToolbarIconColor.inferFromText();
+ if (CustomizationHandler.isCustomizing()) {
+ gCustomizeMode.updateLWTStyling();
+ }
+ },
+
+ _sizePlaceholder(type, width) {
+ Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='" + type + "']"),
+ function(node) { node.width = width; });
+ },
+
+ uninit() {
+ this._initialized = false;
+ removeEventListener("resolutionchange", this);
+ Services.prefs.removeObserver(this._prefName, this);
+ this._menuObserver.disconnect();
+ CustomizableUI.removeListener(this);
+ }
+};
+
+function updateTitlebarDisplay() {
+#ifdef XP_MACOSX
+ // OS X and the other platforms differ enough to necessitate this kind of
+ // special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
+ // we draw in the OS X titlebar when putting the tabs up there. However, OS X
+ // also draws in the titlebar when a lightweight theme is applied, regardless
+ // of whether or not the tabs are drawn in the titlebar.
+ if (TabsInTitlebar.enabled) {
+ document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
+ document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
+ document.documentElement.removeAttribute("drawtitle");
+ }
+ else {
+ // We set chromemargin-nonlwtheme to "" instead of removing it as a way of
+ // making sure that LightweightThemeConsumer doesn't take it upon itself to
+ // detect this value again if and when we do a lwtheme state change.
+ document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
+ let isCustomizing = document.documentElement.hasAttribute("customizing");
+ let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
+ let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+ if ((!hasLWTheme || isCustomizing) && !isPrivate) {
+ document.documentElement.removeAttribute("chromemargin");
+ }
+ document.documentElement.setAttribute("drawtitle", "true");
+ }
+#else
+ if (TabsInTitlebar.enabled) {
+ // not OS X
+ document.documentElement.setAttribute("chromemargin", "0,2,2,2");
+ } else {
+ document.documentElement.removeAttribute("chromemargin");
+ }
+#endif
+}
+
+function onTitlebarMaxClick() {
+ if (window.windowState == window.STATE_MAXIMIZED)
+ window.restore();
+ else
+ window.maximize();
+}