Bug 1448303 - Refactor TabsInTitlebar initial update handling. r=florian
MozReview-Commit-ID: BQTe6cVyHo1
--- a/browser/base/content/browser-tabsintitlebar-stub.js
+++ b/browser/base/content/browser-tabsintitlebar-stub.js
@@ -3,13 +3,14 @@
* 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/. */
// This file is used as a stub object for platforms which
// don't have CAN_DRAW_IN_TITLEBAR defined.
var TabsInTitlebar = {
init() {},
+ whenWindowLayoutReady() {},
uninit() {},
allowedBy() {},
update() {},
enabled: false,
};
--- a/browser/base/content/browser-tabsintitlebar.js
+++ b/browser/base/content/browser-tabsintitlebar.js
@@ -40,17 +40,23 @@ var TabsInTitlebar = {
};
CustomizableUI.addListener(this);
window.addEventListener("resolutionchange", this);
window.addEventListener("resize", this);
gDragSpaceObserver.init();
- this.update(true);
+ this._initialized = true;
+ this.update();
+ },
+
+ whenWindowLayoutReady() {
+ this._windowLayoutReady = true;
+ this.update();
},
allowedBy(condition, allow) {
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
this.update();
}
@@ -97,185 +103,182 @@ var TabsInTitlebar = {
if (oldSizeMode == "fullscreen") {
break;
}
this.update();
break;
}
},
- onDOMContentLoaded() {
- this._domLoaded = true;
- this.update();
- },
-
_onMenuMutate(aMutations) {
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
mutation.attributeName == "autohide") {
TabsInTitlebar.update();
return;
}
}
},
+ _initialized: false,
+ _windowLayoutReady: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
- _domLoaded: false,
_readPref() {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
- update(aFromInit = false) {
- let $ = id => document.getElementById(id);
- let rect = ele => ele.getBoundingClientRect();
- let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
-
- if (window.fullScreen)
- return;
-
- // We want to do this from initialization anyway, so that the
- // "chromemargin" attributes are all correctly setup, but we don't want to
- // do this before the DOM loads again, to prevent spurious reflows that
- // will be superseded by the one on onDOMContentLoaded.
- if (!this._domLoaded && !aFromInit) {
+ update() {
+ if (!this._initialized || window.fullScreen) {
return;
}
let allowed = (Object.keys(this._disallowed)).length == 0;
+ if (allowed) {
+ document.documentElement.setAttribute("tabsintitlebar", "true");
+ } else {
+ document.documentElement.removeAttribute("tabsintitlebar");
+ }
+
+ updateTitlebarDisplay();
+
+ this._layOutTitlebar(allowed);
+
+ ToolbarIconColor.inferFromText("tabsintitlebar", allowed);
+ },
+
+ _layOutTitlebar(drawInTitlebar) {
+ if (!this._windowLayoutReady) {
+ return;
+ }
+
+ let $ = id => document.getElementById(id);
+ let rect = ele => ele.getBoundingClientRect();
+ let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
let titlebar = $("titlebar");
- let titlebarContent = $("titlebar-content");
- let titlebarButtons = $("titlebar-buttonbox");
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();
-
- // Reset the custom titlebar height if the menubar is shown,
- // because we will want to calculate its original height.
- let buttonsShouldMatchTabHeight =
- AppConstants.isPlatformAndVersionAtLeast("win", "10.0") ||
- AppConstants.platform == "linux";
- if (buttonsShouldMatchTabHeight &&
- (menubar.getAttribute("inactive") != "true" ||
- menubar.getAttribute("autohide") != "true")) {
- titlebarButtons.style.removeProperty("height");
- }
-
- // 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 height of the tabs toolbar:
- let fullTabsHeight = rect($("TabsToolbar")).height;
-
- // Buttons first:
- let captionButtonsBoxWidth = rect(titlebarButtons).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
-
- // Adjust the window controls to span the entire
- // tab strip height if we're not showing a menu bar.
- if (buttonsShouldMatchTabHeight && !menuHeight) {
- titlebarContentHeight = fullTabsHeight;
- titlebarButtons.style.height = titlebarContentHeight + "px";
- }
-
- // 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 add a negative margin to the titlebar, so that the following elements
- // will overlap it by the greater of the titlebar height or the tabstrip+menu.
- let maxTitlebarOrTabsHeight = Math.max(titlebarContentHeight, tabAndMenuHeight);
- titlebar.style.marginBottom = "-" + maxTitlebarOrTabsHeight + "px";
-
- // Finally, size the placeholders:
- if (AppConstants.platform == "macosx") {
- this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
- }
- this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
-
- } else {
- document.documentElement.removeAttribute("tabsintitlebar");
- updateTitlebarDisplay();
-
+ if (!drawInTitlebar) {
if (AppConstants.platform == "macosx") {
let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
// Reset styles that might have been modified:
titlebar.style.marginBottom = "";
menubar.style.paddingBottom = "";
+ return;
+ }
+
+ let titlebarContent = $("titlebar-content");
+ let titlebarButtons = $("titlebar-buttonbox");
+
+ // Reset the custom titlebar height if the menubar is shown,
+ // because we will want to calculate its original height.
+ let buttonsShouldMatchTabHeight =
+ AppConstants.isPlatformAndVersionAtLeast("win", "10.0") ||
+ AppConstants.platform == "linux";
+ if (buttonsShouldMatchTabHeight &&
+ (menubar.getAttribute("inactive") != "true" ||
+ menubar.getAttribute("autohide") != "true")) {
+ titlebarButtons.style.removeProperty("height");
+ }
+
+ // 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 height of the tabs toolbar:
+ let fullTabsHeight = rect($("TabsToolbar")).height;
+
+ // Buttons first:
+ let captionButtonsBoxWidth = rect(titlebarButtons).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
+
+ // Adjust the window controls to span the entire
+ // tab strip height if we're not showing a menu bar.
+ if (buttonsShouldMatchTabHeight && !menuHeight) {
+ titlebarContentHeight = fullTabsHeight;
+ titlebarButtons.style.height = titlebarContentHeight + "px";
}
- ToolbarIconColor.inferFromText("tabsintitlebar", TabsInTitlebar.enabled);
+ // 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 add a negative margin to the titlebar, so that the following elements
+ // will overlap it by the greater of the titlebar height or the tabstrip+menu.
+ let maxTitlebarOrTabsHeight = Math.max(titlebarContentHeight, tabAndMenuHeight);
+ titlebar.style.marginBottom = "-" + maxTitlebarOrTabsHeight + "px";
+
+ // Finally, size the placeholders:
+ if (AppConstants.platform == "macosx") {
+ this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+ }
+ this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
},
_sizePlaceholder(type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='" + type + "']"),
function(node) { node.style.width = width + "px"; });
},
uninit() {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1198,21 +1198,22 @@ var gBrowserInit = {
document.documentElement.setAttribute("width", width);
document.documentElement.setAttribute("height", height);
if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
document.documentElement.setAttribute("sizemode", "maximized");
}
}
+ // Update the chromemargin attribute so the window can be sized correctly.
+ window.TabBarVisibility.update();
+ TabsInTitlebar.init();
+
new LightweightThemeConsumer(document);
CompactTheme.init();
-
- TabsInTitlebar.init();
-
if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
window.matchMedia("(-moz-windows-default-theme)").matches) {
let windowFrameColor = new Color(...ChromeUtils.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
.Windows8WindowFrameColor.get());
// Default to black for foreground text.
if (!windowFrameColor.isContrastRatioAcceptable(new Color(0, 0, 0))) {
document.documentElement.setAttribute("darkwindowframe", "true");
}
@@ -1262,32 +1263,31 @@ var gBrowserInit = {
}
initBrowser.removeAttribute("blank");
}
gBrowser.updateBrowserRemoteness(initBrowser, isRemote, {
remoteType, sameProcessAsFrameLoader
});
- gUIDensity.init();
-
// Hack to ensure that the about:home favicon is loaded
// instantaneously, to avoid flickering and improve perceived performance.
this._callWithURIToLoad(uriToLoad => {
if (uriToLoad == "about:home") {
gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
} else if (uriToLoad == "about:privatebrowsing") {
gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
}
});
this._setInitialFocus();
- window.TabBarVisibility.update();
- TabsInTitlebar.onDOMContentLoaded();
+ // Update the UI density before TabsInTitlebar lays out the titlbar.
+ gUIDensity.init();
+ TabsInTitlebar.whenWindowLayoutReady();
},
onLoad() {
gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver);
Services.obs.addObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
window.addEventListener("AppCommand", HandleAppCommandEvent, true);
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -4706,17 +4706,18 @@ var StatusPanel = {
};
var TabBarVisibility = {
_initialUpdateDone: false,
update() {
let toolbar = document.getElementById("TabsToolbar");
let collapse = false;
- if (gBrowser.tabs.length - gBrowser._removingTabs.length == 1) {
+ if (!gBrowser /* gBrowser isn't initialized yet */ ||
+ gBrowser.tabs.length - gBrowser._removingTabs.length == 1) {
collapse = !window.toolbar.visible;
}
if (collapse == toolbar.collapsed && this._initialUpdateDone) {
return;
}
this._initialUpdateDone = true;
--- a/browser/base/content/test/performance/browser_windowopen.js
+++ b/browser/base/content/test/performance/browser_windowopen.js
@@ -18,33 +18,33 @@ const EXPECTED_REFLOWS = [
*/
];
if (Services.appinfo.OS == "WINNT") {
EXPECTED_REFLOWS.push(
{
stack: [
"verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
+ "_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
- "onDOMContentLoaded@chrome://browser/content/browser-tabsintitlebar.js",
- "onDOMContentLoaded@chrome://browser/content/browser.js",
+ "whenWindowLayoutReady@chrome://browser/content/browser-tabsintitlebar.js",
],
maxCount: 2, // This number should only ever go down - never up.
},
);
}
if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
EXPECTED_REFLOWS.push(
{
stack: [
"rect@chrome://browser/content/browser-tabsintitlebar.js",
+ "_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
- "onDOMContentLoaded@chrome://browser/content/browser-tabsintitlebar.js",
- "onDOMContentLoaded@chrome://browser/content/browser.js",
+ "whenWindowLayoutReady@chrome://browser/content/browser-tabsintitlebar.js",
],
// These numbers should only ever go down - never up.
maxCount: Services.appinfo.OS == "WINNT" ? 5 : 4,
},
);
}
/*
@@ -117,24 +117,26 @@ add_task(async function() {
if (Services.appinfo.OS == "WINNT" && win.windowState == win.STATE_MAXIMIZED) {
// The reflows below are triggered by maximizing the window after
// layout. They should be fixed by bug 1447864.
EXPECTED_REFLOWS.push(
{
stack: [
"rect@chrome://browser/content/browser-tabsintitlebar.js",
+ "_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"handleEvent@chrome://browser/content/browser-tabsintitlebar.js",
],
maxCount: 4,
},
{
stack: [
"verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
+ "_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"handleEvent@chrome://browser/content/browser-tabsintitlebar.js",
],
maxCount: 2,
},
);
}