Bug 911127 - Repair DebuggerServer loading on B2G. r=dcamp
authorJ. Ryan Stinnett <jryans@gmail.com>
Sat, 31 Aug 2013 02:34:26 +0200
changeset 145286 8e23707fc9751c12a2eae9dbbbce1e34c90a5c33
parent 145285 7ff96bd19c1c6ac6a2ae7460218a01e55badc2bc
child 145287 9f30b801cfa93144649f7d469a1211bd76600ad9
push id2478
push userryanvm@gmail.com
push dateTue, 03 Sep 2013 12:54:48 +0000
treeherderfx-team@8e23707fc975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp
bugs911127
milestone26.0a1
Bug 911127 - Repair DebuggerServer loading on B2G. r=dcamp
toolkit/devtools/server/dbg-server.jsm
toolkit/devtools/server/main.js
--- a/toolkit/devtools/server/dbg-server.jsm
+++ b/toolkit/devtools/server/dbg-server.jsm
@@ -1,26 +1,44 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
-
 /**
  * Loads the remote debugging protocol code into a sandbox, in order to
  * shield it from the debuggee. This way, when debugging chrome globals,
  * debugger and debuggee will be in separate compartments.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-
 this.EXPORTED_SYMBOLS = ["DebuggerServer", "ActorPool"];
 
-let server = devtools.require("devtools/server/main");
+var loadSubScript =
+  "function loadSubScript(aURL)\n" +
+  "{\n" +
+  "const Ci = Components.interfaces;\n" +
+  "const Cc = Components.classes;\n" +
+  "  try {\n" +
+  "    let loader = Cc[\"@mozilla.org/moz/jssubscript-loader;1\"]\n" +
+  "      .getService(Ci.mozIJSSubScriptLoader);\n" +
+  "    loader.loadSubScript(aURL, this);\n" +
+  "  } catch(e) {\n" +
+  "    dump(\"Error loading: \" + aURL + \": \" + e + \" - \" + e.stack + \"\\n\");\n" +
+  "    throw e;\n" +
+  "  }\n" +
+  "}";
 
-this.DebuggerServer = server.DebuggerServer;
-this.ActorPool = server.ActorPool;
+// Load the debugging server in a sandbox with its own compartment.
+var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
+                      .createInstance(Ci.nsIPrincipal);
+
+var gGlobal = Cu.Sandbox(systemPrincipal);
+Cu.evalInSandbox(loadSubScript, gGlobal, "1.8");
+gGlobal.loadSubScript("resource://gre/modules/devtools/server/main.js");
+
+this.DebuggerServer = gGlobal.DebuggerServer;
+this.ActorPool = gGlobal.ActorPool;
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -5,17 +5,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 /**
  * Toolkit glue for the remote debugging protocol, loaded into the
  * debugging global.
  */
 
-const { Ci, Cc, CC, Cu, Cr, components: Components } = require("chrome");
+// |this.require| is used to test if this file was loaded via the devtools
+// loader (as it is in DebuggerProcess.jsm) or via loadSubScript (as it is from
+// dbg-server.jsm).  Note that testing |require| is not safe in either
+// situation, as it causes a ReferenceError.
+var Ci, Cc, CC, Cu, Cr, Components;
+if (this.require) {
+  ({ Ci, Cc, CC, Cu, Cr, components: Components }) = require("chrome");
+} else {
+  ({
+    interfaces: Ci,
+    classes: Cc,
+    Constructor: CC,
+    utils: Cu,
+    results: Cr
+  }) = Components;
+}
+
+// On B2G, if |this.require| is undefined at this point, it remains undefined
+// later on when |DebuggerServer.registerModule| is called.  On desktop (and
+// perhaps other places), if |this.require| starts out undefined, it ends up
+// being set to some native code by the time we get to |registerModule|.  Here
+// we perform a test early on, and then cache the correct require function for
+// later use.
+var localRequire;
+if (this.require) {
+  localRequire = id => require(id);
+} else {
+  let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+  localRequire = id => devtools.require(id);
+}
+
 const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 const promptConnections = Services.prefs.getBoolPref("devtools.debugger.prompt-connection");
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
@@ -30,17 +60,17 @@ function loadSubScript(aURL)
   } catch(e) {
     let errorStr = "Error loading: " + aURL + ": " + e + " - " + e.stack + "\n";
     dump(errorStr);
     Cu.reportError(errorStr);
     throw e;
   }
 }
 
-let loaderRequire = require;
+let loaderRequire = this.require;
 this.require = null;
 loadSubScript.call(this, "resource://gre/modules/commonjs/sdk/core/promise.js");
 this.require = loaderRequire;
 
 Cu.import("resource://gre/modules/devtools/SourceMap.jsm");
 
 loadSubScript.call(this, "resource://gre/modules/devtools/DevToolsUtils.js");
 
@@ -267,17 +297,17 @@ var DebuggerServer = {
    *    'register' and 'unregister' functions.
    */
   registerModule: function(id) {
     if (id in gRegisteredModules) {
       throw new Error("Tried to register a module twice: " + id + "\n");
     }
 
     let moduleAPI = ModuleAPI();
-    let mod = require(id);
+    let mod = localRequire(id);
     mod.register(moduleAPI);
     gRegisteredModules[id] = { module: mod, api: moduleAPI };
   },
 
   /**
    * Returns true if a module id has been registered.
    */
   isModuleRegistered: function(id) {
@@ -613,33 +643,37 @@ var DebuggerServer = {
       let handler = DebuggerServer.globalActorFactories[name];
       if (handler.name == aFunction.name) {
         delete DebuggerServer.globalActorFactories[name];
       }
     }
   }
 };
 
-exports.DebuggerServer = DebuggerServer;
+if (this.exports) {
+  exports.DebuggerServer = DebuggerServer;
+}
 
 /**
  * Construct an ActorPool.
  *
  * ActorPools are actorID -> actor mapping and storage.  These are
  * used to accumulate and quickly dispose of groups of actors that
  * share a lifetime.
  */
 function ActorPool(aConnection)
 {
   this.conn = aConnection;
   this._cleanups = {};
   this._actors = {};
 }
 
-exports.ActorPool = ActorPool;
+if (this.exports) {
+  exports.ActorPool = ActorPool;
+}
 
 ActorPool.prototype = {
   /**
    * Add an actor to the actor pool.  If the actor doesn't have an ID,
    * allocate one from the connection.
    *
    * @param aActor object
    *        The actor implementation.  If the object has a