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 id25206
push userryanvm@gmail.com
push dateTue, 03 Sep 2013 20:06:30 +0000
treeherdermozilla-central@8c04d40e218d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp
bugs911127
milestone26.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 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