1
/* See license.txt for terms of usage */
8
function(Firebug, Wrapper, Events) {
10
// ********************************************************************************************* //
14
* Returns a command line object (bundled with passed window through closure). The object
15
* provides all necessary APIs as described here:
16
* http://getfirebug.com/wiki/index.php/Command_Line_API
18
* @param {Object} context
21
function createFirebugCommandLine(context, win)
23
var contentView = Wrapper.getContentView(win);
26
if (FBTrace.DBG_COMMANDLINE || FBTrace.DBG_ERRORS)
27
FBTrace.sysout("createFirebugCommandLine ERROR no contentView "+context.getName())
31
// The commandLine object
36
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
39
// List of command line APIs
40
var commands = ["$", "$$", "$x", "$n", "cd", "clear", "inspect", "keys",
41
"values", "debug", "undebug", "monitor", "unmonitor", "traceCalls", "untraceCalls",
42
"traceAll", "untraceAll", "monitorEvents", "unmonitorEvents", "profile", "profileEnd",
43
"copy", "memoryProfile", "memoryProfileEnd"];
45
// Define command line methods
46
for (var i=0; i<commands.length; i++)
48
var command = commands[i];
50
// If the method is already defined, don't override it.
51
if (contentView[command])
54
function createCommandHandler(cmd) {
56
return notifyFirebug(arguments, cmd, 'firebugExecuteCommand');
60
commandLine[command] = createCommandHandler(command);
61
commandLine.__exposedProps__[command] = "rw";
64
// Define shortcuts for some console methods
65
var consoleShortcuts = ["dir", "dirxml", "table"];
66
for (var i=0; i<consoleShortcuts.length; i++)
68
var command = consoleShortcuts[i];
70
// If the method is already defined, don't override it.
71
if (contentView[command])
74
function createShortcutHandler(cmd) {
76
return contentView.console[cmd].apply(contentView.console, arguments);
80
commandLine[command] = createShortcutHandler(command);
81
commandLine.__exposedProps__[command] = "rw";
84
// Define console variables (inspector history).
85
var props = ["$0", "$1"];
86
for (var i=0; i<props.length; i++)
89
if (contentView[prop])
92
function createVariableHandler(prop) {
94
return notifyFirebug(arguments, prop, 'firebugExecuteCommand');
98
commandLine.__defineGetter__(prop, createVariableHandler(prop));
99
commandLine.__exposedProps__[prop] = "r";
104
// xxxHonza: TODO make this private.
105
commandLine["detachCommandLine"] = detachCommandLine;
106
commandLine.__exposedProps__["detachCommandLine"] = "r";
108
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
109
// Helpers (not accessible from web content)
111
function attachCommandLine()
113
if (FBTrace.DBG_COMMANDLINE)
114
FBTrace.sysout("commandLine.Exposed.attachCommandLine; "+window.location);
116
if (!contentView.console)
118
var console = createFirebugConsole(context, win);
119
contentView.console = console;
122
Events.addEventListener(contentView.document, "firebugCommandLine", firebugEvalEvent, true);
125
function detachCommandLine()
127
Events.removeEventListener(contentView.document, "firebugCommandLine", firebugEvalEvent, true);
128
delete contentView._FirebugCommandLine; // suicide!
130
if (FBTrace.DBG_COMMANDLINE)
131
FBTrace.sysout("commandLine.Exposed.detachCommandLine; "+window.location);
134
function firebugEvalEvent(event)
136
if (FBTrace.DBG_COMMANDLINE)
137
FBTrace.sysout("commandLine.Exposed.firebugEvalEvent "+window.location);
139
var expr = contentView.document.getUserData("firebug-expr"); // see commandLine.js
142
if (FBTrace.DBG_COMMANDLINE)
143
FBTrace.sysout("commandLine.Exposed; did evaluate on "+expr);
146
function evaluate(expr)
150
var line = Components.stack.lineNumber;
151
var result = contentView.eval(expr);
152
notifyFirebug([result], "evaluated", "firebugAppendConsole");
156
// change source and line number of exeptions from commandline code
157
// create new error since properties of nsIXPCException are not modifiable
158
var shouldModify, isXPCException;
159
if (exc.filename == Components.stack.filename)
160
shouldModify = isXPCException = true;
161
else if(exc.fileName == Components.stack.filename)
166
var result = new Error;
168
result.source = expr;
169
result.message = exc.message;
170
result.lineNumber = exc.lineNumber - line;
171
result.fileName = "data:," + encodeURIComponent(expr);
173
result.name = exc.name;
179
notifyFirebug([result], "evaluateError", "firebugAppendConsole");
183
function notifyFirebug(objs, methodName, eventID)
185
var event = contentView.document.createEvent("Events");
186
event.initEvent(eventID, true, false);
188
commandLine.userObjects = [];
189
for (var i=0; i<objs.length; i++)
190
commandLine.userObjects.push(objs[i]);
192
var length = commandLine.userObjects.length;
193
contentView.document.setUserData("firebug-methodName", methodName, null);
195
contentView.document.dispatchEvent(event);
197
if (FBTrace.DBG_COMMANDLINE)
198
FBTrace.sysout("commandLine.Exposed; dispatched event "+methodName+" via "+
199
eventID+" with "+objs.length+ " user objects, [0]:"+commandLine.userObjects[0]);
202
if (contentView.document.getUserData("firebug-retValueType") == "array")
205
if (!result && commandLine.userObjects.length == length+1)
206
return commandLine.userObjects[length];
208
for (var i=length; i<commandLine.userObjects.length && result; i++)
209
result.push(commandLine.userObjects[i]);
217
// ********************************************************************************************* //
220
Firebug.CommandLineExposed =
222
createFirebugCommandLine: createFirebugCommandLine
225
return Firebug.CommandLineExposed;
227
// ********************************************************************************************* //
1
/* See license.txt for terms of usage */
2
/*jshint esnext:true, es5:true, curly:false, evil:true, forin: false*/
3
/*global Firebug:true, FBTrace:true, Components:true, define:true */
7
"firebug/debugger/debuggerLib",
9
"firebug/console/commandLineAPI",
12
function(Wrapper, DebuggerLib, Obj, CommandLineAPI, Locale) {
15
// ********************************************************************************************* //
18
const Cu = Components.utils;
20
// ********************************************************************************************* //
23
// List of command line APIs
24
var commandNames = ["$", "$$", "$n", "$x", "cd", "clear", "inspect", "keys",
25
"values", "debug", "undebug", "monitor", "unmonitor", "traceCalls", "untraceCalls",
26
"traceAll", "untraceAll", "copy" /*, "memoryProfile", "memoryProfileEnd"*/];
28
// List of shortcuts for some console methods
29
var consoleShortcuts = ["dir", "dirxml", "table"];
31
// List of console variables.
32
var props = ["$0", "$1", "$2", "$3", "$4"];
34
// Registered commands, name -> config object.
35
var userCommands = Object.create(null);
37
// List of command line APIs to auto-complete, kept equal to the concatenation
38
// of the above minus trace*.
39
var completionList = [
40
"$", "$$", "$n", "$x", "cd", "clear", "inspect", "keys",
41
"values", "debug", "undebug", "monitor", "unmonitor", "copy"
42
].concat(consoleShortcuts, props);
43
var unsortedCompletionList = true;
45
// ********************************************************************************************* //
46
// Command Line Implementation
49
* Returns a command line object (bundled with passed window through closure). The object
50
* provides all necessary APIs as described here:
51
* http://getfirebug.com/wiki/index.php/Command_Line_API
53
* @param {Object} context
56
function createFirebugCommandLine(context, win)
58
var contentView = Wrapper.getContentView(win);
61
if (FBTrace.DBG_COMMANDLINE || FBTrace.DBG_ERRORS)
62
FBTrace.sysout("createFirebugCommandLine ERROR no contentView " + context.getName());
67
// The debuggee global.
68
var dglobal = DebuggerLib.getDebuggeeGlobal(context, win);
70
if (!context.commandLineCache)
71
context.commandLineCache = new WeakMap();
72
var commandLineCache = context.commandLineCache;
74
var commandLine = commandLineCache.get(win.document);
76
return copyCommandLine(commandLine, dglobal);
78
// The commandLine object.
79
commandLine = dglobal.makeDebuggeeValue(Object.create(null));
81
var console = Firebug.ConsoleExposed.createFirebugConsole(context, win);
82
// The command line API instance.
83
var commands = CommandLineAPI.getCommandLineAPI(context);
85
// Helpers for command creation.
86
function createCommandHandler(command)
88
var wrappedCommand = function()
92
return command.apply(null, arguments);
96
throw new Error(ex.message, ex.fileName, ex.lineNumber);
99
return dglobal.makeDebuggeeValue(wrappedCommand);
102
function createVariableHandler(handler, config)
104
var debuggeeObj = {}, object;
106
// Callable getters are commands whose syntax are both `command` and `command()`.
107
// The help command has this syntax for example.
108
if (config.isCallableGetter === true)
109
debuggeeObj = function(){ return object.handle(); };
111
object = dglobal.makeDebuggeeValue(debuggeeObj);
112
object.handle = function()
116
return handler(context);
120
throw new Error(ex.message, ex.fileName, ex.lineNumber);
126
function createUserCommandHandler(config, name)
132
return config.handler.call(null, context, arguments);
136
throw new Error(ex.message, ex.fileName, ex.lineNumber);
141
// Define command line methods.
142
for (var commandName in commands)
144
var command = commands[commandName];
145
commandLine[commandName] = createCommandHandler(command);
148
// Register shortcut.
149
consoleShortcuts.forEach(function(name)
151
var command = console[name].bind(console);
152
commandLine[name] = createCommandHandler(command);
155
// Register user commands.
156
for (var name in userCommands)
158
var config = userCommands[name];
159
var command = createUserCommandHandler(config, name);
160
if (userCommands[name].getter)
161
commandLine[name] = createVariableHandler(command, config);
163
commandLine[name] = createCommandHandler(command);
166
commandLineCache.set(win.document, commandLine);
168
// Return a copy so the original one is preserved from changes.
169
return copyCommandLine(commandLine, dglobal);
172
// ********************************************************************************************* //
176
* Registers a command.
178
* @param {string} name The name of the command
179
* @param {object} config The configuration. See some examples in commandLineHelp.js
180
* and commandLineInclude.js
182
function registerCommand(name, config)
184
if (commandNames[name] || consoleShortcuts[name] || props[name] || userCommands[name])
186
if (FBTrace.DBG_ERRORS)
188
FBTrace.sysout("firebug.registerCommand; ERROR This command is already " +
189
"registered: " + name);
195
userCommands[name] = config;
196
completionList.push(name);
197
unsortedCompletionList = true;
202
* Unregisters a command.
204
* @param {string} name The name of the command to unregister
206
function unregisterCommand(name)
208
if (!userCommands[name])
210
if (FBTrace.DBG_ERRORS)
212
FBTrace.sysout("firebug.unregisterCommand; ERROR This command is not " +
213
"registered: " + name);
219
delete userCommands[name];
220
var ind = completionList.indexOf(name);
222
completionList.splice(ind, 1);
227
* Evaluates an expression in the thread of the webpage, so the Firebug UI is not frozen
228
* when the expression calls a function which will be paused.
231
* @param {object} context
232
* @param {Window} win
233
* @param {string} expr The expression (transformed if needed)
234
* @param {string} origExpr The expression as typed by the user
235
* @param {function} onSuccess The function to trigger in case of success
236
* @param {function} onError The function to trigger in case of exception
238
* @see CommandLine.evaluate
240
function evaluateInPageContext(context, win)
242
executeInWindowContext(win, evaluate, arguments);
246
* Evaluates an expression.
248
* @param {object} context
249
* @param {Window} win
250
* @param {string} expr The expression (transformed if needed)
251
* @param {string} origExpr The expression as typed by the user
252
* @param {function} onSuccess The function to trigger in case of success
253
* @param {function} onError The function to trigger in case of exception
255
function evaluate(context, win, expr, origExpr, onSuccess, onError)
258
var contentView = Wrapper.getContentView(win);
259
var commandLine = createFirebugCommandLine(context, win);
260
var dglobal = DebuggerLib.getDebuggeeGlobal(context, win);
263
updateVars(commandLine, dglobal, context);
264
removeConflictingNames(commandLine, context, contentView);
266
resObj = dglobal.evalInGlobalWithBindings(expr, commandLine);
268
var unwrap = function(obj)
270
return DebuggerLib.unwrapDebuggeeValue(obj, contentView, dglobal);
273
// In case of abnormal termination, as if by the "slow script" dialog box,
274
// do not print anything in the console.
277
if (FBTrace.DBG_ERROR)
278
FBTrace.sysout("CommandLineExposed.evaluate; something went wrong when evaluating this"+
279
" expression: "+expr);
283
if (resObj.hasOwnProperty("return"))
285
result = unwrap(resObj.return);
286
if (resObj.return && resObj.return.handle)
288
resObj.return.handle();
289
// Do not print anything in the console in case of getter commands.
293
else if (resObj.hasOwnProperty("yield"))
295
result = unwrap(resObj.yield);
297
else if (resObj.hasOwnProperty("throw"))
299
// Change source and line number of exceptions from commandline code
300
// create new error since properties of nsIXPCException are not modifiable.
301
// Example of code raising nsIXPCException: `alert({toString: function(){ throw "blah"; }})`
303
// xxxFlorent: FIXME: we can't get the right stack trace with this example:
305
// throw new Error("error");
310
var exc = unwrap(resObj.throw);
312
if (exc === null || exc === undefined)
315
if (typeof exc !== "object")
317
exc = new Error(exc, null, null);
318
exc.fileName = exc.lineNumber = exc.stack = null;
321
var shouldModify = false, isXPCException = false;
322
var fileName = exc.filename || exc.fileName || "";
323
var isInternalError = fileName.lastIndexOf("chrome://", 0) === 0;
324
var lineNumber = null;
327
var isFileNameMasked = DebuggerLib.isFrameLocationEval(fileName);
328
if (isInternalError || isFileNameMasked)
331
isXPCException = (exc.filename !== undefined);
333
// Lie and show the pre-transformed expression instead.
334
fileName = "data:,/* " + Locale.$STR("commandline.errorSourceHeader") + " */"+
335
encodeURIComponent("\n"+origExpr);
337
if (isInternalError && typeof exc.stack === "string")
339
splitStack = exc.stack.split("\n");
340
var correctionSucceeded = correctStackTrace(splitStack);
341
if (correctionSucceeded)
343
// correct the line number so we take into account the comment prepended above
344
lineNumber = findLineNumberInExceptionStack(splitStack) + 1;
346
// correct the first trace
347
splitStack.splice(0, 1, "@" + fileName + ":" + lineNumber);
348
stack = splitStack.join("\n");
351
shouldModify = false;
355
// correct the line number so we take into account the comment prepended above
356
lineNumber = exc.lineNumber + 1;
360
result = new Error();
364
result.stack = stack;
365
result.source = origExpr;
366
result.message = exc.message;
367
result.lineNumber = lineNumber;
368
result.fileName = fileName;
370
// The error message can also contain post-transform details about the
371
// source, but it's harder to lie about. Make it prettier, at least.
372
if (typeof result.message === "string")
373
result.message = result.message.replace(/__fb_scopedVars\(/g, "<get closure>(");
376
result.name = exc.name;
380
Obj.getPropertyNames(exc).forEach(function(prop)
382
result[prop] = exc[prop];
384
result.stack = exc.stack;
385
result.source = exc.source;
388
executeInWindowContext(window, onError, [result, context]);
392
executeInWindowContext(window, onSuccess, [result, context]);
395
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
396
// Helpers (not accessible from web content)
398
function copyCommandLine(commandLine, dglobal)
400
var copy = dglobal.makeDebuggeeValue(Object.create(null));
401
for (var name in commandLine)
402
copy[name] = commandLine[name];
406
function findLineNumberInExceptionStack(splitStack)
408
var m = splitStack[0].match(/:(\d+)$/);
409
return m !== null ? +m[1] : null;
412
function correctStackTrace(splitStack)
414
var filename = Components.stack.filename;
415
// remove the frames over the evaluated expression
416
for (var i = 0; i < splitStack.length-1 &&
417
splitStack[i+1].indexOf(evaluate.name + "@" + filename, 0) === -1 ; i++);
419
if (i >= splitStack.length)
421
splitStack.splice(0, i);
425
function updateVars(commandLine, dglobal, context)
427
var htmlPanel = context.getPanel("html", true);
428
var vars = htmlPanel ? htmlPanel.getInspectorVars() : null;
430
for (var prop in vars)
431
commandLine[prop] = dglobal.makeDebuggeeValue(vars[prop]);
433
// Iterate all registered commands and pick those which represents a 'variable'.
434
// These needs to be available as variables within the Command Line namespace.
435
for (var prop in userCommands)
437
var cmd = userCommands[prop];
440
var value = cmd.handler.call(null, context);
441
commandLine[prop] = dglobal.makeDebuggeeValue(value);
446
function removeConflictingNames(commandLine, context, contentView)
448
for (var name in commandLine)
450
// Note: we cannot trust contentView.hasOwnProperty, so we use the "in" operator.
451
if (name in contentView)
452
delete commandLine[name];
457
* Executes a function in another window execution context.
459
* Useful when we have to pause some debuggee functions without freezing
462
* @param {Window} win The window having the thread in which we want to execute the function
463
* @param {function} func The function to execute
464
* @param {Array or Array-Like object} args The arguments to pass to the function
466
function executeInWindowContext(win, func, args)
468
var listener = function()
470
win.document.removeEventListener("firebugCommandLine", listener);
471
func.apply(null, args);
473
win.document.addEventListener("firebugCommandLine", listener);
474
var event = document.createEvent("Events");
475
event.initEvent("firebugCommandLine", true, false);
476
win.document.dispatchEvent(event);
479
function getAutoCompletionList()
481
if (unsortedCompletionList)
483
unsortedCompletionList = false;
484
completionList.sort();
486
return completionList;
489
// ********************************************************************************************* //
492
Firebug.CommandLineExposed =
494
createFirebugCommandLine: createFirebugCommandLine,
495
commands: commandNames,
496
consoleShortcuts: consoleShortcuts,
498
userCommands: userCommands,
499
registerCommand: registerCommand,
500
unregisterCommand: unregisterCommand,
501
evaluate: evaluateInPageContext,
502
getAutoCompletionList: getAutoCompletionList,
505
return Firebug.CommandLineExposed;
507
// ********************************************************************************************* //