Bug 1356532: Part 5 - Avoid layout flushes during adjustHeight(). r=florian
MozReview-Commit-ID: 16hfIz8Ai9M
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1234,85 +1234,106 @@ extends="chrome://global/content/binding
}
]]>
</body>
</method>
<method name="adjustHeight">
<body>
<![CDATA[
+
+ if (this._adjustHeightPromise) {
+ return;
+ }
+
// Figure out how many rows to show
let rows = this.richlistbox.childNodes;
let numRows = Math.min(this._matchCount, this.maxRows, rows.length);
this.removeAttribute("height");
- // Default the height to 0 if we have no rows to show
- let height = 0;
- if (numRows) {
- let firstRowRect = rows[0].getBoundingClientRect();
- if (this._rlbPadding == undefined) {
- let style = window.getComputedStyle(this.richlistbox);
+ this._adjustHeightPromise = (async () => {
+ // Default the height to 0 if we have no rows to show
+ let height = 0;
+ let currentHeight;
- let transition = style.transitionProperty;
- this._rlbAnimated = transition && transition != "none";
+ await BrowserUtils.promiseSlowLayoutFlush(document, "layout", () => {
+ if (numRows) {
+ let firstRowRect = rows[0].getBoundingClientRect();
+ if (this._rlbPadding == undefined) {
+ let style = window.getComputedStyle(this.richlistbox);
- let paddingTop = parseInt(style.paddingTop) || 0;
- let paddingBottom = parseInt(style.paddingBottom) || 0;
- this._rlbPadding = paddingTop + paddingBottom;
- }
+ let transition = style.transitionProperty;
+ this._rlbAnimated = transition && transition != "none";
+
+ let paddingTop = parseInt(style.paddingTop) || 0;
+ let paddingBottom = parseInt(style.paddingBottom) || 0;
+ this._rlbPadding = paddingTop + paddingBottom;
+ }
- if (numRows > this.maxRows) {
- // Set a fixed max-height to avoid flicker when growing the panel.
- let lastVisibleRowRect = rows[this.maxRows - 1].getBoundingClientRect();
- let visibleHeight = lastVisibleRowRect.bottom - firstRowRect.top;
- this.richlistbox.style.maxHeight =
- visibleHeight + this._rlbPadding + "px";
- }
+ if (numRows > this.maxRows) {
+ // Set a fixed max-height to avoid flicker when growing the panel.
+ let lastVisibleRowRect = rows[this.maxRows - 1].getBoundingClientRect();
+ let visibleHeight = lastVisibleRowRect.bottom - firstRowRect.top;
- // The class `forceHandleUnderflow` is for the item might need to
- // handle OverUnderflow or Overflow when the height of an item will
- // be changed dynamically.
- for (let i = 0; i < numRows; i++) {
- if (rows[i].classList.contains("forceHandleUnderflow")) {
- rows[i].handleOverUnderflow();
- }
- }
+ BrowserUtils.promiseAnimationFrame(window, () => {
+ this.richlistbox.style.maxHeight =
+ visibleHeight + this._rlbPadding + "px";
+ });
+ }
+
+ // The class `forceHandleUnderflow` is for the item might need to
+ // handle OverUnderflow or Overflow when the height of an item will
+ // be changed dynamically.
+ for (let i = 0; i < numRows; i++) {
+ if (rows[i].classList.contains("forceHandleUnderflow")) {
+ rows[i].handleOverUnderflow();
+ }
+ }
- let lastRowRect = rows[numRows - 1].getBoundingClientRect();
- // Calculate the height to have the first row to last row shown
- height = lastRowRect.bottom - firstRowRect.top +
- this._rlbPadding;
- }
+ let lastRowRect = rows[numRows - 1].getBoundingClientRect();
+ // Calculate the height to have the first row to last row shown
+ height = lastRowRect.bottom - firstRowRect.top +
+ this._rlbPadding;
+ }
+
+ currentHeight = this.richlistbox.getBoundingClientRect().height;
+ });
+
+ this._adjustHeightPromise = null;
- let animate = this._rlbAnimated &&
- this.getAttribute("dontanimate") != "true";
- let currentHeight = this.richlistbox.getBoundingClientRect().height;
- if (height > currentHeight) {
- // Grow immediately.
- if (animate) {
- this.richlistbox.removeAttribute("height");
- this.richlistbox.style.height = height + "px";
+ let animate = this._rlbAnimated &&
+ this.getAttribute("dontanimate") != "true";
+ if (height > currentHeight) {
+ BrowserUtils.promiseAnimationFrame(window, () => {
+ // Grow immediately.
+ if (animate) {
+ this.richlistbox.removeAttribute("height");
+ this.richlistbox.style.height = height + "px";
+ } else {
+ this.richlistbox.style.removeProperty("height");
+ this.richlistbox.height = height;
+ }
+ })
} else {
- this.richlistbox.style.removeProperty("height");
- this.richlistbox.height = height;
+ // Delay shrinking to avoid flicker.
+ this._shrinkTimeout = setTimeout(() => {
+ BrowserUtils.promiseAnimationFrame(window, () => {
+ this._collapseUnusedItems();
+ if (animate) {
+ this.richlistbox.removeAttribute("height");
+ this.richlistbox.style.height = height + "px";
+ } else {
+ this.richlistbox.style.removeProperty("height");
+ this.richlistbox.height = height;
+ }
+ });
+ }, this.mInput.shrinkDelay);
}
- } else {
- // Delay shrinking to avoid flicker.
- this._shrinkTimeout = setTimeout(() => {
- this._collapseUnusedItems();
- if (animate) {
- this.richlistbox.removeAttribute("height");
- this.richlistbox.style.height = height + "px";
- } else {
- this.richlistbox.style.removeProperty("height");
- this.richlistbox.height = height;
- }
- }, this.mInput.shrinkDelay);
- }
+ })();
]]>
</body>
</method>
<method name="_appendCurrentResult">
<parameter name="invalidateReason"/>
<body>
<![CDATA[