1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/wrapper",
  5     "firebug/lib/events",
  6     "firebug/lib/dom",
  7 ],
  8 function(Wrapper, Events, Dom) {
  9 
 10 // ********************************************************************************************* //
 11 // Command Line APIs
 12 
 13 // List of command line APIs
 14 var commands = ["$", "$$", "$x", "$n", "cd", "clear", "inspect", "keys",
 15     "values", "debug", "undebug", "monitor", "unmonitor", "traceCalls", "untraceCalls",
 16     "traceAll", "untraceAll", "copy" /*, "memoryProfile", "memoryProfileEnd"*/];
 17 
 18 // List of shortcuts for some console methods
 19 var consoleShortcuts = ["dir", "dirxml", "table"];
 20 
 21 // List of console variables.
 22 var props = ["$0", "$1"];
 23 
 24 // Registered commands, name -> config object.
 25 var userCommands = {};
 26 
 27 // ********************************************************************************************* //
 28 // Command Line Implementation
 29 
 30 /**
 31  * Returns a command line object (bundled with passed window through closure). The object
 32  * provides all necessary APIs as described here:
 33  * http://getfirebug.com/wiki/index.php/Command_Line_API
 34  *
 35  * @param {Object} context
 36  * @param {Object} win
 37  */
 38 function createFirebugCommandLine(context, win)
 39 {
 40     var contentView = Wrapper.getContentView(win);
 41     if (!contentView)
 42     {
 43         if (FBTrace.DBG_COMMANDLINE || FBTrace.DBG_ERRORS)
 44             FBTrace.sysout("createFirebugCommandLine ERROR no contentView " + context.getName());
 45 
 46         return null;
 47     }
 48 
 49     // The commandLine object
 50     var commandLine = {
 51         __exposedProps__: {}
 52     };
 53 
 54     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 55     // Exposed Properties
 56 
 57     function createCommandHandler(cmd) {
 58         return function() {
 59             return notifyFirebug(arguments, cmd, "firebugExecuteCommand");
 60         }
 61     }
 62 
 63     function createShortcutHandler(cmd) {
 64         return function() {
 65             return console[cmd].apply(console, arguments);
 66         }
 67     }
 68 
 69     function createVariableHandler(prop) {
 70         return function() {
 71             return notifyFirebug(arguments, prop, "firebugExecuteCommand");
 72         }
 73     }
 74 
 75     // Define command line methods
 76     for (var i=0; i<commands.length; i++)
 77     {
 78         var command = commands[i];
 79 
 80         // If the method is already defined, don't override it.
 81         if (command in contentView)
 82             continue;
 83 
 84         commandLine[command] = createCommandHandler(command);
 85         commandLine.__exposedProps__[command] = "rw";
 86     }
 87 
 88     var console = Firebug.ConsoleExposed.createFirebugConsole(context, win);
 89 
 90     // Define shortcuts for some console methods
 91     for (var i=0; i<consoleShortcuts.length; i++)
 92     {
 93         var command = consoleShortcuts[i];
 94 
 95         // If the method is already defined, don't override it.
 96         if (command in contentView)
 97             continue;
 98 
 99         commandLine[command] = createShortcutHandler(command);
100         commandLine.__exposedProps__[command] = "r";
101     }
102 
103     // Define console variables.
104     for (var i=0; i<props.length; i++)
105     {
106         var prop = props[i];
107         if (prop in contentView)
108             continue;
109 
110         commandLine.__defineGetter__(prop, createVariableHandler(prop));
111         commandLine.__exposedProps__[prop] = "r";
112     }
113 
114     // Define user registered commands.
115     for (var name in userCommands)
116     {
117         // If the method is already defined, don't override it.
118         if (name in contentView)
119             continue;
120 
121         var config = userCommands[name];
122 
123         if (config.getter)
124         {
125             commandLine.__defineGetter__(name, createVariableHandler(name));
126             commandLine.__exposedProps__[name] = "r";
127         }
128         else
129         {
130             commandLine[name] = createCommandHandler(name);
131             commandLine.__exposedProps__[name] = "r";
132         }
133     }
134 
135     attachCommandLine();
136 
137     // xxxHonza: TODO make this private.
138     commandLine["detachCommandLine"] = detachCommandLine;
139     commandLine.__exposedProps__["detachCommandLine"] = "r";
140 
141     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
142     // Helpers (not accessible from web content)
143 
144     function attachCommandLine()
145     {
146         if (FBTrace.DBG_COMMANDLINE)
147             FBTrace.sysout("commandLine.Exposed.attachCommandLine; " + window.location);
148 
149         if (!contentView.console)
150         {
151             var console = createFirebugConsole(context, win);
152             contentView.console = console;
153         }
154 
155         Events.addEventListener(contentView.document, "firebugCommandLine",
156             firebugEvalEvent, true);
157     }
158 
159     function detachCommandLine()
160     {
161         Events.removeEventListener(contentView.document, "firebugCommandLine",
162             firebugEvalEvent, true);
163 
164         // suicide!
165         delete contentView._FirebugCommandLine;
166 
167         if (FBTrace.DBG_COMMANDLINE)
168             FBTrace.sysout("commandLine.Exposed.detachCommandLine; " + window.location);
169     }
170 
171     function firebugEvalEvent(event)
172     {
173         if (FBTrace.DBG_COMMANDLINE)
174             FBTrace.sysout("commandLine.Exposed.firebugEvalEvent " + window.location);
175 
176         // see commandLine.js
177         var expr = Dom.getMappedData(contentView.document, "firebug-expr");
178         evaluate(expr);
179 
180         if (FBTrace.DBG_COMMANDLINE)
181             FBTrace.sysout("commandLine.Exposed; did evaluate on " + expr);
182     }
183 
184     function evaluate(expr)
185     {
186         try
187         {
188             var line = Components.stack.lineNumber;
189             var result = contentView.eval(expr);
190 
191             // See Issue 5221
192             //var result = FirebugEvaluate(expr, contentView);
193             notifyFirebug([result], "evaluated", "firebugAppendConsole");
194         }
195         catch (exc)
196         {
197             // change source and line number of exeptions from commandline code
198             // create new error since properties of nsIXPCException are not modifiable
199             var shouldModify, isXPCException;
200             if (exc.filename == Components.stack.filename)
201                 shouldModify = isXPCException = true;
202             else if (exc.fileName == Components.stack.filename)
203                 shouldModify = true;
204 
205             if (shouldModify)
206             {
207                 var result = new Error;
208                 result.stack = null;
209                 result.source = expr;
210                 result.message = exc.message;
211                 result.lineNumber = exc.lineNumber - line;
212                 result.fileName = "data:," + encodeURIComponent(expr);
213 
214                 if (!isXPCException)
215                     result.name = exc.name;
216             }
217             else
218             {
219                 result = exc;
220             }
221 
222             notifyFirebug([result], "evaluateError", "firebugAppendConsole");
223         }
224     }
225 
226     function notifyFirebug(objs, methodName, eventID)
227     {
228         var event = contentView.document.createEvent("Events");
229         event.initEvent(eventID, true, false);
230 
231         commandLine.userObjects = [];
232         for (var i=0; i<objs.length; i++)
233             commandLine.userObjects.push(objs[i]);
234 
235         var length = commandLine.userObjects.length;
236         Dom.setMappedData(contentView.document, "firebug-methodName", methodName);
237 
238         contentView.document.dispatchEvent(event);
239 
240         if (FBTrace.DBG_COMMANDLINE)
241         {
242             FBTrace.sysout("commandLine.Exposed; dispatched event " + methodName + " via " +
243                 eventID + " with " + objs.length + " user objects, [0]:" +
244                 commandLine.userObjects[0]);
245         }
246 
247         var result;
248         if (Dom.getMappedData(contentView.document, "firebug-retValueType") == "array")
249             result = [];
250 
251         if (!result && commandLine.userObjects.length == length + 1)
252             return commandLine.userObjects[length];
253 
254         for (var i=length; i<commandLine.userObjects.length && result; i++)
255             result.push(commandLine.userObjects[i]);
256 
257         return result;
258     }
259 
260     return commandLine;
261 };
262 
263 /* see Issue 5221
264 // chrome: urls are filtered out by debugger, so we create script with a data url
265 // to get eval sequences in location list and 0 error ofsets
266 const evalFileSrc = "data:text/javascript,FirebugEvaluate=function(t,w)w.eval(t)";
267 var script = document.createElementNS("http://www.w3.org/1999/xhtml", "script")
268 script.src = evalFileSrc;
269 document.documentElement.appendChild(script);
270 */
271 
272 // ********************************************************************************************* //
273 // User Commands
274 
275 function registerCommand(name, config)
276 {
277     if (commands[name] || consoleShortcuts[name] || props[name] || userCommands[name])
278     {
279         if (FBTrace.DBG_ERRORS)
280         {
281             FBTrace.sysout("firebug.registerCommand; ERROR This command is already " +
282                 "registered: " + name);
283         }
284 
285         return false;
286     }
287 
288     userCommands[name] = config;
289     return true;
290 }
291 
292 function unregisterCommand(name)
293 {
294     if (!userCommands[name])
295     {
296         if (FBTrace.DBG_ERRORS)
297         {
298             FBTrace.sysout("firebug.unregisterCommand; ERROR This command is not " +
299                 "registered: " + name);
300         }
301 
302         return false;
303     }
304 
305     delete userCommands[name];
306     return true;
307 }
308 
309 // ********************************************************************************************* //
310 // Registration
311 
312 Firebug.CommandLineExposed =
313 {
314     createFirebugCommandLine: createFirebugCommandLine,
315     commands: commands,
316     consoleShortcuts: consoleShortcuts,
317     properties: props,
318     userCommands: userCommands,
319     registerCommand: registerCommand,
320     unregisterCommand: unregisterCommand,
321 };
322 
323 return Firebug.CommandLineExposed;
324 
325 // ********************************************************************************************* //
326 });
327