browser/devtools/commandline/CmdCalllogChrome.jsm
author Ed Morley <emorley@mozilla.com>
Tue, 30 Oct 2012 17:02:31 +0000
changeset 111799 ad1d720d82b7f84d3c7e50f4b02b7c3201662ddb
parent 111786 1545e91c658ec9cd25d9750a43bbe7afd8057cf9
child 111803 67cb43bb8865ecbcb79c2ea04a0494fd223cc69d
permissions -rw-r--r--
Backout a145ded68994, e0cf397089ec & 1545e91c658e (bug 798491) for bustage on a CLOSED TREE

/* 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/. */


let EXPORTED_SYMBOLS = [ ];

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;

Cu.import("resource:///modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
                                  "resource:///modules/HUDService.jsm");

XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
  let JsDebugger = {};
  Cu.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);

  let global = Components.utils.getGlobalForObject({});
  JsDebugger.addDebuggerToGlobal(global);

  return global.Debugger;
});

let debuggers = [];
let sandboxes = [];

/**
 * 'calllog chromestart' command
 */
gcli.addCommand({
  name: "calllog chromestart",
  description: gcli.lookup("calllogChromeStartDesc"),
  get hidden() gcli.hiddenByChromePref(),
  params: [
    {
      name: "sourceType",
      type: {
        name: "selection",
        data: ["content-variable", "chrome-variable", "jsm", "javascript"]
      }
    },
    {
      name: "source",
      type: "string",
      description: gcli.lookup("calllogChromeSourceTypeDesc"),
      manual: gcli.lookup("calllogChromeSourceTypeManual"),
    }
  ],
  exec: function(args, context) {
    let globalObj;
    let contentWindow = context.environment.contentDocument.defaultView;

    if (args.sourceType == "jsm") {
      try {
        globalObj = Cu.import(args.source);
      }
      catch (e) {
        return gcli.lookup("callLogChromeInvalidJSM");
      }
    } else if (args.sourceType == "content-variable") {
      if (args.source in contentWindow) {
        globalObj = Cu.getGlobalForObject(contentWindow[args.source]);
      } else {
        throw new Error(gcli.lookup("callLogChromeVarNotFoundContent"));
      }
    } else if (args.sourceType == "chrome-variable") {
      let chromeWin = context.environment.chromeDocument.defaultView;
      if (args.source in chromeWin) {
        globalObj = Cu.getGlobalForObject(chromeWin[args.source]);
      } else {
        return gcli.lookup("callLogChromeVarNotFoundChrome");
      }
    } else {
      let chromeWin = context.environment.chromeDocument.defaultView;
      let sandbox = new Cu.Sandbox(chromeWin,
                                   {
                                     sandboxPrototype: chromeWin,
                                     wantXrays: false,
                                     sandboxName: "gcli-cmd-calllog-chrome"
                                   });
      let returnVal;
      try {
        returnVal = Cu.evalInSandbox(args.source, sandbox, "ECMAv5");
        sandboxes.push(sandbox);
      } catch(e) {
        // We need to save the message before cleaning up else e contains a dead
        // object.
        let msg = gcli.lookup("callLogChromeEvalException") + ": " + e;
        Cu.nukeSandbox(sandbox);
        return msg;
      }

      if (typeof returnVal == "undefined") {
        return gcli.lookup("callLogChromeEvalNeedsObject");
      }

      globalObj = Cu.getGlobalForObject(returnVal);
    }

    let dbg = new Debugger(globalObj);
    debuggers.push(dbg);

    dbg.onEnterFrame = function(frame) {
      // BUG 773652 -  Make the output from the GCLI calllog command nicer
      contentWindow.console.log(gcli.lookup("callLogChromeMethodCall") +
                                ": " + this.callDescription(frame));
    }.bind(this);

    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab;
    HUDService.activateHUDForContext(tab);

    return gcli.lookup("calllogChromeStartReply");
  },

  valueToString: function(value) {
    if (typeof value !== "object" || value === null)
      return uneval(value);
    return "[object " + value.class + "]";
  },

  callDescription: function(frame) {
    let name = frame.callee.name || gcli.lookup("callLogChromeAnonFunction");
    let args = frame.arguments.map(this.valueToString).join(", ");
    return name + "(" + args + ")";
  }
});

/**
 * 'calllog chromestop' command
 */
gcli.addCommand({
  name: "calllog chromestop",
  description: gcli.lookup("calllogChromeStopDesc"),
  get hidden() gcli.hiddenByChromePref(),
  exec: function(args, context) {
    let numDebuggers = debuggers.length;
    if (numDebuggers == 0) {
      return gcli.lookup("calllogChromeStopNoLogging");
    }

    for (let dbg of debuggers) {
      dbg.onEnterFrame = undefined;
      dbg.enabled = false;
    }
    for (let sandbox of sandboxes) {
      Cu.nukeSandbox(sandbox);
    }
    debuggers = [];
    sandboxes = [];

    return gcli.lookupFormat("calllogChromeStopReply", [ numDebuggers ]);
  }
});