Bug 888492 - Use the content window rather than tabbrowser for load notifications. r=jwalker, a=akeybl
authorDave Camp <dcamp@mozilla.com>
Mon, 12 Aug 2013 12:50:09 -0700
changeset 154042 1874fd8b57882086d59b0d0759d3de624457781d
parent 154041 e7fbc267ac5c895dc45fbac1ad148cf22de72a82
child 154043 691c5a00acd2f92047f862addf8154b3a88f016a
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalker, akeybl
bugs888492
milestone25.0a2
Bug 888492 - Use the content window rather than tabbrowser for load notifications. r=jwalker, a=akeybl
toolkit/devtools/server/actors/inspector.js
toolkit/devtools/server/actors/webbrowser.js
toolkit/devtools/server/transport.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -45,17 +45,17 @@
  * the client - when a node is disconnected from the DOM tree we want to be
  * able to free the client objects for all the children nodes.
  *
  * So to be able to answer "all the children of a given node that we have
  * seen on the client side", we guarantee that every time we've seen a node,
  * we connect it up through its parents.
  */
 
-const {Cc, Ci, Cu} = require("chrome");
+const {Cc, Ci, Cu, Cr} = require("chrome");
 
 const protocol = require("devtools/server/protocol");
 const {Arg, Option, method, RetVal, types} = protocol;
 const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
 const promise = require("sdk/core/promise");
 const object = require("sdk/util/object");
 const events = require("sdk/event/core");
 const { Unknown } = require("sdk/platform/xpcom");
@@ -687,27 +687,39 @@ let traversalMethod = {
  *
  * See nsIWebProgressListener for details
  * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIWebProgressListener
  */
 var ProgressListener = Class({
   extends: Unknown,
   interfaces: ["nsIWebProgressListener", "nsISupportsWeakReference"],
 
-  initialize: function(webProgress) {
+  initialize: function(tabActor) {
     Unknown.prototype.initialize.call(this);
-    this.webProgress = webProgress;
-    this.webProgress.addProgressListener(this);
+    this.webProgress = tabActor.webProgress;
+    this.webProgress.addProgressListener(
+      this, Ci.nsIWebProgress.NOTIFY_ALL
+    );
   },
 
   destroy: function() {
-    this.webProgress.removeProgressListener(this);
+    try {
+      this.webProgress.removeProgressListener(this);
+    } catch(ex) {
+      // This can throw during browser shutdown.
+    }
+    this.webProgress = null;
   },
 
   onStateChange: makeInfallible(function stateChange(progress, request, flag, status) {
+    if (!this.webProgress) {
+      console.warn("got an onStateChange after destruction");
+      return;
+    }
+
     let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
     let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
     if (!(isWindow || isDocument)) {
       return;
     }
 
     if (isDocument && (flag & Ci.nsIWebProgressListener.STATE_START)) {
       events.emit(this, "windowchange-start", progress.DOMWindow);
@@ -734,19 +746,19 @@ var WalkerActor = protocol.ActorClass({
     }
   },
 
   /**
    * Create the WalkerActor
    * @param DebuggerServerConnection conn
    *    The server connection.
    */
-  initialize: function(conn, document, webProgress, options) {
+  initialize: function(conn, tabActor, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
-    this.rootDoc = document;
+    this.rootDoc = tabActor.window.document;
     this._refMap = new Map();
     this._pendingMutations = [];
     this._activePseudoClassLocks = new Set();
 
     // Nodes which have been removed from the client's known
     // ownership tree are considered "orphaned", and stored in
     // this set.
     this._orphaned = new Set();
@@ -755,17 +767,17 @@ var WalkerActor = protocol.ActorClass({
     // even when it is orphaned with the `retainNode` method.  This
     // list contains orphaned nodes that were so retained.
     this._retainedOrphans = new Set();
 
     this.onMutations = this.onMutations.bind(this);
     this.onFrameLoad = this.onFrameLoad.bind(this);
     this.onFrameUnload = this.onFrameUnload.bind(this);
 
-    this.progressListener = ProgressListener(webProgress);
+    this.progressListener = ProgressListener(tabActor);
 
     events.on(this.progressListener, "windowchange-start", this.onFrameUnload);
     events.on(this.progressListener, "windowchange-stop", this.onFrameLoad);
 
     // Ensure that the root document node actor is ready and
     // managed.
     this.rootNode = this.document();
   },
@@ -2063,21 +2075,21 @@ var InspectorActor = protocol.ActorClass
     if (this._walkerPromise) {
       return this._walkerPromise;
     }
 
     let deferred = promise.defer();
     this._walkerPromise = deferred.promise;
 
     let window = this.window;
-
     var domReady = () => {
       let tabActor = this.tabActor;
       window.removeEventListener("DOMContentLoaded", domReady, true);
-      deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
+      this.walker = WalkerActor(this.conn, tabActor, options);
+      deferred.resolve(this.walker);
     };
 
     if (window.document.readyState === "loading") {
       window.addEventListener("DOMContentLoaded", domReady, true);
     } else {
       domReady();
     }
 
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -540,16 +540,27 @@ BrowserTabActor.prototype = {
       return this.browser;
     } else if (this.browser instanceof Ci.nsIDOMElement) {
       return this.browser.contentWindow;
     } else {
       return null;
     }
   },
 
+  /**
+   * Getter for the best nsIWebProgress for to watching this window.
+   */
+  get webProgress() {
+    return this.window
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDocShell)
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIWebProgress);
+  },
+
   grip: function BTA_grip() {
     dbg_assert(!this.exited,
                "grip() shouldn't be called on exited browser actor.");
     dbg_assert(this.actorID,
                "tab should have an actorID.");
 
     let response = {
       actor: this.actorID,
--- a/toolkit/devtools/server/transport.js
+++ b/toolkit/devtools/server/transport.js
@@ -252,17 +252,21 @@ LocalDebuggerTransport.prototype = {
     if (this.other) {
       // Remove the reference to the other endpoint before calling close(), to
       // avoid infinite recursion.
       let other = this.other;
       delete this.other;
       other.close();
     }
     if (this.hooks) {
-      this.hooks.onClosed();
+      try {
+        this.hooks.onClosed();
+      } catch(ex) {
+        Components.utils.reportError(ex);
+      }
       this.hooks = null;
     }
   },
 
   /**
    * An empty method for emulating the DebuggerTransport API.
    */
   ready: function LDT_ready() {},