Bug 899052 - Implement Addon Thread Actor. r=fitzgen, r=shu
authorJordan Santell <jsantell@gmail.com>
Fri, 07 Mar 2014 00:09:14 -0800
changeset 172805 c6a1cc77c943be2c08dfbbdaf89cc42b6838e131
parent 172727 923f1411f42f722cac39cbd5ad381e92ef9bb436
child 172806 80ea77dd1eac3ee9d4118e104630d0a2c6156a02
push id26382
push userryanvm@gmail.com
push dateMon, 10 Mar 2014 21:09:54 +0000
treeherdermozilla-central@41d962d23e81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen, shu
bugs899052
milestone30.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 899052 - Implement Addon Thread Actor. r=fitzgen, r=shu
browser/devtools/debugger/test/addon-source/browser_dbg_addon3/lib/main.js
browser/devtools/debugger/test/addon-source/browser_dbg_addon3/package.json
browser/devtools/debugger/test/addon3.xpi
browser/devtools/debugger/test/browser.ini
browser/devtools/debugger/test/browser_dbg_addonactor.js
browser/devtools/debugger/test/head.js
js/src/vm/Debugger.cpp
toolkit/components/telemetry/Histograms.json
toolkit/devtools/client/dbg-client.jsm
toolkit/devtools/server/actors/script.js
toolkit/devtools/server/actors/webbrowser.js
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon3/lib/main.js
@@ -0,0 +1,13 @@
+var { Cc, Ci } = require("chrome");
+var { once } = require("sdk/system/events");
+
+var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+var observer = {
+  observe: function () {
+    debugger;
+  }
+};
+
+once("sdk:loader:destroy", () => observerService.removeObserver(observer, "debuggerAttached"));
+
+observerService.addObserver(observer, "debuggerAttached", false);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon3/package.json
@@ -0,0 +1,9 @@
+{
+  "name": "browser_dbg_addon3",
+  "title": "browser_dbg_addon3",
+  "id": "jid1-ami3akps3baaeg",
+  "description": "a basic add-on",
+  "author": "",
+  "license": "MPL 2.0",
+  "version": "0.1"
+}
new file mode 100644
index 0000000000000000000000000000000000000000..673b31b9d1748647f72e94801e16591fecf7122b
GIT binary patch
literal 7423
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
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   addon1.xpi
   addon2.xpi
+  addon3.xpi
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_blackboxing_blackboxme.js
   code_blackboxing_one.js
   code_blackboxing_three.js
   code_blackboxing_two.js
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -75,16 +76,17 @@ support-files =
   doc_watch-expressions.html
   doc_watch-expression-button.html
   doc_with-frame.html
   head.js
   sjs_random-javascript.sjs
   testactors.js
 
 [browser_dbg_aaa_run_first_leaktest.js]
