Bug 911216 - Part 20: Make aboutPerformance.js robust against timing issues when running in mochitest harness. r=yoric
authorTill Schneidereit <till@tillschneidereit.net>
Thu, 26 May 2016 16:13:47 +0200
changeset 340206 b87905133eafb182539e8ea332ad19ab9548e8ee
parent 340205 8d3730e7d1f0fb096bfe93d11afd2691e9fe1453
child 340207 1b8749c914b047f44fe3447c62c4472f244c6a8e
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyoric
bugs911216
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 911216 - Part 20: Make aboutPerformance.js robust against timing issues when running in mochitest harness. r=yoric
toolkit/components/aboutperformance/content/aboutPerformance.js
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -110,18 +110,38 @@ let tabFinder = {
       if (result) {
         return result;
       }
     }
     return null;
   }
 };
 
+/**
+ * Returns a Promise that's resolved after the next turn of the event loop.
+ *
+ * Just returning a resolved Promise would mean that any `then` callbacks
+ * would be called right after the end of the current turn, so `setTimeout`
+ * is used to delay Promise resolution until the next turn.
+ *
+ * In mochi tests, it's possible for this to be called after the
+ * about:performance window has been torn down, which causes `setTimeout` to
+ * throw an NS_ERROR_NOT_INITIALIZED exception. In that case, returning
+ * `undefined` is fine.
+ */
 function wait(ms = 0) {
-  return new Promise(resolve => setTimeout(resolve, ms));
+  try {
+    let resolve;
+    let p = new Promise(resolve_ => {resolve = resolve_});
+    setTimeout(resolve, ms);
+    return p;
+  } catch (e) {
+    dump("WARNING: wait aborted because of an invalid Window state in aboutPerformance.js.\n");
+    return;
+  }
 }
 
 /**
  * The performance of a webpage or an add-on between two instants.
  *
  * Clients should call `promiseInit()` before using the methods of this object.
  *
  * @param {PerformanceDiff} The underlying performance data.
@@ -393,17 +413,17 @@ var State = {
   update: Task.async(function*() {
     // If the buffer is empty, add one value for bootstraping purposes.
     if (this._buffer.length == 0) {
       if (this._oldest) {
         throw new Error("Internal Error, we shouldn't have a `_oldest` value yet.");
       }
       this._latest = this._oldest = yield this._monitor.promiseSnapshot();
       this._buffer.push(this._oldest);
-      yield new Promise(resolve => setTimeout(resolve, BUFFER_SAMPLING_RATE_MS * 1.1));
+      yield wait(BUFFER_SAMPLING_RATE_MS * 1.1);
     }
 
 
     let now = Cu.now();
 
     // If we haven't sampled in a while, add a sample to the buffer.
     let latestInBuffer = this._buffer[this._buffer.length - 1];
     let deltaT = now - latestInBuffer.date;
@@ -930,11 +950,11 @@ var go = Task.async(function*() {
     let options = JSON.parse(value);
     Control._setOptions(options);
     Control.update();
   };
   Services.obs.addObserver(testUpdate, TEST_DRIVER_TOPIC, false);
   window.addEventListener("unload", () => Services.obs.removeObserver(testUpdate, TEST_DRIVER_TOPIC));
 
   yield Control.update();
-  yield new Promise(resolve => setTimeout(resolve, BUFFER_SAMPLING_RATE_MS * 1.1));
+  yield wait(BUFFER_SAMPLING_RATE_MS * 1.1);
   yield Control.update();
 });