Bug 1353013 - Wait for inner and outer window nukings before completing shutdown leaks in content processes. r?mccr8 draft
authorMike Conley <mconley@mozilla.com>
Wed, 28 Mar 2018 11:17:48 -0700
changeset 774431 4f1ec690d43324003f854624e43f6c743f095196
parent 772574 7b9da7139d94951431a148dcaf8a388640c91b27
child 774432 a3786b7c424a4a1974be9f785bed695d9470959d
push id104397
push usermconley@mozilla.com
push dateWed, 28 Mar 2018 21:50:30 +0000
reviewersmccr8
bugs1353013
milestone61.0a1
Bug 1353013 - Wait for inner and outer window nukings before completing shutdown leaks in content processes. r?mccr8 This is needed to avoid some intermittent shutdown leaks being reported from automation that patches later in this series unhappily seem to trigger quite often. MozReview-Commit-ID: 2HVOILBXWP
testing/mochitest/ShutdownLeaksCollector.jsm
--- a/testing/mochitest/ShutdownLeaksCollector.jsm
+++ b/testing/mochitest/ShutdownLeaksCollector.jsm
@@ -16,18 +16,24 @@ var ContentCollector = {
   init: function() {
       let processType = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processType;
       if (processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
         // In the main process, we handle triggering collections in browser-test.js
         return;
       }
 
     Services.cpmm.addMessageListener("browser-test:collect-request", this);
+    Services.obs.addObserver(this, "outer-window-destroyed");
+    Services.obs.addObserver(this, "outer-window-nuked");
+    Services.obs.addObserver(this, "inner-window-destroyed");
+    Services.obs.addObserver(this, "inner-window-nuked");
   },
 
+  destroyedWindowIDs: new Set(),
+
   receiveMessage: function(aMessage) {
     switch (aMessage.name) {
       case "browser-test:collect-request":
         Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
 
         Cu.forceGC();
         Cu.forceCC();
 
@@ -37,28 +43,77 @@ var ContentCollector = {
             // as possible is freed.
             Cu.forceGC();
             Cu.forceCC();
             aCallback();
           });
         };
 
         shutdownCleanup(() => {
-          setTimeout(() => {
-            shutdownCleanup(() => {
-              this.finish();
-            })
-          }, 1000);
+          this.doneDestroyingWindows().then(() => {
+            setTimeout(() => {
+              shutdownCleanup(() => {
+                this.finish();
+              })
+            }, 1000);
+          });
         });
 
         break;
     }
   },
 
+  observe(subject, topic, data) {
+    // Subject for outer-window-destroyed and outer-window-nuked
+    // should be the outerWindowID wrapped in an
+    // nsISupportsPRUint64
+    let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+
+    switch (topic) {
+      case "inner-window-destroyed":
+        // Fall-through
+      case "outer-window-destroyed": {
+        this.onWindowDestroyed(id);
+        break;
+      }
+      case "inner-window-nuked":
+        // Fall-through
+      case "outer-window-nuked": {
+        this.onWindowNuked(id);
+        break;
+      }
+    }
+  },
+
+  onWindowDestroyed(windowID) {
+    this.destroyedWindowIDs.add(windowID);
+  },
+
+  onWindowNuked(windowID) {
+    this.destroyedWindowIDs.delete(windowID);
+    if (this._resolver) {
+      this._resolver();
+    }
+  },
+
+  doneDestroyingWindows() {
+    if (!this.destroyedWindowIDs.size) {
+      return Promise.resolve();
+    } else {
+      return new Promise(resolve => {
+        this._resolver = resole;
+      });
+    }
+  },
+
   finish() {
     let pid = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processID;
     dump("Completed ShutdownLeaks collections in process " + pid + "\n");
 
     Services.cpmm.removeMessageListener("browser-test:collect-request", this);
+    Services.obs.removeObserver(this, "outer-window-destroyed");
+    Services.obs.removeObserver(this, "outer-window-nuked");
+    Services.obs.removeObserver(this, "inner-window-destroyed");
+    Services.obs.removeObserver(this, "inner-window-nuked");
   },
 
 };
 ContentCollector.init();