+[browser_dbg_addonactor.js]
 [browser_dbg_auto-pretty-print-01.js]
 [browser_dbg_auto-pretty-print-02.js]
 [browser_dbg_bfcache.js]
 [browser_dbg_blackboxing-01.js]
 [browser_dbg_blackboxing-02.js]
 [browser_dbg_blackboxing-03.js]
 [browser_dbg_blackboxing-04.js]
 [browser_dbg_blackboxing-05.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_addonactor.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Make sure we can attach to addon actors.
+
+const ADDON3_URL = EXAMPLE_URL + "addon3.xpi";
+const ADDON_MODULE_URL = "resource://jid1-ami3akps3baaeg-at-jetpack/browser_dbg_addon3/lib/main.js";
+
+var gAddon, gClient, gThreadClient;
+
+function test() {
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init(() => true);
+    DebuggerServer.addBrowserActors();
+  }
+
+  let transport = DebuggerServer.connectPipe();
+  gClient = new DebuggerClient(transport);
+  gClient.connect((aType, aTraits) => {
+    is(aType, "browser",
+      "Root actor should identify itself as a browser.");
+
+    installAddon()
+      .then(attachAddonActorForUrl.bind(null, gClient, ADDON3_URL))
+      .then(attachAddonThread)
+      .then(testDebugger)
+      .then(testSources)
+      .then(uninstallAddon)
+      .then(closeConnection)
+      .then(finish)
+      .then(null, aError => {
+        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      });
+  });
+}
+
+function installAddon () {
+  return addAddon(ADDON3_URL).then(aAddon => {
+    gAddon = aAddon;
+  });
+}
+
+function attachAddonThread ([aGrip, aResponse]) {
+  info("attached addon actor for URL");
+  let deferred = promise.defer();
+
+  gClient.attachThread(aResponse.threadActor, (aResponse, aThreadClient) => {
+    info("attached thread");
+    gThreadClient = aThreadClient;
+    gThreadClient.resume(deferred.resolve);
+  });
+  return deferred.promise;
+}
+
+function testDebugger() {
+  info('Entering testDebugger');
+  let deferred = promise.defer();
+
+  once(gClient, "paused").then(() => {
+    ok(true, "Should be able to attach to addon actor");
+    gThreadClient.resume(deferred.resolve)
+  });
+
+  Services.obs.notifyObservers(null, "debuggerAttached", null);
+
+  return deferred.promise;
+}
+
+function testSources() {
+  let deferred = promise.defer();
+
+  gThreadClient.getSources(aResponse => {
+    // source URLs contain launch-specific temporary directory path,
+    // hence the ".contains" call.
+    const matches = aResponse.sources.filter(s =>
+      s.url.contains(ADDON_MODULE_URL));
+    is(matches.length, 1,
+      "the main script of the addon is present in the source list");
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
+
+function uninstallAddon() {
+  return removeAddon(gAddon);
+}
+
+function closeConnection () {
+  let deferred = promise.defer();
+  gClient.close(deferred.resolve);
+  return deferred.promise;
+}
+
+registerCleanupFunction(function() {
+  gClient = null;
+  gAddon = null;
+  gThreadClient = null;
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -149,16 +149,17 @@ function getTabActorForUrl(aClient, aUrl
     let tabActor = aResponse.tabs.filter(aGrip => aGrip.url == aUrl).pop();
     deferred.resolve(tabActor);
   });
 
   return deferred.promise;
 }
 
 function getAddonActorForUrl(aClient, aUrl) {
+  info("Get addon actor for URL: " + aUrl);
   let deferred = promise.defer();
 
   aClient.listAddons(aResponse => {
     let addonActor = aResponse.addons.filter(aGrip => aGrip.url == aUrl).pop();
     deferred.resolve(addonActor);
   });
 
   return deferred.promise;
@@ -656,8 +657,19 @@ function stopTracing(aPanel) {
 function filterTraces(aPanel, f) {
   const traces = aPanel.panelWin.document
     .getElementById("tracer-traces")
     .querySelector("scrollbox")
     .children;
   return Array.filter(traces, f);
 }
 
+function attachAddonActorForUrl(aClient, aUrl) {
+  let deferred = promise.defer();
+
+  getAddonActorForUrl(aClient, aUrl).then(aGrip => {
+    aClient.attachAddon(aGrip.actor, aResponse => {
+      deferred.resolve([aGrip, aResponse]);
+    });
+  });
+
+  return deferred.promise;
+}
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2741,19 +2741,26 @@ Debugger::findAllGlobals(JSContext *cx, 
 {
     THIS_DEBUGGER(cx, argc, vp, "findAllGlobals", args, dbg);
 
     RootedObject result(cx, NewDenseEmptyArray(cx));
     if (!result)
         return false;
 
     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+        if (c->options().invisibleToDebugger())
+            continue;
+
         c->zone()->scheduledForDestruction = false;
 
         GlobalObject *global = c->maybeGlobal();
+
+        if (cx->runtime()->isSelfHostingGlobal(global))
+            continue;
+
         if (global) {
             /*
              * We pulled |global| out of nowhere, so it's possible that it was
              * marked gray by XPConnect. Since we're now exposing it to JS code,
              * we need to mark it black.
              */
             JS::ExposeGCThingToActiveJS(global, JSTRACE_OBJECT);
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4896,16 +4896,30 @@
   },
   "DEVTOOLS_DEBUGGER_RDP_REMOTE_THREADDETACH_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000",
     "n_buckets": "1000",
     "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
   },
+  "DEVTOOLS_DEBUGGER_RDP_LOCAL_ADDONDETACH_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
+  },
+  "DEVTOOLS_DEBUGGER_RDP_REMOTE_ADDONDETACH_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
+  },
   "DEVTOOLS_DEBUGGER_RDP_LOCAL_TABDETACH_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000",
     "n_buckets": "1000",
     "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
   },
   "DEVTOOLS_DEBUGGER_RDP_REMOTE_TABDETACH_MS": {
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -232,16 +232,17 @@ const UnsolicitedPauses = {
  */
 this.DebuggerClient = function (aTransport)
 {
   this._transport = aTransport;
   this._transport.hooks = this;
 
   // Map actor ID to client instance for each actor type.
   this._threadClients = new Map;
+  this._addonClients = new Map;
   this._tabClients = new Map;
   this._tracerClients = new Map;
   this._consoleClients = new Map;
 
   this._pendingRequests = [];
   this._activeRequests = new Map;
   this._eventsEnabled = true;
 
@@ -408,18 +409,20 @@ DebuggerClient.prototype = {
           }
         });
       }
     };
 
     detachClients(this._consoleClients, () => {
       detachClients(this._threadClients, () => {
         detachClients(this._tabClients, () => {
-          this._transport.close();
-          this._transport = null;
+          detachClients(this._addonClients, () => {
+            this._transport.close();
+            this._transport = null;
+          });
         });
       });
     });
   },
 
   /*
    * This function exists only to preserve DebuggerClient's interface;
    * new code should say 'client.mainRoot.listTabs()'.
@@ -462,16 +465,41 @@ DebuggerClient.prototype = {
         tabClient = new TabClient(this, aResponse);
         this._tabClients.set(aTabActor, tabClient);
       }
       aOnResponse(aResponse, tabClient);
     });
   },
 
   /**
+   * Attach to an addon actor.
+   *
+   * @param string aAddonActor
+   *        The actor ID for the addon to attach.
+   * @param function aOnResponse
+   *        Called with the response packet and a AddonClient
+   *        (which will be undefined on error).
+   */
+  attachAddon: function DC_attachAddon(aAddonActor, aOnResponse) {
+    let packet = {
+      to: aAddonActor,
+      type: "attach"
+    };
+    this.request(packet, aResponse => {
+      let addonClient;
+      if (!aResponse.error) {
+        addonClient = new AddonClient(this, aAddonActor);
+        this._addonClients[aAddonActor] = addonClient;
+        this.activeAddon = addonClient;
+      }
+      aOnResponse(aResponse, addonClient);
+    });
+  },
+
+  /**
    * Attach to a Web Console actor.
    *
    * @param string aConsoleActor
    *        The ID for the console actor to attach to.
    * @param array aListeners
    *        The console listeners you want to start.
    * @param function aOnResponse
    *        Called with the response packet and a WebConsoleClient
@@ -738,17 +766,22 @@ DebuggerClient.prototype = {
     return pool ? pool.get(actorID) : null;
   },
 
   poolFor: function (actorID) {
     for (let pool of this._pools) {
       if (pool.has(actorID)) return pool;
     }
     return null;
-  }
+  },
+
+  /**
+   * Currently attached addon.
+   */
+  activeAddon: null
 }
 
 eventSource(DebuggerClient.prototype);
 
 // Constants returned by `FeatureCompatibilityShim.onPacketTest`.
 const SUPPORTED = 1;
 const NOT_SUPPORTED = 2;
 const SKIP = 3;
