зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1358712 - Get rid of synchronous layout flushes when calculating where to put the StatusPanel. r=Felipe
MozReview-Commit-ID: KHagFdaRAzF --HG-- extra : rebase_source : 4c79b7f23cc8980179ab0437eab13e7b03fd60c1
This commit is contained in:
Родитель
0c2106dafb
Коммит
0a0c4cc456
|
@ -7815,19 +7815,64 @@ var MousePosTracker = {
|
|||
_listeners: new Set(),
|
||||
_x: 0,
|
||||
_y: 0,
|
||||
_mostRecentEvent: null,
|
||||
|
||||
get _windowUtils() {
|
||||
delete this._windowUtils;
|
||||
return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a listener, and then waits for the next refresh
|
||||
* driver tick before running the listener to see if the
|
||||
* mouse is within the listener's target rect.
|
||||
*
|
||||
* @param listener (object)
|
||||
* A listener is expected to expose the following properties:
|
||||
*
|
||||
* getMouseTargetRect (function)
|
||||
* Returns the rect that the MousePosTracker needs to alert
|
||||
* the listener about if the mouse happens to be within it.
|
||||
*
|
||||
* onTrackingStarted (function, optional)
|
||||
* Called after the next refresh driver tick after listening,
|
||||
* when the mouse's initial position relative to the MouseTargetRect
|
||||
* can be computed. If the listener is removed before the refresh
|
||||
* driver tick, this might never be called.
|
||||
*
|
||||
* onMouseEnter (function, optional)
|
||||
* The function to be called if the mouse enters the rect
|
||||
* returned by getMouseTargetRect. MousePosTracker always
|
||||
* runs this inside of a requestAnimationFrame, since it
|
||||
* assumes that the notification is used to update the DOM.
|
||||
*
|
||||
* onMouseLeave (function, optional)
|
||||
* The function to be called if the mouse exits the rect
|
||||
* returned by getMouseTargetRect. MousePosTracker always
|
||||
* runs this inside of a requestAnimationFrame, since it
|
||||
* assumes that the notification is used to update the DOM.
|
||||
*/
|
||||
addListener(listener) {
|
||||
if (this._listeners.has(listener))
|
||||
if (this._listeners.has(listener)) {
|
||||
return;
|
||||
}
|
||||
|
||||
listener._hover = false;
|
||||
this._listeners.add(listener);
|
||||
|
||||
this._callListener(listener);
|
||||
// We're adding some asynchronicity here, during which the listener
|
||||
// might be removed. At each step, we need to ensure that the listener
|
||||
// is still registered before proceeding.
|
||||
window.promiseDocumentFlushed(() => {
|
||||
if (this._listeners.has(listener)) {
|
||||
this._callListeners([listener]);
|
||||
window.requestAnimationFrame(() => {
|
||||
if (this._listeners.has(listener) && listener.onTrackingStarted) {
|
||||
listener.onTrackingStarted();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeListener(listener) {
|
||||
|
@ -7835,38 +7880,73 @@ var MousePosTracker = {
|
|||
},
|
||||
|
||||
handleEvent(event) {
|
||||
var fullZoom = this._windowUtils.fullZoom;
|
||||
this._x = event.screenX / fullZoom - window.mozInnerScreenX;
|
||||
this._y = event.screenY / fullZoom - window.mozInnerScreenY;
|
||||
let firstEvent = !this._mostRecentEvent;
|
||||
this._mostRecentEvent = event;
|
||||
|
||||
this._listeners.forEach(function(listener) {
|
||||
try {
|
||||
this._callListener(listener);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}, this);
|
||||
if (firstEvent) {
|
||||
window.promiseDocumentFlushed(() => {
|
||||
this.onDocumentFlushed();
|
||||
this._mostRecentEvent = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_callListener(listener) {
|
||||
let rect = listener.getMouseTargetRect();
|
||||
let hover = this._x >= rect.left &&
|
||||
this._x <= rect.right &&
|
||||
this._y >= rect.top &&
|
||||
this._y <= rect.bottom;
|
||||
onDocumentFlushed() {
|
||||
let event = this._mostRecentEvent;
|
||||
|
||||
if (hover == listener._hover)
|
||||
return;
|
||||
if (event) {
|
||||
let fullZoom = this._windowUtils.fullZoom;
|
||||
this._x = event.screenX / fullZoom - window.mozInnerScreenX;
|
||||
this._y = event.screenY / fullZoom - window.mozInnerScreenY;
|
||||
|
||||
listener._hover = hover;
|
||||
|
||||
if (hover) {
|
||||
if (listener.onMouseEnter)
|
||||
listener.onMouseEnter();
|
||||
} else if (listener.onMouseLeave) {
|
||||
listener.onMouseLeave();
|
||||
this._callListeners(this._listeners);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_callListeners(listeners) {
|
||||
let functionsToCall = [];
|
||||
for (let listener of listeners) {
|
||||
let rect;
|
||||
try {
|
||||
rect = listener.getMouseTargetRect();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
let hover = this._x >= rect.left &&
|
||||
this._x <= rect.right &&
|
||||
this._y >= rect.top &&
|
||||
this._y <= rect.bottom;
|
||||
|
||||
if (hover == listener._hover) {
|
||||
continue;
|
||||
}
|
||||
|
||||
listener._hover = hover;
|
||||
if (hover) {
|
||||
if (listener.onMouseEnter) {
|
||||
functionsToCall.push(listener.onMouseEnter.bind(listener));
|
||||
}
|
||||
} else if (listener.onMouseLeave) {
|
||||
functionsToCall.push(listener.onMouseLeave.bind(listener));
|
||||
}
|
||||
}
|
||||
|
||||
// _callListeners is being called from within a promiseDocumentFlushed,
|
||||
// where we are expressly forbidden from dirtying styles or layout. Since
|
||||
// the onMouseEnter or onMouseLeave functions are liable to do such
|
||||
// dirtying, we run them inside a requestAnimationFrame callback instead.
|
||||
window.requestAnimationFrame(() => {
|
||||
for (let fn of functionsToCall) {
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
var ToolbarIconColor = {
|
||||
|
|
|
@ -4642,10 +4642,12 @@ var StatusPanel = {
|
|||
}
|
||||
|
||||
if (val) {
|
||||
this._labelElement.value = val;
|
||||
this.panel.removeAttribute("inactive");
|
||||
this._mouseTargetRect = null;
|
||||
this._labelElement.value = val;
|
||||
MousePosTracker.addListener(this);
|
||||
// The inactive state for the panel will be removed in onTrackingStarted,
|
||||
// once the initial position of the mouse relative to the StatusPanel
|
||||
// is figured out (to avoid both flicker and sync flushing).
|
||||
} else {
|
||||
this.panel.setAttribute("inactive", "true");
|
||||
MousePosTracker.removeListener(this);
|
||||
|
@ -4654,6 +4656,10 @@ var StatusPanel = {
|
|||
return val;
|
||||
},
|
||||
|
||||
onTrackingStarted() {
|
||||
this.panel.removeAttribute("inactive");
|
||||
},
|
||||
|
||||
getMouseTargetRect() {
|
||||
if (!this._mouseTargetRect) {
|
||||
this._calcMouseTargetRect();
|
||||
|
|
Загрузка…
Ссылка в новой задаче