Bug 990074: Sources linked via the optionsURL in install.rdf do not show up in addon debugger. r=fitzgen
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 16 Apr 2014 14:14:50 -0700
changeset 197418 e666661da03c8dbe32f9e0a6f28da4275a13489d
parent 197417 7a915afcb0d4b1ed65e940cf237e534141dbe816
child 197419 6f49fe82d009aeca4105acf7db368312642542ef
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs990074
milestone31.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 990074: Sources linked via the optionsURL in install.rdf do not show up in addon debugger. r=fitzgen
browser/devtools/debugger/test/addon-source/browser_dbg_addon4/chrome.manifest
browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test.xul
browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test2.xul
browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul.js
browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul2.js
browser/devtools/debugger/test/addon-source/browser_dbg_addon5/chrome.manifest
browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test.xul
browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test2.xul
browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul.js
browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul2.js
browser/devtools/debugger/test/addon4.xpi
browser/devtools/debugger/test/addon5.xpi
browser/devtools/debugger/test/browser_dbg_addon-modules-unpacked.js
browser/devtools/debugger/test/browser_dbg_addon-modules.js
browser/devtools/debugger/test/head.js
toolkit/devtools/server/actors/script.js
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/chrome.manifest
@@ -0,0 +1,1 @@
+content browser_dbg_addon4 .
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul.js"/>
+  <label value="test.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test2.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul2.js"/>
+  <label value="test2.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul2.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/chrome.manifest
@@ -0,0 +1,1 @@
+content browser_dbg_addon5 .
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul.js"/>
+  <label value="test.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test2.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul2.js"/>
+  <label value="test2.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul2.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
index a654b7e011b029859b2be10071d13ecb4e9e78eb..31c5331b69dee6d15183647bb598f90195ff1c2a
GIT binary patch
literal 2918
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
index 2c18f6da191746c1a7c658087fbce264a410e5a6..16991f7a06aeaf2bb32a045d7c925f1d41372d9e
GIT binary patch
literal 2933
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
TQ9~Jlk7+Uk6j1phZ2RnQ9pEq>
--- a/browser/devtools/debugger/test/browser_dbg_addon-modules-unpacked.js
+++ b/browser/devtools/debugger/test/browser_dbg_addon-modules-unpacked.js
@@ -3,46 +3,59 @@
 
 // Make sure the add-on actor can see loaded JS Modules from an add-on
 
 const ADDON_URL = EXAMPLE_URL + "addon5.xpi";
 
 function test() {
   Task.spawn(function () {
     let addon = yield addAddon(ADDON_URL);
+    let tab1 = yield addTab("chrome://browser_dbg_addon5/content/test.xul");
+
     let addonDebugger = yield initAddonDebugger(ADDON_URL);
 
     is(addonDebugger.title, "Debugger - Test unpacked add-on with JS Modules", "Saw the right toolbox title.");
 
     // Check the inital list of sources is correct
     let groups = yield addonDebugger.getSourceGroups();
     is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
 
     let sources = groups[0].sources;
-    is(sources.length, 2, "Should be two sources");
-    ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
-    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
-    is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
-    is(sources[1].label, "test.jsm", "correct label for addon code")
-
-    // Load a new module and check it appears in the list of sources
-    Cu.import("resource://browser_dbg_addon5/test2.jsm", {});
-
-    groups = yield addonDebugger.getSourceGroups();
-    is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
-
-    sources = groups[0].sources;
     is(sources.length, 3, "Should be three sources");
     ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
     is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
     is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
     is(sources[1].label, "test.jsm", "correct label for addon code")
-    is(sources[2].url, "resource://browser_dbg_addon5/test2.jsm", "correct url for addon code")
-    is(sources[2].label, "test2.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon5/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+
+    // Load a new module and tab and check they appear in the list of sources
+    Cu.import("resource://browser_dbg_addon5/test2.jsm", {});
+    let tab2 = yield addTab("chrome://browser_dbg_addon5/content/test2.xul");
+
+    groups = yield addonDebugger.getSourceGroups();
+    is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
+
+    sources = groups[0].sources;
+    is(sources.length, 5, "Should be five sources");
+    ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
+    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
+    is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
+    is(sources[1].label, "test.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon5/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+    is(sources[3].url, "resource://browser_dbg_addon5/test2.jsm", "correct url for addon code")
+    is(sources[3].label, "test2.jsm", "correct label for addon code")
+    is(sources[4].url, "chrome://browser_dbg_addon5/content/testxul2.js", "correct url for addon tab code")
+    is(sources[4].label, "testxul2.js", "correct label for addon tab code")
 
     Cu.unload("resource://browser_dbg_addon5/test2.jsm");
     yield addonDebugger.destroy();
+    yield removeTab(tab1);
+    yield removeTab(tab2);
     yield removeAddon(addon);
     finish();
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_addon-modules.js
+++ b/browser/devtools/debugger/test/browser_dbg_addon-modules.js
@@ -3,46 +3,59 @@
 
 // Make sure the add-on actor can see loaded JS Modules from an add-on
 
 const ADDON_URL = EXAMPLE_URL + "addon4.xpi";
 
 function test() {
   Task.spawn(function () {
     let addon = yield addAddon(ADDON_URL);
+    let tab1 = yield addTab("chrome://browser_dbg_addon4/content/test.xul");
+
     let addonDebugger = yield initAddonDebugger(ADDON_URL);
 
     is(addonDebugger.title, "Debugger - Test add-on with JS Modules", "Saw the right toolbox title.");
 
     // Check the inital list of sources is correct
     let groups = yield addonDebugger.getSourceGroups();
     is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
 
     let sources = groups[0].sources;
-    is(sources.length, 2, "Should be two sources");
-    ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
-    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
-    is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
-    is(sources[1].label, "test.jsm", "correct label for addon code")
-
-    // Load a new module and check it appears in the list of sources
-    Cu.import("resource://browser_dbg_addon4/test2.jsm", {});
-
-    groups = yield addonDebugger.getSourceGroups();
-    is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
-
-    sources = groups[0].sources;
     is(sources.length, 3, "Should be three sources");
     ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
     is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
     is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
     is(sources[1].label, "test.jsm", "correct label for addon code")
-    is(sources[2].url, "resource://browser_dbg_addon4/test2.jsm", "correct url for addon code")
-    is(sources[2].label, "test2.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon4/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+
+    // Load a new module and tab and check they appear in the list of sources
+    Cu.import("resource://browser_dbg_addon4/test2.jsm", {});
+    let tab2 = yield addTab("chrome://browser_dbg_addon4/content/test2.xul");
+
+    groups = yield addonDebugger.getSourceGroups();
+    is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
+
+    sources = groups[0].sources;
+    is(sources.length, 5, "Should be five sources");
+    ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
+    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
+    is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
+    is(sources[1].label, "test.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon4/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+    is(sources[3].url, "resource://browser_dbg_addon4/test2.jsm", "correct url for addon code")
+    is(sources[3].label, "test2.jsm", "correct label for addon code")
+    is(sources[4].url, "chrome://browser_dbg_addon4/content/testxul2.js", "correct url for addon tab code")
+    is(sources[4].label, "testxul2.js", "correct label for addon tab code")
 
     Cu.unload("resource://browser_dbg_addon4/test2.jsm");
     yield addonDebugger.destroy();
+    yield removeTab(tab1);
+    yield removeTab(tab2);
     yield removeAddon(addon);
     finish();
   });
 }
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -113,17 +113,21 @@ function addAddon(aUrl) {
 
   let deferred = promise.defer();
 
   AddonManager.getInstallForURL(aUrl, aInstaller => {
     aInstaller.install();
     let listener = {
       onInstallEnded: function(aAddon, aAddonInstall) {
         aInstaller.removeListener(listener);
-        deferred.resolve(aAddonInstall);
+
+        // Wait for add-on's startup scripts to execute. See bug 997408
+        executeSoon(function() {
+          deferred.resolve(aAddonInstall);
+        });
       }
     };
     aInstaller.addListener(listener);
   }, "application/x-xpinstall");
 
   return deferred.promise;
 }
 
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -19,18 +19,19 @@ let addonManager = null;
 
 /**
  * This is a wrapper around amIAddonManager.mapURIToAddonID which always returns
  * false on B2G to avoid loading the add-on manager there and reports any
  * exceptions rather than throwing so that the caller doesn't have to worry
  * about them.
  */
 function mapURIToAddonID(uri, id) {
-  if (Services.appinfo.ID == B2G_ID)
+  if (Services.appinfo.ID == B2G_ID) {
     return false;
+  }
 
   if (!addonManager) {
     addonManager = Cc["@mozilla.org/addons/integration;1"].
                    getService(Ci.amIAddonManager);
   }
 
   try {
     return addonManager.mapURIToAddonID(uri, id);
@@ -4772,29 +4773,56 @@ function AddonThreadActor(aConnect, aHoo
 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",
 
+  onAttach: function(aRequest) {
+    if (!this.attached) {
+      Services.obs.addObserver(this, "document-element-inserted", false);
+    }
+    return ThreadActor.prototype.onAttach.call(this, aRequest);
+  },
+
+  disconnect: function() {
+    if (this.attached) {
+      Services.obs.removeObserver(this, "document-element-inserted");
+    }
+    return ThreadActor.prototype.disconnect.call(this);
+  },
+
+  /**
+   * Called when a new DOM document element is created. Check if the DOM was
+   * laoded from an add-on and if so make the window a debuggee.
+   */
+  observe: function(aSubject, aTopic, aData) {
+    let id = {};
+    if (mapURIToAddonID(aSubject.documentURIObject, id) && id.value === this.addonID) {
+      this.dbg.addDebuggee(aSubject.defaultView);
+    }
+  },
+
   /**
    * 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: function(aSourceURL) {
     // Hide eval scripts
-    if (!aSourceURL)
+    if (!aSourceURL) {
       return false;
+    }
 
     // XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it
-    if (aSourceURL == "resource://gre/modules/addons/XPIProvider.jsm")
+    if (aSourceURL == "resource://gre/modules/addons/XPIProvider.jsm") {
       return false;
+    }
 
     return true;
   },
 
   /**
    * 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
@@ -4831,44 +4859,72 @@ update(AddonThreadActor.prototype, {
   },
 
   /**
    * Checks if the provided global belongs to the debugged add-on.
    *
    * @param aGlobal Debugger.Object
    */
   _checkGlobal: function ADA_checkGlobal(aGlobal) {
+    let obj = null;
+    try {
+      obj = aGlobal.unsafeDereference();
+    }
+    catch (e) {
+      // Because of bug 991399 we sometimes get bad objects here. If we can't
+      // dereference them then they won't be useful to us
+      return false;
+    }
+
     try {
       // This will fail for non-Sandbox objects, hence the try-catch block.
-      let metadata = Cu.getSandboxMetadata(aGlobal.unsafeDereference());
-      if (metadata)
+      let metadata = Cu.getSandboxMetadata(obj);
+      if (metadata) {
         return metadata.addonID === this.addonID;
+      }
     } catch (e) {
     }
 
+    if (obj instanceof Ci.nsIDOMWindow) {
+      let id = {};
+      if (mapURIToAddonID(obj.document.documentURIObject, id)) {
+        return id.value === this.addonID;
+      }
+      return false;
+    }
+
     // Check the global for a __URI__ property and then try to map that to an
     // add-on
     let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
-    if (uridescriptor && "value" in uridescriptor) {
+    if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
+      let uri;
       try {
-        let uri = Services.io.newURI(uridescriptor.value, null, null);
-        let id = {};
-        if (mapURIToAddonID(uri, id)) {
-          return id.value === this.addonID;
-        }
+        uri = Services.io.newURI(uridescriptor.value, null, null);
       }
       catch (e) {
-        DevToolsUtils.reportException("AddonThreadActor.prototype._checkGlobal", e);
+        DevToolsUtils.reportException("AddonThreadActor.prototype._checkGlobal",
+                                      new Error("Invalid URI: " + uridescriptor.value));
+        return false;
+      }
+
+      let id = {};
+      if (mapURIToAddonID(uri, id)) {
+        return id.value === this.addonID;
       }
     }
 
     return false;
   }
 });
 
+AddonThreadActor.prototype.requestTypes = Object.create(ThreadActor.prototype.requestTypes);
+update(AddonThreadActor.prototype.requestTypes, {
+  "attach": AddonThreadActor.prototype.onAttach
+});
+
 /**
  * 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;
   this._useSourceMaps = aUseSourceMaps;