Bug 749316 - Put Debugger object into chrome scratchpad. r=dcamp,bholley.
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 01 May 2012 10:17:32 -0500
changeset 93145 421f51f36a75c579e9bc30b7d2cfd038fc963e4d
parent 93144 5e80edf4c2dd22745d6cb895b2f3fa36741e16dc
child 93146 963e2482837b84b2f66214d941464eaa6d49e8ba
push idunknown
push userunknown
push dateunknown
reviewersdcamp, bholley
bugs749316
milestone15.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 749316 - Put Debugger object into chrome scratchpad. r=dcamp,bholley.
browser/devtools/scratchpad/scratchpad.js
js/ductwork/debugger/IJSDebugger.idl
js/ductwork/debugger/JSDebugger.cpp
js/ductwork/debugger/jsdebugger.jsm
js/ductwork/debugger/tests/test_nativewrappers.js
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
toolkit/devtools/debugger/server/dbg-server.js
toolkit/devtools/debugger/tests/unit/test_getyoungestframe.js
toolkit/devtools/debugger/tests/unit/test_nativewrappers.js
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -56,16 +56,17 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/PropertyPanel.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
+Cu.import("resource://gre/modules/jsdebugger.jsm");
 
 
 const SCRATCHPAD_CONTEXT_CONTENT = 1;
 const SCRATCHPAD_CONTEXT_BROWSER = 2;
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const BUTTON_POSITION_SAVE = 0;
 const BUTTON_POSITION_CANCEL = 1;
@@ -290,16 +291,17 @@ var Scratchpad = {
       return;
     }
 
     if (!this._chromeSandbox ||
         this.browserWindow != this._previousBrowserWindow) {
       this._chromeSandbox = new Cu.Sandbox(this.browserWindow,
         { sandboxPrototype: this.browserWindow, wantXrays: false, 
           sandboxName: 'scratchpad-chrome'});
+      addDebuggerToGlobal(this._chromeSandbox);
 
       this._previousBrowserWindow = this.browserWindow;
     }
 
     return this._chromeSandbox;
   },
 
   /**
--- a/js/ductwork/debugger/IJSDebugger.idl
+++ b/js/ductwork/debugger/IJSDebugger.idl
@@ -35,18 +35,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 /**
  * Do not use this interface. Instead, write:
  *     Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+ *     addDebuggerToGlobal(global);
  */
-[scriptable, uuid(2fc14cc6-4ed0-4bbf-a7dd-e535bf088eb5)]
+[scriptable, uuid(a36fa816-31da-4b23-bc97-6412771f0867)]
 interface IJSDebugger : nsISupports
 {
   /**
-   * Define the global Debugger constructor.
+   * Define the global Debugger constructor on a given global.
    */
   [implicit_jscontext]
-  void addClass();
+  void addClass(in jsval global);
 };
--- a/js/ductwork/debugger/JSDebugger.cpp
+++ b/js/ductwork/debugger/JSDebugger.cpp
@@ -64,27 +64,41 @@ JSDebugger::JSDebugger()
 {
 }
 
 JSDebugger::~JSDebugger()
 {
 }
 
 NS_IMETHODIMP
