зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1447619 - avoid reflowing when returning items to the toolbar from the overflow menu, r=florian
MozReview-Commit-ID: BFGRssWb9F --HG-- extra : rebase_source : e8932810584e10a1de0c1b11c1a152d2dc0f48dd
This commit is contained in:
Родитель
81b1272512
Коммит
39d0e25c38
|
@ -13,27 +13,9 @@
|
||||||
* for tips on how to do that.
|
* for tips on how to do that.
|
||||||
*/
|
*/
|
||||||
const EXPECTED_REFLOWS = [
|
const EXPECTED_REFLOWS = [
|
||||||
{
|
/**
|
||||||
stack: [
|
* Nothing here! Please don't add anything new!
|
||||||
"onOverflow@resource:///modules/CustomizableUI.jsm",
|
*/
|
||||||
],
|
|
||||||
maxCount: 48,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
stack: [
|
|
||||||
"_moveItemsBackToTheirOrigin@resource:///modules/CustomizableUI.jsm",
|
|
||||||
"_onLazyResize@resource:///modules/CustomizableUI.jsm",
|
|
||||||
],
|
|
||||||
maxCount: 5,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
stack: [
|
|
||||||
"_onLazyResize@resource:///modules/CustomizableUI.jsm",
|
|
||||||
],
|
|
||||||
maxCount: 4,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const gToolbar = document.getElementById("PersonalToolbar");
|
const gToolbar = document.getElementById("PersonalToolbar");
|
||||||
|
|
|
@ -4408,18 +4408,36 @@ OverflowableToolbar.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onOverflow(aEvent) {
|
/**
|
||||||
// The rangeParent check is here because of bug 1111986 and ensuring that
|
* Avoid re-entrancy in the overflow handling by keeping track of invocations:
|
||||||
// overflow events from the bookmarks toolbar items or similar things that
|
*/
|
||||||
// manage their own overflow don't trigger an overflow on the entire toolbar
|
_lastOverflowCounter: 0,
|
||||||
if (!this._enabled ||
|
|
||||||
(aEvent && aEvent.target != this._toolbar.customizationTarget) ||
|
/**
|
||||||
(aEvent && aEvent.rangeParent))
|
* Handle overflow in the toolbar by moving items to the overflow menu.
|
||||||
|
* @param {Event} aEvent
|
||||||
|
* The overflow event that triggered handling overflow. May be omitted
|
||||||
|
* in some cases (e.g. when we run this method after overflow handling
|
||||||
|
* is re-enabled from customize mode, to ensure correct handling of
|
||||||
|
* initial overflow).
|
||||||
|
*/
|
||||||
|
async onOverflow(aEvent) {
|
||||||
|
if (!this._enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let child = this._target.lastChild;
|
let child = this._target.lastChild;
|
||||||
|
|
||||||
while (child && this._target.scrollLeftMin != this._target.scrollLeftMax) {
|
let thisOverflowResponse = ++this._lastOverflowCounter;
|
||||||
|
|
||||||
|
let win = this._target.ownerGlobal;
|
||||||
|
let [scrollLeftMin, scrollLeftMax] = await win.promiseDocumentFlushed(() => {
|
||||||
|
return [this._target.scrollLeftMin, this._target.scrollLeftMax];
|
||||||
|
});
|
||||||
|
if (win.closed || this._lastOverflowCounter != thisOverflowResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (child && scrollLeftMin != scrollLeftMax) {
|
||||||
let prevChild = child.previousSibling;
|
let prevChild = child.previousSibling;
|
||||||
|
|
||||||
if (child.getAttribute("overflows") != "false") {
|
if (child.getAttribute("overflows") != "false") {
|
||||||
|
@ -4438,13 +4456,26 @@ OverflowableToolbar.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
child = prevChild;
|
child = prevChild;
|
||||||
|
[scrollLeftMin, scrollLeftMax] = await win.promiseDocumentFlushed(() => {
|
||||||
|
return [this._target.scrollLeftMin, this._target.scrollLeftMax];
|
||||||
|
});
|
||||||
|
// If the window has closed or if we re-enter because we were waiting
|
||||||
|
// for layout, stop.
|
||||||
|
if (win.closed || this._lastOverflowCounter != thisOverflowResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let win = this._target.ownerGlobal;
|
|
||||||
win.UpdateUrlbarSearchSplitterState();
|
win.UpdateUrlbarSearchSplitterState();
|
||||||
|
// Reset the counter because we finished handling overflow.
|
||||||
|
this._lastOverflowCounter = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onResize(aEvent) {
|
_onResize(aEvent) {
|
||||||
|
// Ignore bubbled-up resize events.
|
||||||
|
if (aEvent.target != aEvent.target.ownerGlobal.top) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this._lazyResizeHandler) {
|
if (!this._lazyResizeHandler) {
|
||||||
this._lazyResizeHandler = new DeferredTask(this._onLazyResize.bind(this),
|
this._lazyResizeHandler = new DeferredTask(this._onLazyResize.bind(this),
|
||||||
LAZY_RESIZE_INTERVAL_MS, 0);
|
LAZY_RESIZE_INTERVAL_MS, 0);
|
||||||
|
@ -4452,16 +4483,33 @@ OverflowableToolbar.prototype = {
|
||||||
this._lazyResizeHandler.arm();
|
this._lazyResizeHandler.arm();
|
||||||
},
|
},
|
||||||
|
|
||||||
_moveItemsBackToTheirOrigin(shouldMoveAllItems) {
|
/**
|
||||||
|
* Try to move toolbar items back to the toolbar from the overflow menu.
|
||||||
|
* @param {boolean} shouldMoveAllItems
|
||||||
|
* Whether we should move everything (e.g. because we're being disabled)
|
||||||
|
* @param {number} targetWidth
|
||||||
|
* Optional; the width of the toolbar in which we can put things.
|
||||||
|
* Some consumers pass this to avoid reflows.
|
||||||
|
* While there are items in the list, this width won't change, and so
|
||||||
|
* we can avoid flushing layout by providing it and/or caching it.
|
||||||
|
* Note that if `shouldMoveAllItems` is true, we never need the width
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
_moveItemsBackToTheirOrigin(shouldMoveAllItems, targetWidth) {
|
||||||
let placements = gPlacements.get(this._toolbar.id);
|
let placements = gPlacements.get(this._toolbar.id);
|
||||||
|
let win = this._target.ownerGlobal;
|
||||||
while (this._list.firstChild) {
|
while (this._list.firstChild) {
|
||||||
let child = this._list.firstChild;
|
let child = this._list.firstChild;
|
||||||
let minSize = this._collapsed.get(child.id);
|
let minSize = this._collapsed.get(child.id);
|
||||||
|
|
||||||
if (!shouldMoveAllItems &&
|
if (!shouldMoveAllItems && minSize) {
|
||||||
minSize &&
|
if (!targetWidth) {
|
||||||
this._target.clientWidth <= minSize) {
|
let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||||
break;
|
targetWidth = Math.floor(dwu.getBoundsWithoutFlushing(this._target).width);
|
||||||
|
}
|
||||||
|
if (targetWidth <= minSize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._collapsed.delete(child.id);
|
this._collapsed.delete(child.id);
|
||||||
|
@ -4493,7 +4541,6 @@ OverflowableToolbar.prototype = {
|
||||||
CustomizableUIInternal.notifyListeners("onWidgetUnderflow", child, this._target);
|
CustomizableUIInternal.notifyListeners("onWidgetUnderflow", child, this._target);
|
||||||
}
|
}
|
||||||
|
|
||||||
let win = this._target.ownerGlobal;
|
|
||||||
win.UpdateUrlbarSearchSplitterState();
|
win.UpdateUrlbarSearchSplitterState();
|
||||||
|
|
||||||
let collapsedWidgetIds = Array.from(this._collapsed.keys());
|
let collapsedWidgetIds = Array.from(this._collapsed.keys());
|
||||||
|
@ -4506,14 +4553,21 @@ OverflowableToolbar.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onLazyResize() {
|
async _onLazyResize() {
|
||||||
if (!this._enabled)
|
if (!this._enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._target.scrollLeftMin != this._target.scrollLeftMax) {
|
let win = this._target.ownerGlobal;
|
||||||
|
let [min, max, targetWidth] = await win.promiseDocumentFlushed(() => {
|
||||||
|
return [this._target.scrollLeftMin, this._target.scrollLeftMax, this._target.clientWidth];
|
||||||
|
});
|
||||||
|
if (win.closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (min != max) {
|
||||||
this.onOverflow();
|
this.onOverflow();
|
||||||
} else {
|
} else {
|
||||||
this._moveItemsBackToTheirOrigin();
|
this._moveItemsBackToTheirOrigin(false, targetWidth);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4608,7 +4662,7 @@ OverflowableToolbar.prototype = {
|
||||||
} else {
|
} else {
|
||||||
// If it's now the first item in the overflow list,
|
// If it's now the first item in the overflow list,
|
||||||
// maybe we can return it:
|
// maybe we can return it:
|
||||||
this._moveItemsBackToTheirOrigin();
|
this._moveItemsBackToTheirOrigin(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,14 @@ add_task(async function() {
|
||||||
|
|
||||||
let originalWindowWidth = window.outerWidth;
|
let originalWindowWidth = window.outerWidth;
|
||||||
window.resizeTo(kForceOverflowWidthPx, window.outerHeight);
|
window.resizeTo(kForceOverflowWidthPx, window.outerHeight);
|
||||||
await waitForCondition(() => navbar.hasAttribute("overflowing"));
|
// Wait for all the widgets to overflow. We can't just wait for the
|
||||||
|
// `overflowing` attribute because we leave time for layout flushes
|
||||||
|
// inbetween, so it's possible for the timeout to run before the
|
||||||
|
// navbar has "settled"
|
||||||
|
await waitForCondition(() => {
|
||||||
|
return navbar.hasAttribute("overflowing") &&
|
||||||
|
navbar.customizationTarget.lastChild.getAttribute("overflows") == "false";
|
||||||
|
});
|
||||||
|
|
||||||
// Find last widget that doesn't allow overflowing
|
// Find last widget that doesn't allow overflowing
|
||||||
let nonOverflowing = navbar.customizationTarget.lastChild;
|
let nonOverflowing = navbar.customizationTarget.lastChild;
|
||||||
|
|
|
@ -1172,11 +1172,15 @@ PlacesToolbar.prototype = {
|
||||||
case "overflow":
|
case "overflow":
|
||||||
if (!this._isOverflowStateEventRelevant(aEvent))
|
if (!this._isOverflowStateEventRelevant(aEvent))
|
||||||
return;
|
return;
|
||||||
|
// Avoid triggering overflow in containers if possible
|
||||||
|
aEvent.stopPropagation();
|
||||||
this._onOverflow();
|
this._onOverflow();
|
||||||
break;
|
break;
|
||||||
case "underflow":
|
case "underflow":
|
||||||
if (!this._isOverflowStateEventRelevant(aEvent))
|
if (!this._isOverflowStateEventRelevant(aEvent))
|
||||||
return;
|
return;
|
||||||
|
// Avoid triggering underflow in containers if possible
|
||||||
|
aEvent.stopPropagation();
|
||||||
this._onUnderflow();
|
this._onUnderflow();
|
||||||
break;
|
break;
|
||||||
case "TabOpen":
|
case "TabOpen":
|
||||||
|
|
Загрузка…
Ссылка в новой задаче