Bug 1186937 - Disconnect MutationObserver instances on Node actor destruction. r=bgrins
authorAlexandre Poirot <poirot.alex@gmail.com>
Mon, 14 Sep 2015 02:47:13 -0700
changeset 294867 36abb9d306015a9b67235d72b3d68b38a898b916
parent 294866 6d3465f4aa24a2894c2d7f14d988fd5c4000a17e
child 294868 cf8240fca7de8f27a2efa028d426bed143d09fed
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1186937
milestone43.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 1186937 - Disconnect MutationObserver instances on Node actor destruction. r=bgrins
toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -219,16 +219,29 @@ var NodeActor = exports.NodeActor = prot
     return this.walker.conn;
   },
 
   isDocumentElement: function() {
     return this.rawNode.ownerDocument &&
            this.rawNode.ownerDocument.documentElement === this.rawNode;
   },
 
+  destroy: function () {
+    protocol.Actor.prototype.destroy.call(this);
+
+    if (this.mutationObserver) {
+      if (!Cu.isDeadWrapper(this.mutationObserver)) {
+        this.mutationObserver.disconnect();
+      }
+      this.mutationObserver = null;
+    }
+    this.rawNode = null;
+    this.walker = null;
+  },
+
   // Returns the JSON representation of this object over the wire.
   form: function(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     let parentNode = this.walker.parentNode(this);
     let singleTextChild = this.walker.singleTextChild(this);
@@ -291,16 +304,35 @@ var NodeActor = exports.NodeActor = prot
     events.emit(NodeActor, "form", {
       target: this,
       data: form
     });
 
     return form;
   },
 
+  /**
+   * Watch the given document node for mutations using the DOM observer
+   * API.
+   */
+  watchDocument: function(callback) {
+    let node = this.rawNode;
+    // Create the observer on the node's actor.  The node will make sure
+    // the observer is cleaned up when the actor is released.
+    let observer = new node.defaultView.MutationObserver(callback);
+    observer.mergeAttributeRecords = true;
+    observer.observe(node, {
+      attributes: true,
+      characterData: true,
+      childList: true,
+      subtree: true
+    });
+    this.mutationObserver = observer;
+  },
+
   get isBeforePseudoElement() {
     return this.rawNode.nodeName === "_moz_generated_content_before"
   },
 
   get isAfterPseudoElement() {
     return this.rawNode.nodeName === "_moz_generated_content_after"
   },
 
@@ -736,22 +768,16 @@ let NodeFront = protocol.FrontClass(Node
   },
 
   /**
    * Destroy a node front.  The node must have been removed from the
    * ownership tree before this is called, unless the whole walker front
    * is being destroyed.
    */
   destroy: function() {
-    // If an observer was added on this node, shut it down.
-    if (this.observer) {
-      this.observer.disconnect();
-      this.observer = null;
-    }
-
     protocol.Front.prototype.destroy.call(this);
   },
 
   // Update the object given a form representation off the wire.
   form: function(form, detail, ctx) {
     if (detail === "actorid") {
       this.actorID = form;
       return;
@@ -1363,17 +1389,17 @@ var WalkerActor = protocol.ActorClass({
     actor = new NodeActor(this, node);
 
     // Add the node actor as a child of this walker actor, assigning
     // it an actorID.
     this.manage(actor);
     this._refMap.set(node, actor);
 
     if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-      this._watchDocument(actor);
+      actor.watchDocument(this.onMutations);
     }
     return actor;
   },
 
   _onReflows: function(reflows) {
     // Going through the nodes the walker knows about, see which ones have
     // had their display changed and send a display-change event if any
     let changes = [];
@@ -1460,34 +1486,16 @@ var WalkerActor = protocol.ActorClass({
 
     return {
       nodes: nodeActors,
       newParents: [...newParents]
     };
   },
 
   /**
-   * Watch the given document node for mutations using the DOM observer
-   * API.
-   */
-  _watchDocument: function(actor) {
-    let node = actor.rawNode;
-    // Create the observer on the node's actor.  The node will make sure
-    // the observer is cleaned up when the actor is released.
-    actor.observer = new actor.rawNode.defaultView.MutationObserver(this.onMutations);
-    actor.observer.mergeAttributeRecords = true;
-    actor.observer.observe(node, {
-      attributes: true,
-      characterData: true,
-      childList: true,
-      subtree: true
-    });
-  },
-
-  /**
    * Return the document node that contains the given node,
    * or the root node if no node is specified.
    * @param NodeActor node
    *        The node whose document is needed, or null to
    *        return the root.
    */
   document: method(function(node) {
     let doc = isNodeDead(node) ? this.rootDoc : nodeDocument(node.rawNode);