Bug 1389550 - Remove sync reflow during about:preferences loading and schedule them with animationend event, r=jaws, r=florian
To remove the sync reflow during page load, the code that reads the layout
would need to take place after the very first paint. Here the first paint is
detected by listening to an animationend event triggered by a no-op CSS animation.
Flash of misplaced content is avoid by keeping these elements visually hidden until
the code puts them into the right place.
MozReview-Commit-ID: 9MrVhcnnPG
--- a/browser/components/preferences/in-content-new/findInPage.js
+++ b/browser/components/preferences/in-content-new/findInPage.js
@@ -16,17 +16,17 @@ var gSearchResultsPane = {
}
this.inited = true;
this.searchInput = document.getElementById("searchInput");
this.searchInput.hidden = !Services.prefs.getBoolPref("browser.preferences.search");
if (!this.searchInput.hidden) {
this.searchInput.addEventListener("input", this);
this.searchInput.addEventListener("command", this);
window.addEventListener("DOMContentLoaded", () => {
- this.searchInput.focus();
+ first_paint(() => this.searchInput.focus());
});
// Initialize other panes in an idle callback.
window.requestIdleCallback(() => this.initializeCategories());
}
let strings = this.strings;
this.searchInput.placeholder = AppConstants.platform == "win" ?
strings.getString("searchInput.labelWin") :
strings.getString("searchInput.labelUnix");
--- a/browser/components/preferences/in-content-new/preferences.js
+++ b/browser/components/preferences/in-content-new/preferences.js
@@ -44,16 +44,24 @@ function register_module(categoryName, c
categoryObject.init();
this.inited = true;
}
});
}
document.addEventListener("DOMContentLoaded", init_all, {once: true});
+function first_paint(callback) {
+ window.addEventListener("animationend", callback, { once: true });
+}
+
+first_paint(() => {
+ first_paint = (callback) => callback();
+});
+
function init_all() {
document.documentElement.instantApply = true;
gSubDialog.init();
register_module("paneGeneral", gMainPane);
register_module("paneSearch", gSearchPane);
register_module("panePrivacy", gPrivacyPane);
register_module("paneContainers", gContainersPane);
@@ -71,17 +79,17 @@ function init_all() {
});
categories.addEventListener("mousedown", function() {
this.removeAttribute("keyboard-navigation");
});
window.addEventListener("hashchange", onHashChange);
gotoPref();
- init_dynamic_padding();
+ first_paint(init_dynamic_padding);
var initFinished = new CustomEvent("Initialized", {
"bubbles": true,
"cancelable": true
});
document.dispatchEvent(initFinished);
let helpButton = document.querySelector(".help-button");
@@ -119,16 +127,19 @@ function init_dynamic_padding() {
bottom: ${reducedHelpButtonBottom / 2}px;
}
}
`;
let mediaStyle = document.createElementNS("http://www.w3.org/1999/xhtml", "html:style");
mediaStyle.setAttribute("type", "text/css");
mediaStyle.appendChild(document.createCDATASection(mediaRule));
document.documentElement.appendChild(mediaStyle);
+
+ categories.classList.remove("visually-hidden");
+ helpButton.classList.remove("visually-hidden");
}
function telemetryBucketForCategory(category) {
category = category.toLowerCase();
switch (category) {
case "containers":
case "general":
case "privacy":
@@ -204,18 +215,20 @@ function gotoPref(aCategory) {
if (item) {
categories.selectedItem = item;
} else {
categories.clearSelection();
}
window.history.replaceState(category, document.title);
search(category, "data-category", subcategory, "data-subcategory");
- let mainContent = document.querySelector(".main-content");
- mainContent.scrollTop = 0;
+ first_paint(() => {
+ let mainContent = document.querySelector(".main-content");
+ mainContent.scrollTop = 0;
+ });
Services.telemetry
.getHistogramById("FX_PREFERENCES_CATEGORY_OPENED_V2")
.add(telemetryBucketForCategory(friendlyName));
}
function search(aQuery, aAttribute, aSubquery, aSubAttribute) {
let mainPrefPane = document.getElementById("mainPrefPane");
--- a/browser/components/preferences/in-content-new/preferences.xul
+++ b/browser/components/preferences/in-content-new/preferences.xul
@@ -118,17 +118,17 @@
<stringbundle id="appManagerBundle"
src="chrome://browser/locale/preferences/applicationManager.properties"/>
</stringbundleset>
<stack flex="1">
<hbox flex="1">
<!-- category list -->
- <richlistbox id="categories">
+ <richlistbox id="categories" class="visually-hidden">
<richlistitem id="category-general"
class="category"
value="paneGeneral"
helpTopic="prefs-main"
tooltiptext="&paneGeneral.title;"
align="center">
<image class="category-icon"/>
<label class="category-name" flex="1">&paneGeneral.title;</label>
@@ -170,17 +170,17 @@
<label class="category-name" flex="1">&paneSync1.title;</label>
</richlistitem>
</richlistbox>
<keyset>
<key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand="gSearchResultsPane.searchInput.focus();"/>
</keyset>
- <html:a class="help-button" target="_blank" aria-label="&helpButton2.label;">&helpButton2.label;</html:a>
+ <html:a class="help-button visually-hidden" target="_blank" aria-label="&helpButton2.label;">&helpButton2.label;</html:a>
<vbox class="main-content" flex="1" align="start">
<vbox class="pane-container">
<hbox class="search-container" pack="end">
<textbox type="search" id="searchInput" hidden="true" clickSelectsAll="true"/>
</hbox>
<prefpane id="mainPrefPane">
#include searchResults.xul
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -10,16 +10,25 @@
}
:root {
--in-content-category-background: #fafafc;
}
.main-content {
padding-top: 0;
+ /* XXX: A no-op animation solely for the purpose of triggering an animationend
+ event after the first paint. */
+ animation: noop 0s;
+}
+
+.main-content {
+}
+
+@keyframes noop {
}
.pane-container {
/* A workaround to keep the container always float on the `top: 0` (Bug 1377009) */
display: block;
width: 664px;
min-width: 530px;
}