@@ -1044,16 +1077,46 @@ TabClient.prototype = {
     options: args(0)
   }, {
     telemetry: "RECONFIGURETAB"
   }),
 };
 
 eventSource(TabClient.prototype);
 
+function AddonClient(aClient, aActor) {
+  this._client = aClient;
+  this._actor = aActor;
+  this.request = this._client.request;
+}
+
+AddonClient.prototype = {
+  get actor() { return this._actor; },
+  get _transport() { return this._client._transport; },
+
+  /**
+   * Detach the client from the addon actor.
+   *
+   * @param function aOnResponse
+   *        Called with the response packet.
+   */
+  detach: DebuggerClient.requester({
+    type: "detach"
+  }, {
+    after: function(aResponse) {
+      if (this._client.activeAddon === this._client._addonClients[this.actor]) {
+        this._client.activeAddon = null
+      }
+      delete this._client._addonClients[this.actor];
+      return aResponse;
+    },
+    telemetry: "ADDONDETACH"
+  })
+};
+
 /**
  * A RootClient object represents a root actor on the server. Each
  * DebuggerClient keeps a RootClient instance representing the root actor
  * for the initial connection; DebuggerClient's 'listTabs' and
  * 'listChildProcesses' methods forward to that root actor.
  *
  * @param aClient object
  *      The client connection to which this actor belongs.
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -4557,16 +4557,106 @@ update(ChromeDebuggerActor.prototype, {
         type: "newGlobal",
         // TODO: after bug 801084 lands see if we need to JSONify this.
         hostAnnotations: aGlobal.hostAnnotations
       });
     }
   }
 });
 
+/**
+ * Creates an actor for handling add-on debugging. AddonThreadActor is
+ * a thin wrapper over ThreadActor.
+ *
+ * @param aConnection object
+ *        The DebuggerServerConnection with which this AddonThreadActor
+ *        is associated. (Currently unused, but required to make this
+ *        constructor usable with addGlobalActor.)
+ *
+ * @param aHooks object
+ *        An object with preNest and postNest methods for calling
+ *        when entering and exiting a nested event loops.
+ *
+ * @param aAddonID string
+ *        ID of the add-on this actor will debug. It will be used to
+ *        filter out globals marked for debugging.
+ */
+
+function AddonThreadActor(aConnect, aHooks, aAddonID) {
+  this.addonID = aAddonID;
+  ThreadActor.call(this, aHooks);
+}
+
+AddonThreadActor.prototype = Object.create(ThreadActor.prototype);
+
+update(AddonThreadActor.prototype, {
+  constructor: AddonThreadActor,
+
+  // A constant prefix that will be used to form the actor ID by the server.
+  actorPrefix: "addonThread",
+
+  /**
+   * Override the eligibility check for scripts and sources to make
+   * sure every script and source with a URL is stored when debugging
+   * add-ons.
+   */
+  _allowSource: (aSourceURL) => !!aSourceURL,
+
+  /**
+   * An object that will be used by ThreadActors to tailor their
+   * behaviour depending on the debugging context being required (chrome,
+   * addon or content). The methods that this object provides must
+   * be bound to the ThreadActor before use.
+   */
+  globalManager: {
+    findGlobals: function ADA_findGlobals() {
+      for (let global of this.dbg.findAllGlobals()) {
+        if (this._checkGlobal(global)) {
+          this.dbg.addDebuggee(global);
+        }
+      }
+    },
+
+    /**
+     * A function that the engine calls when a new global object
+     * has been created.
+     *
+     * @param aGlobal Debugger.Object
+     *        The new global object that was created.
+     */
+    onNewGlobal: function ADA_onNewGlobal(aGlobal) {
+      if (this._checkGlobal(aGlobal)) {
+        this.addDebuggee(aGlobal);
+        // Notify the client.
+        this.conn.send({
+          from: this.actorID,
+          type: "newGlobal",
+          // TODO: after bug 801084 lands see if we need to JSONify this.
+          hostAnnotations: aGlobal.hostAnnotations
+        });
+      }
+    }
+  },
+
+  /**
+   * Checks if the provided global belongs to the debugged add-on.
+   *
+   * @param aGlobal Debugger.Object
+   */
+  _checkGlobal: function ADA_checkGlobal(aGlobal) {
+    let metadata;
+    try {
+      // This will fail for non-Sandbox objects, hence the try-catch block.
+      metadata = Cu.getSandboxMetadata(aGlobal.unsafeDereference());
+    } catch (e) {
+    }
+
+    return metadata && metadata.addonID === this.addonID;
+  }
+});
 
 /**
  * Manages the sources for a thread. Handles source maps, locations in the
  * sources, etc for ThreadActors.
  */
 function ThreadSources(aThreadActor, aUseSourceMaps, aAllowPredicate,
                        aOnNewSource) {
   this._thread = aThreadActor;
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -988,30 +988,40 @@ BrowserAddonList.prototype.onInstalled =
 BrowserAddonList.prototype.onUninstalled = function (aAddon) {
   this._actorByAddonId.delete(aAddon.id);
   this._onListChanged();
 };
 
 function BrowserAddonActor(aConnection, aAddon) {
   this.conn = aConnection;
   this._addon = aAddon;
+  this._contextPool = null;
+  this._threadActor = null;
   AddonManager.addAddonListener(this);
 }
 
 BrowserAddonActor.prototype = {
   actorPrefix: "addon",
 
+  get exited() {
+    return !this._addon;
+  },
+
   get id() {
     return this._addon.id;
   },
 
   get url() {
     return this._addon.sourceURI ? this._addon.sourceURI.spec : undefined;
   },
 
+  get attached() {
+    return this._threadActor;
+  },
+
   form: function BAA_form() {
     dbg_assert(this.actorID, "addon should have an actorID.");
 
     return {
       actor: this.actorID,
       id: this.id,
       url: this.url
     };
@@ -1019,19 +1029,82 @@ BrowserAddonActor.prototype = {
 
   disconnect: function BAA_disconnect() {
     AddonManager.removeAddonListener(this);
   },
 
   onUninstalled: function BAA_onUninstalled(aAddon) {
     if (aAddon != this._addon)
       return;
+
+    if (this.attached) {
+      this.onDetach();
+      this.conn.send({ from: this.actorID, type: "tabDetached" });
+    }
+
     this._addon = null;
     AddonManager.removeAddonListener(this);
   },
+
+  onAttach: function BAA_onAttach() {
+    if (this.exited) {
+      return { type: "exited" };
+    }
+
+    if (!this.attached) {
+      this._contextPool = new ActorPool(this.conn);
+      this.conn.addActorPool(this._contextPool);
+
+      this._threadActor = new AddonThreadActor(this.conn, this,
+                                               this._addon.id);
+      this._contextPool.addActor(this._threadActor);
+    }
+
+    return { type: "tabAttached", threadActor: this._threadActor.actorID };
+  },
+
+  onDetach: function BAA_onDetach() {
+    if (!this.attached) {
+      return { error: "wrongState" };
+    }
+
+    this.conn.removeActorPool(this._contextPool);
+    this._contextPool = null;
+
+    this._threadActor = null;
+
+    return { type: "detached" };
+  },
+
+  preNest: function() {
+    let e = Services.wm.getEnumerator(null);
+    while (e.hasMoreElements()) {
+      let win = e.getNext();
+      let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+      windowUtils.suppressEventHandling(true);
+      windowUtils.suspendTimeouts();
+    }
+  },
+
+  postNest: function() {
+    let e = Services.wm.getEnumerator(null);
+    while (e.hasMoreElements()) {
+      let win = e.getNext();
+      let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+      windowUtils.resumeTimeouts();
+      windowUtils.suppressEventHandling(false);
+    }
+  }
+};
+
+BrowserAddonActor.prototype.requestTypes = {
+  "attach": BrowserAddonActor.prototype.onAttach,
+  "detach": BrowserAddonActor.prototype.onDetach
 };
 
 /**
  * The DebuggerProgressListener object is an nsIWebProgressListener which
  * handles onStateChange events for the inspected browser. If the user tries to
  * navigate away from a paused page, the listener makes sure that the debuggee
  * is resumed before the navigation begins.
  *