-JSDebugger::AddClass(JSContext *cx)
+JSDebugger::AddClass(const JS::Value &global, JSContext* cx)
 {
   nsresult rv;
   nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
 
-  JSObject* global = JS_GetGlobalForScopeChain(cx);
-  if (!global) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (!global.isObject()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  
+  JSObject* obj = &global.toObject();
+  obj = JS_UnwrapObjectAndInnerize(obj);
+  if (!obj) {
+    return NS_ERROR_FAILURE;
   }
 
-  if (!JS_DefineDebuggerObject(cx, global)) {
+  JSAutoEnterCompartment aec;
+  if (!aec.enter(cx, obj)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (JS_GetGlobalForObject(cx, obj) != obj) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!JS_DefineDebuggerObject(cx, obj)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 }
 }
--- a/js/ductwork/debugger/jsdebugger.jsm
+++ b/js/ductwork/debugger/jsdebugger.jsm
@@ -31,25 +31,27 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-let EXPORTED_SYMBOLS = [ "Debugger" ];
+let EXPORTED_SYMBOLS = [ "addDebuggerToGlobal" ];
 
 /*
  * This is the js module for Debugger. Import it like so:
  *   Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+ *   addDebuggerToGlobal(this);
  *
  * This will create a 'Debugger' object, which provides an interface to debug
  * JavaScript code running in other compartments in the same process, on the
  * same thread.
  *
  * For documentation on the API, see:
  *   https://wiki.mozilla.org/Debugger
  */
 
-// Initialize the Debugger object. You do not need to do this yourself.
 const init = Components.classes["@mozilla.org/jsdebugger;1"].createInstance(Components.interfaces.IJSDebugger);
-init.addClass();
+function addDebuggerToGlobal(global) {
+  init.addClass(global);
+};
--- a/js/ductwork/debugger/tests/test_nativewrappers.js
+++ b/js/ductwork/debugger/tests/test_nativewrappers.js
@@ -1,11 +1,12 @@
 function run_test()
 {
   Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+  addDebuggerToGlobal(this);
   var g = testGlobal("test1");
 
   var dbg = new Debugger();
   dbg.addDebuggee(g);
   dbg.onDebuggerStatement = function(aFrame) {
     let args = aFrame["arguments"];
     try {
       args[0];
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1770,16 +1770,22 @@ JS_DumpCompartmentPCCounts(JSContext *cx
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_UnwrapObject(JSObject *obj)
 {
     return UnwrapObject(obj);
 }
 
+JS_PUBLIC_API(JSObject *)
+JS_UnwrapObjectAndInnerize(JSObject *obj)
+{
+    return UnwrapObject(obj, /* stopAtOuter = */ false);
+}
+
 JS_FRIEND_API(JSBool)
 js_CallContextDebugHandler(JSContext *cx)
 {
     ScriptFrameIter iter(cx);
     JS_ASSERT(!iter.done());
 
     jsval rval;
     switch (js::CallContextDebugHandler(cx, iter.script(), iter.pc(), &rval)) {
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -545,15 +545,18 @@ extern JS_PUBLIC_API(void)
 JS_DumpPCCounts(JSContext *cx, JSScript *script);
 
 extern JS_PUBLIC_API(void)
 JS_DumpCompartmentPCCounts(JSContext *cx);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_UnwrapObject(JSObject *obj);
 
+extern JS_PUBLIC_API(JSObject *)
+JS_UnwrapObjectAndInnerize(JSObject *obj);
+
 /* Call the context debug handler on the topmost scripted frame. */
 extern JS_FRIEND_API(JSBool)
 js_CallContextDebugHandler(JSContext *cx);
 
 JS_END_EXTERN_C
 
 #endif /* jsdbgapi_h___ */
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -46,16 +46,19 @@
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const CC = Components.Constructor;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
+Cu.import("resource://gre/modules/jsdebugger.jsm");
+addDebuggerToGlobal(this);
+
 function dumpn(str) {
   if (wantLogging) {
     dump("DBG-SERVER: " + str + "\n");
   }
 }
 
 function dbg_assert(cond, e) {
   if (!cond) {
@@ -92,23 +95,16 @@ var DebuggerServer = {
   /**
    * Initialize the debugger server.
    */
   init: function DH_init() {
     if (this.initialized) {
       return;
     }
 
-    // Hack: Merely loading jsdebugger.jsm will not work, because it will load
-    // in the chrome compartment, and then we'd get a cross-compartment wrapper
-    // of that. The Debugger object must be created in the sandbox compartment,
-    // that is, this file's compartment.
-    const init = Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger);
-    init.addClass();  // adds global variable Debugger to this global.
-
     this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
     this.initTransport();
     this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
   },
 
   /**
    * Initialize the debugger server's transport variables.  This can be
    * in place of init() for cases where the jsdebugger isn't needed.
--- a/toolkit/devtools/debugger/tests/unit/test_getyoungestframe.js
+++ b/toolkit/devtools/debugger/tests/unit/test_getyoungestframe.js
@@ -1,11 +1,12 @@
 function run_test()
 {
   Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+  addDebuggerToGlobal(this);
   var xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
   var g = testGlobal("test1");
 
   var dbg = new Debugger();
   dbg.addDebuggee(g);
   dbg.onDebuggerStatement = function(aFrame) {
     do_check_true(aFrame === dbg.getNewestFrame());
     // Execute from the nested event loop, dbg.getNewestFrame() won't
--- a/toolkit/devtools/debugger/tests/unit/test_nativewrappers.js
+++ b/toolkit/devtools/debugger/tests/unit/test_nativewrappers.js
@@ -1,11 +1,12 @@
 function run_test()
 {
   Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+  addDebuggerToGlobal(this);
   var g = testGlobal("test1");
 
   var dbg = new Debugger();
   dbg.addDebuggee(g);
   dbg.onDebuggerStatement = function(aFrame) {
     let args = aFrame.arguments;
     try {
       args[0];