tenfourfox/browser/base/content/browser-tabsintitlebar.js

303 lines
12 KiB
JavaScript

/* -*- 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: function () {
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);
this._initialized = true;
},
allowedBy: function (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: function (subject, topic, data) {
if (topic == "nsPref:changed")
this._readPref();
},
_onMenuMutate: function (aMutations) {
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
mutation.attributeName == "autohide") {
TabsInTitlebar._update(true);
return;
}
}
},
_initialized: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
_readPref: function () {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
_update: function (aForce=false) {
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (!this._initialized || window.fullScreen)
return;
let allowed = true;
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;
}
}
for (let something in this._disallowed) {
allowed = false;
break;
}
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;
if (AppConstants.platform == "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;
}
// 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;
if (AppConstants.platform != "macosx") {
titlebarContent.style.marginBottom = extraMargin + "px";
}
titlebarContentHeight += extraMargin;
} else {
titlebarContent.style.removeProperty("margin-bottom");
}
// Then we bring up the titlebar by the same amount, but we add any negative margin:
titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
// Finally, size the placeholders:
if (AppConstants.platform == "macosx") {
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
if (!this._draghandles) {
this._draghandles = {};
let tmp = {};
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
let mouseDownCheck = function () {
return !this._dragBindingAlive && TabsInTitlebar.enabled;
};
this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
}
} else {
document.documentElement.removeAttribute("tabsintitlebar");
updateTitlebarDisplay();
if (AppConstants.platform == "macosx") {
let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
// 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: function (type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
function (node) { node.width = width; });
},
uninit: function () {
this._initialized = false;
Services.prefs.removeObserver(this._prefName, this);
this._menuObserver.disconnect();
CustomizableUI.removeListener(this);
}
};
function updateTitlebarDisplay() {
if (AppConstants.platform == "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 { // not OS X
if (TabsInTitlebar.enabled)
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
else
document.documentElement.removeAttribute("chromemargin");
}
}
function onTitlebarMaxClick() {
if (window.windowState == window.STATE_MAXIMIZED)
window.restore();
else
window.maximize();
}