mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-11-20 10:33:36 +00:00
303 lines
12 KiB
JavaScript
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();
|
|
}
|