Bug 932880: Developer tools leak many windows until shutdown in browser-chrome tests. r=anton CLOSED TREE
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 30 Oct 2013 20:29:06 -0500
changeset 167485 494d0cca0cb08cb74dca4c8d7ca28b7470438530
parent 167484 ba5c76b30f290300479c9a30bca000b3ccfec097
child 167486 acd7840da8347c9abc7c872a6676e55c452b79b5
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanton
bugs932880
milestone28.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 932880: Developer tools leak many windows until shutdown in browser-chrome tests. r=anton CLOSED TREE
browser/devtools/framework/toolbox.js
browser/devtools/inspector/inspector-panel.js
browser/devtools/inspector/test/head.js
toolkit/devtools/server/actors/inspector.js
toolkit/devtools/server/protocol.js
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -903,11 +903,12 @@ Toolbox.prototype = {
         target.off("close", this.destroy);
         return target.destroy();
       }
     }).then(() => {
       this.emit("destroyed");
       // Free _host after the call to destroyed in order to let a chance
       // to destroyed listeners to still query toolbox attributes
       this._host = null;
-    });
+      this._toolPanels.clear();
+    }).then(null, console.error);
   }
 };
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -491,16 +491,18 @@ InspectorPanel.prototype = {
     }
 
     if (this.highlighter) {
       this.highlighter.off("locked", this.onLockStateChanged);
       this.highlighter.off("unlocked", this.onLockStateChanged);
       this.highlighter.destroy();
     }
 
+    delete this.onLockStateChanged;
+
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this._destroyPromise = this.walker.release().then(null, console.error);
       delete this.walker;
       delete this.pageStyle;
     } else {
       this._destroyPromise = promise.resolve(null);
     }
@@ -524,16 +526,17 @@ InspectorPanel.prototype = {
     this.sidebar.off("select", this._setDefaultSidebar);
     this.sidebar.destroy();
     this.sidebar = null;
 
     this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
     this.breadcrumbs.destroy();
     this.searchSuggestions.destroy();
+    delete this.searchBox;
     this.selection.off("new-node-front", this.onNewSelection);
     this.selection.off("before-new-node", this.onBeforeNewSelection);
     this.selection.off("before-new-node-front", this.onBeforeNewSelection);
     this.selection.off("detached-front", this.onDetached);
     this._destroyMarkup();
     this._selection.destroy();
     this._selection = null;
     this.panelWin.inspector = null;
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -183,8 +183,13 @@ function getComputedPropertyValue(aName)
     let name = prop.querySelector(".property-name");
 
     if (name.textContent === aName) {
       let value = prop.querySelector(".property-value");
       return value.textContent;
     }
   }
 }
+
+SimpleTest.registerCleanupFunction(function () {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  gDevTools.closeToolbox(target);
+});
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -2362,16 +2362,21 @@ var InspectorFront = exports.InspectorFr
     this.actorID = tabForm.inspectorActor;
 
     // XXX: This is the first actor type in its hierarchy to use the protocol
     // library, so we're going to self-own on the client side for now.
     client.addActorPool(this);
     this.manage(this);
   },
 
+  destroy: function() {
+    delete this.walker;
+    protocol.Front.prototype.destroy.call(this);
+  },
+
   getWalker: protocol.custom(function() {
     return this._getWalker().then(walker => {
       this.walker = walker;
       return walker;
     });
   }, {
     impl: "_getWalker"
   }),
--- a/toolkit/devtools/server/protocol.js
+++ b/toolkit/devtools/server/protocol.js
@@ -910,17 +910,22 @@ let actorProto = function(actorProto) {
           return;
         }
 
         let sendReturn = (ret) => {
           let response = spec.response.write(ret, this);
           response.from = this.actorID;
           // If spec.release has been specified, destroy the object.
           if (spec.release) {
-            this.destroy();
+            try {
+              this.destroy();
+            } catch(e) {
+              this.writeError(e);
+              return;
+            }
           }
 
           conn.send(response);
         };
 
         this._queueResponse(p => {
           return p
             .then(() => ret)
@@ -983,16 +988,22 @@ let Front = Class({
     this._requests = [];
     if (form) {
       this.actorID = form.actor;
       this.form(form, detail, context);
     }
   },
 
   destroy: function() {
+    // Reject all outstanding requests, they won't make sense after
+    // the front is destroyed.
+    while (this._requests && this._requests.length > 0) {
+      let deferred = this._requests.shift();
+      deferred.reject(new Error("Connection closed"));
+    }
     Pool.prototype.destroy.call(this);
     this.actorID = null;
   },
 
   /**
    * @returns a promise that will resolve to the actorID this front
    * represents.
    */