Bug 1356532: Part 1 - Add BrowserUtils.promiseAnimationFrame helper. r=florian
It's important that requestAnimationFrame callbacks run synchronously, and
carefully control what code runs during them. At the same time, we often need
UI code that runs before and after the DOM mutations in an animation frame
callback.
This helper makes it possible to run DOM mutation code during an animation
frame in the course of an async function, and then resume execution after the
animation frame callback has been executed, and without danger of blocking the
frame from being painted.
It's also important to note that, at the moment, the promise microtask queue
is not flushed at the end of the animation frame callback, which means we can
safely rely on promise resolution handlers automatically executing after frame
has been painted. After
bug 1193394 is fixed, that will no longer be the case,
and we'll need to resolve the promise from a task dispatched to the event
loop.
MozReview-Commit-ID: H5MHWKuLf3O
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -720,9 +720,33 @@ this.BrowserUtils = {
.getInterface(Ci.nsIDOMWindowUtils);
if (!utils.needsFlush(FLUSH_TYPES[flushType])) {
return callback();
}
return this.promiseReflowed(doc, callback);
},
+
+ /**
+ * Calls the given callback on the next animation frame for the given
+ * window, and resolves with its return value after it's been
+ * executed.
+ *
+ * The callback function may alter the DOM freely, but *must not query the document's style or
+ * layout state*.
+ *
+ * @param {Window} win
+ * @param {function} callback
+ * @returns {Promise}
+ */
+ promiseAnimationFrame(win, callback) {
+ return new Promise((resolve, reject) => {
+ win.requestAnimationFrame(timestamp => {
+ try {
+ resolve(callback(timestamp));
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+ },
};