1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/object",
  5     "firebug/firebug",
  6     "firebug/chrome/reps",
  7     "firebug/lib/locale",
  8     "firebug/lib/events",
  9     "firebug/lib/wrapper",
 10     "firebug/lib/url",
 11     "firebug/lib/css",
 12     "firebug/lib/dom",
 13     "firebug/chrome/firefox",
 14     "firebug/chrome/window",
 15     "firebug/lib/system",
 16     "firebug/lib/xpath",
 17     "firebug/lib/string",
 18     "firebug/lib/xml",
 19     "firebug/lib/array",
 20     "firebug/lib/persist",
 21     "firebug/lib/keywords",
 22     "firebug/console/console",
 23     "firebug/console/commandLineHelp",
 24     "firebug/console/commandLineInclude",
 25     "firebug/console/commandLineExposed",
 26     "firebug/console/autoCompleter",
 27     "firebug/console/commandHistory"
 28 ],
 29 function(Obj, Firebug, FirebugReps, Locale, Events, Wrapper, Url, Css, Dom, Firefox, Win, System,
 30     Xpath, Str, Xml, Arr, Persist, Keywords, Console, CommandLineHelp,
 31     CommandLineInclude, CommandLineExposed) {
 32 
 33 // ********************************************************************************************* //
 34 // Constants
 35 
 36 const Cc = Components.classes;
 37 const Ci = Components.interfaces;
 38 
 39 const commandPrefix = ">>>";
 40 const reCmdSource = /^with\(_FirebugCommandLine\){(.*)};$/;
 41 
 42 // ********************************************************************************************* //
 43 // Command Line
 44 
 45 Firebug.CommandLine = Obj.extend(Firebug.Module,
 46 {
 47     dispatchName: "commandLine",
 48 
 49     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 50 
 51     // targetWindow was needed by evaluateInSandbox, let's leave it for a while in case
 52     // we rethink this yet again
 53     initializeCommandLineIfNeeded: function (context, win)
 54     {
 55         if (!context || !win)
 56             return;
 57 
 58         // The command-line requires that the console has been initialized first,
 59         // so make sure that's so.  This call should have no effect if the console
 60         // is already initialized.
 61         var consoleIsReady = Firebug.Console.isReadyElsePreparing(context, win);
 62 
 63         // Make sure the command-line is initialized.  This call should have no
 64         // effect if the command-line is already initialized.
 65         var commandLineIsReady = Firebug.CommandLine.isReadyElsePreparing(context, win);
 66 
 67         if (FBTrace.DBG_COMMANDLINE)
 68         {
 69             FBTrace.sysout("commandLine.initializeCommandLineIfNeeded console ready: " +
 70                 consoleIsReady + " commandLine ready: " + commandLineIsReady);
 71         }
 72     },
 73 
 74     // returns user-level wrapped object I guess.
 75     evaluate: function(expr, context, thisValue, targetWindow, successConsoleFunction,
 76         exceptionFunction, noStateChange)
 77     {
 78         if (!context)
 79             return;
 80 
 81         try
 82         {
 83             var result = null;
 84             var debuggerState = Firebug.Debugger.beginInternalOperation();
 85 
 86             if (this.isSandbox(context))
 87             {
 88                 result = this.evaluateInSandbox(expr, context, thisValue, targetWindow,
 89                     successConsoleFunction, exceptionFunction);
 90             }
 91             else if (Firebug.Debugger.hasValidStack(context))
 92             {
 93                 result = this.evaluateInDebugFrame(expr, context, thisValue, targetWindow,
 94                     successConsoleFunction, exceptionFunction);
 95             }
 96             else
 97             {
 98                 result = this.evaluateByEventPassing(expr, context, thisValue, targetWindow,
 99                     successConsoleFunction, exceptionFunction);
100             }
101 
102             if (!noStateChange)
103                 context.invalidatePanels("dom", "html");
104         }
105         catch (exc)
106         {
107             // XXX jjb, I don't expect this to be taken, the try here is for the finally
108             if (FBTrace.DBG_ERRORS && FBTrace.DBG_COMMANDLINE)
109             {
110                 FBTrace.sysout("commandLine.evaluate with context.stopped:" + context.stopped +
111                     " EXCEPTION " + exc, exc);
112             }
113         }
114         finally
115         {
116             Firebug.Debugger.endInternalOperation(debuggerState);
117         }
118 
119         return result;
120     },
121 
122     evaluateByEventPassing: function(expr, context, thisValue, targetWindow,
123         successConsoleFunction, exceptionFunction)
124     {
125         var win = targetWindow ? targetWindow :
126             (context.baseWindow ? context.baseWindow : context.window);
127 
128         if (!win)
129         {
130             if (FBTrace.DBG_ERRORS && FBTrace.DBG_COMMANDLINE)
131                 FBTrace.sysout("commandLine.evaluateByEventPassing: no targetWindow!");
132             return;
133         }
134 
135         //xxxHonza: do not detach the command line here. In case where Firebug is 
136         // halted in the debugger and debugging a function executed in the command line
137         // the command line handler needs to be yet used to display the return value.
138 
139         // Inject commandLine APIs again.
140         this.initializeCommandLineIfNeeded(context, win);
141 
142         // Make sure the command line script is attached.
143         if (!Firebug.CommandLine.isAttached(context, win))
144         {
145             FBTrace.sysout("commandLine: document does not have command line attached " +
146                 "it's too early for command line "+Win.getWindowId(win)+" location:"+
147                 Win.safeGetWindowLocation(win), document);
148 
149             if (Xml.isXMLPrettyPrint(context, win))
150             {
151                 var msg = Locale.$STR("commandline.disabledForXMLDocs");
152                 var row = Firebug.Console.logFormatted([msg], context, "warn", true);
153                 var objectBox = row.querySelector(".objectBox");
154 
155                 // Log a message with a clickable link that can be used to enable
156                 // the command line - but the page will switch into HTML. The listener
157                 // passed into the function is called when the user clicks the link.
158                 FirebugReps.Description.render(msg, objectBox, Obj.bind(function()
159                 {
160                     // Reset the flag that protect script injection into the page.
161                     context.isXMLPrettyPrint = false;
162 
163                     // Now inject the command line.
164                     Firebug.CommandLine.initializeCommandLineIfNeeded(context, win);
165                 }, this));
166             }
167             else
168             {
169                 Firebug.Console.logFormatted(["Firebug cannot find firebug-CommandLineAttached " +
170                     "through Dom.getMappedData, it is too early for command line",
171                      win], context, "error", true);
172             }
173             return;
174         }
175 
176         var event = document.createEvent("Events");
177         event.initEvent("firebugCommandLine", true, false);
178         Dom.setMappedData(win.document, "firebug-methodName", "evaluate");
179 
180         expr = expr.toString();
181         expr = "with(_FirebugCommandLine){\n" + expr + "\n};";
182         Dom.setMappedData(win.document, "firebug-expr", expr);
183 
184         var consoleHandler = Firebug.Console.injector.getConsoleHandler(context, win);
185 
186         if (!consoleHandler)
187         {
188             FBTrace.sysout("commandLine evaluateByEventPassing no consoleHandler "+
189                 Win.safeGetWindowLocation(win));
190             return;
191         }
192 
193         if (successConsoleFunction)
194         {
195             consoleHandler.setEvaluatedCallback( function useConsoleFunction(result)
196             {
197                 var ignoreReturnValue = Console.getDefaultReturnValue(win);
198                 if (result === ignoreReturnValue)
199                     return;
200 
201                 successConsoleFunction(result, context);
202             });
203         }
204 
205         if (exceptionFunction)
206         {
207             consoleHandler.setEvaluateErrorCallback(function useExceptionFunction(result)
208             {
209                 exceptionFunction(result, context, "errorMessage");
210             });
211         }
212         else
213         {
214             consoleHandler.setEvaluateErrorCallback(function useErrorFunction(result)
215             {
216                 if (result)
217                 {
218                     var m = reCmdSource.exec(result.source);
219                     if (m && m.length > 0)
220                         result.source = m[1];
221                 }
222 
223                 Firebug.Console.logFormatted([result], context, "error", true);
224             });
225         }
226 
227         if (FBTrace.DBG_COMMANDLINE)
228         {
229             FBTrace.sysout("commandLine.evaluateByEventPassing '" + expr +
230                 "' using consoleHandler:", consoleHandler);
231         }
232 
233         try
234         {
235             win.document.dispatchEvent(event);
236 
237             // Clean up the command line APIs.
238             Firebug.CommandLine.injector.detachCommandLine(context, win);
239         }
240         catch(exc)
241         {
242             if (FBTrace.DBG_COMMANDLINE || FBTrace.DBG_ERRORS)
243                 FBTrace.sysout("commandLine.evaluateByEventPassing dispatchEvent FAILS " + exc,
244                     {exc:exc, event:event});
245         }
246 
247         if (FBTrace.DBG_COMMANDLINE)
248         {
249             FBTrace.sysout("commandLine.evaluateByEventPassing return after firebugCommandLine " +
250                 "event:", event);
251         }
252     },
253 
254     evaluateInDebugFrame: function(expr, context, thisValue, targetWindow,
255         successConsoleFunction, exceptionFunction)
256     {
257         var result = null;
258 
259         // targetWindow may be frame in HTML
260         var win = targetWindow ? targetWindow :
261             (context.baseWindow ? context.baseWindow : context.window);
262 
263         if (!context.commandLineAPI)
264             context.commandLineAPI = new FirebugCommandLineAPI(context);
265 
266         var htmlPanel = context.getPanel("html", true);
267         var scope = {
268             api       : context.commandLineAPI,
269             vars      : htmlPanel?htmlPanel.getInspectorVars():null,
270             thisValue : thisValue
271         };
272 
273         try
274         {
275             result = Firebug.Debugger.evaluate(expr, context, scope);
276 
277             successConsoleFunction(result, context);
278         }
279         catch (e)
280         {
281             exceptionFunction(e, context);
282         }
283 
284         return result;
285     },
286 
287     evaluateByPostMessage: function(expr, context, thisValue, targetWindow,
288         successConsoleFunction, exceptionFunction)
289     {
290         // targetWindow may be frame in HTML
291         var win = targetWindow ? targetWindow :
292             (context.baseWindow ? context.baseWindow : context.window);
293 
294         if (!win)
295         {
296             if (FBTrace.DBG_ERRORS && FBTrace.DBG_COMMANDLINE)
297                 FBTrace.sysout("commandLine.evaluateByPostMessage: no targetWindow!");
298             return;
299         }
300 
301         // We're going to use some command-line facilities, but it may not have initialized yet.
302         this.initializeCommandLineIfNeeded(context, win);
303 
304         expr = expr.toString();
305         expr = "with(_FirebugCommandLine){\n" + expr + "\n};";
306 
307         var consoleHandler = Firebug.Console.injector.getConsoleHandler(context, win);
308 
309         if (!consoleHandler)
310         {
311             FBTrace.sysout("commandLine evaluateByPostMessage no consoleHandler "+
312                 Win.safeGetWindowLocation(win));
313             return;
314         }
315 
316         if (successConsoleFunction)
317         {
318             consoleHandler.setEvaluatedCallback( function useConsoleFunction(result)
319             {
320                 var ignoreReturnValue = Console.getDefaultReturnValue(win);
321                 if (result === ignoreReturnValue)
322                     return;
323 
324                 successConsoleFunction(result, context);
325             });
326         }
327 
328         if (exceptionFunction)
329         {
330             consoleHandler.evaluateError = function useExceptionFunction(result)
331             {
332                 exceptionFunction(result, context, "errorMessage");
333             }
334         }
335         else
336         {
337             consoleHandler.evaluateError = function useErrorFunction(result)
338             {
339                 if (result)
340                 {
341                     var m = reCmdSource.exec(result.source);
342                     if (m && m.length > 0)
343                         result.source = m[1];
344                 }
345 
346                 Firebug.Console.logFormatted([result], context, "error", true);
347             }
348         }
349 
350         return win.postMessage(expr, "*");
351     },
352 
353     evaluateInWebPage: function(expr, context, targetWindow)
354     {
355         var win = targetWindow ? targetWindow :
356             (context.baseWindow ? context.baseWindow : context.window);
357         var element = Dom.addScript(win.document, "_firebugInWebPage", expr);
358         if (!element)
359             return;
360 
361         setTimeout(function delayRemoveScriptTag()
362         {
363             // we don't need the script element, result is in DOM object
364             if (element.parentNode)
365                 element.parentNode.removeChild(element);
366         });
367 
368         return "true";
369     },
370 
371     // isSandbox(context) true, => context.global is a Sandbox
372     evaluateInSandbox: function(expr, context, thisValue, targetWindow, successConsoleFunction,
373         exceptionFunction)
374     {
375         var result,
376             scriptToEval = expr;
377 
378         try
379         {
380             result = Components.utils.evalInSandbox(scriptToEval, context.global);
381 
382             if (FBTrace.DBG_COMMANDLINE)
383                 FBTrace.sysout("commandLine.evaluateInSandbox success for sandbox ", scriptToEval);
384 
385             successConsoleFunction(result, context);
386         }
387         catch (e)
388         {
389             if (FBTrace.DBG_ERRORS && FBTrace.DBG_COMMANDLINE)
390                 FBTrace.sysout("commandLine.evaluateInSandbox FAILED in "+context.getName()+
391                     " because "+e, e);
392 
393             exceptionFunction(e, context);
394 
395             result = new FirebugReps.ErrorMessageObj("commandLine.evaluateInSandbox FAILED: " + e,
396                 Url.getDataURLForContent(scriptToEval, "FirebugCommandLineEvaluate"),
397                 e.lineNumber, 0, "js", context, null);
398         }
399 
400         return result;
401     },
402 
403     isSandbox: function (context)
404     {
405         return (context.global && context.global+"" === "[object Sandbox]");
406     },
407 
408     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
409 
410     enter: function(context, command)
411     {
412         var expr = command ? command : this.getExpression(context);
413         if (expr == "")
414             return;
415 
416         var mozJSEnabled = Firebug.Options.getPref("javascript", "enabled");
417         if (!mozJSEnabled)
418         {
419             Firebug.Console.log(Locale.$STR("console.JSDisabledInFirefoxPrefs"), context, "info");
420             return;
421         }
422 
423         if (!Firebug.commandEditor || context.panelName != "console")
424         {
425             this.clear(context);
426             Firebug.Console.log(commandPrefix + " " + expr, context, "command", FirebugReps.Text);
427         }
428         else
429         {
430             var shortExpr = Str.cropString(Str.stripNewLines(expr), 100);
431             Firebug.Console.log(commandPrefix + " " + shortExpr, context, "command",
432                 FirebugReps.Text);
433         }
434 
435         this.commandHistory.appendToHistory(expr);
436 
437         var noscript = getNoScript();
438         if (noscript)
439         {
440             var currentURI = Firefox.getCurrentURI();
441             var noScriptURI = currentURI ? noscript.getSite(currentURI.spec) : null;
442             if (noScriptURI)
443                 noScriptURI = (noscript.jsEnabled || noscript.isJSEnabled(noScriptURI)) ?
444                     null : noScriptURI;
445         }
446 
447         if (noscript && noScriptURI)
448             noscript.setJSEnabled(noScriptURI, true);
449 
450         var goodOrBad = Obj.bind(Firebug.Console.log, Firebug.Console);
451         this.evaluate(expr, context, null, null, goodOrBad, goodOrBad);
452 
453         if (noscript && noScriptURI)
454             noscript.setJSEnabled(noScriptURI, false);
455 
456         var consolePanel = Firebug.currentContext.panelMap.console;
457         if (consolePanel)
458             Dom.scrollToBottom(consolePanel.panelNode);
459     },
460 
461     enterInspect: function(context)
462     {
463         var expr = this.getCommandLine(context).value;
464         if (expr == "")
465             return;
466 
467         this.clear(context);
468         this.commandHistory.appendToHistory(expr);
469 
470         this.evaluate(expr, context, null, null, function(result, context)
471         {
472             if (typeof(result) != undefined)
473                 Firebug.chrome.select(result);
474         });
475     },
476 
477     reenter: function(context)
478     {
479         var command = this.commandHistory.getLastCommand();
480         this.enter(context, command);
481     },
482 
483     copyBookmarklet: function(context)
484     {
485         // XXXsilin: This needs escaping, and stripNewLines is exactly the
486         // wrong thing to do when it comes to JavaScript.
487         var commandLine = this.getCommandLine(context);
488         var expr = "javascript: " + Str.stripNewLines(commandLine.value);
489         System.copyToClipboard(expr);
490     },
491 
492     focus: function(context)
493     {
494         if (Firebug.isDetached())
495             Firebug.chrome.focus();
496         else
497             Firebug.toggleBar(true);
498 
499         var commandLine = this.getCommandLine(context);
500 
501         if (!context.panelName)
502         {
503             Firebug.chrome.selectPanel("console");
504         }
505         else if (context.panelName != "console")
506         {
507             this.Popup.toggle(Firebug.currentContext);
508             setTimeout(function() { commandLine.select(); });
509         }
510         else
511         {
512             // We are already on the console, if the command line has also
513             // the focus, toggle back. But only if the UI has been already
514             // opened.
515             if (commandLine.getAttribute("focused") != "true")
516                 setTimeout(function() { commandLine.select(); });
517         }
518     },
519 
520     clear: function(context)
521     {
522         var commandLine = this.getCommandLine(context);
523 
524         if (commandLine.value)
525         {
526             commandLine.value = "";
527             this.autoCompleter.hide();
528             this.update(context);
529             return true;
530         }
531 
532         return false;
533     },
534 
535     cancel: function(context)
536     {
537         return this.clear(context);
538     },
539 
540     update: function(context)
541     {
542         var commandLine = this.getCommandLine(context);
543         context.commandLineText = commandLine.value;
544     },
545 
546     // xxxsz: setMultiLine should just be called when switching between Command Line
547     // and Command Editor
548     // xxxHonza: it is called for me when switching between the Command Line and
549     // Command Editor 
550     setMultiLine: function(multiLine, chrome, saveMultiLine)
551     {
552         var context = Firebug.currentContext;
553 
554         if (FBTrace.DBG_COMMANDLINE)
555         {
556             FBTrace.sysout("commandLine.setMultiline; multiLine: " + multiLine + " for: " +
557                 (context ? context.getName() : "no contet"));
558         }
559 
560         if (context && context.panelName != "console")
561             return;
562 
563         Dom.collapse(chrome.$("fbCommandBox"), multiLine);
564         Dom.collapse(chrome.$("fbPanelSplitter"), !multiLine);
565         Dom.collapse(chrome.$("fbSidePanelDeck"), !multiLine);
566 
567         if (multiLine)
568             chrome.$("fbSidePanelDeck").selectedPanel = chrome.$("fbCommandEditorBox");
569 
570         var commandLine = this.getSingleRowCommandLine();
571         var commandEditor = this.getCommandEditor();
572 
573         // we are just closing the view
574         if (saveMultiLine)
575         {
576             commandLine.value = commandEditor.value;
577             return;
578         }
579 
580         if (context)
581         {
582             var text = context.commandLineText || "";
583             context.commandLineText = text;
584 
585             if (multiLine)
586                 commandEditor.value = Str.cleanIndentation(text);
587             else
588                 commandLine.value = Str.stripNewLines(text);
589         }
590         // else we may be hiding a panel while turning Firebug off
591     },
592 
593     toggleMultiLine: function(forceCommandEditor)
594     {
595         var showCommandEditor = forceCommandEditor || !Firebug.commandEditor;
596         if (showCommandEditor != Firebug.commandEditor)
597             Firebug.Options.set("commandEditor", showCommandEditor);
598     },
599 
600     checkOverflow: function(context)
601     {
602         if (!context)
603             return;
604 
605         var commandLine = this.getCommandLine(context);
606         if (commandLine.value.indexOf("\n") >= 0)
607         {
608             setTimeout(Obj.bindFixed(function()
609             {
610                 Firebug.Options.set("commandEditor", true);
611 
612                 // Switch to the Console panel, where the multiline command line
613                 // is actually displayed. This should be improved see issue 5146
614                 Firebug.chrome.selectPanel("console");
615             }, this));
616         }
617     },
618 
619     onCommandLineOverflow: function(event)
620     {
621         this.checkOverflow(Firebug.currentContext);
622     },
623 
624     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
625     // extends Module
626 
627     initialize: function()
628     {
629         Firebug.Module.initialize.apply(this, arguments);
630 
631         this.setAutoCompleter();
632         this.commandHistory = new Firebug.CommandHistory();
633 
634         if (Firebug.commandEditor)
635             this.setMultiLine(true, Firebug.chrome);
636     },
637 
638     // (Re)create the auto-completer for the small command line.
639     setAutoCompleter: function()
640     {
641         if (this.autoCompleter)
642             this.autoCompleter.shutdown();
643 
644         var commandLine = this.getSingleRowCommandLine();
645         var completionBox = this.getCompletionBox();
646 
647         var options = {
648             showCompletionPopup: Firebug.Options.get("commandLineShowCompleterPopup"),
649             completionPopup: Firebug.chrome.$("fbCommandLineCompletionList"),
650             popupMeasurer: Firebug.chrome.$("fbCommandLineMeasurer"),
651             tabWarnings: true,
652             includeCurrentScope: true
653         };
654 
655         this.autoCompleter = new Firebug.JSAutoCompleter(commandLine, completionBox, options);
656     },
657 
658     initializeUI: function()
659     {
660         this.onCommandLineInput = Obj.bind(this.onCommandLineInput, this);
661         this.onCommandLineOverflow = Obj.bind(this.onCommandLineOverflow, this);
662         this.onCommandLineKeyUp = Obj.bind(this.onCommandLineKeyUp, this);
663         this.onCommandLineKeyDown = Obj.bind(this.onCommandLineKeyDown, this);
664         this.onCommandLineKeyPress = Obj.bind(this.onCommandLineKeyPress, this);
665         this.attachListeners();
666     },
667 
668     attachListeners: function()
669     {
670         var commandLine = this.getSingleRowCommandLine();
671 
672         Events.addEventListener(commandLine, "input", this.onCommandLineInput, true);
673         Events.addEventListener(commandLine, "overflow", this.onCommandLineOverflow, true);
674         Events.addEventListener(commandLine, "keyup", this.onCommandLineKeyUp, true);
675         Events.addEventListener(commandLine, "keydown", this.onCommandLineKeyDown, true);
676         Events.addEventListener(commandLine, "keypress", this.onCommandLineKeyPress, true);
677     },
678 
679     shutdown: function()
680     {
681         var commandLine = this.getSingleRowCommandLine();
682 
683         if (this.autoCompleter)
684             this.autoCompleter.shutdown();
685 
686         if (this.commandHistory)
687             this.commandHistory.detachListeners();
688 
689         Events.removeEventListener(commandLine, "input", this.onCommandLineInput, true);
690         Events.removeEventListener(commandLine, "overflow", this.onCommandLineOverflow, true);
691         Events.removeEventListener(commandLine, "keyup", this.onCommandLineKeyUp, true);
692         Events.removeEventListener(commandLine, "keydown", this.onCommandLineKeyDown, true);
693         Events.removeEventListener(commandLine, "keypress", this.onCommandLineKeyPress, true);
694     },
695 
696     destroyContext: function(context, persistedState)
697     {
698         var panelState = Persist.getPersistedState(this, "console");
699         panelState.commandLineText = context.commandLineText;
700 
701         var commandLine = this.getCommandLine(context);
702         commandLine.value = "";
703 
704         this.autoCompleter.hide();
705         Persist.persistObjects(this, panelState);
706         // more of our work is done in the Console
707 
708         // All command line handlers should be removed at this moment.
709         for (var handler in context.activeCommandLineHandlers)
710         {
711             FBTrace.sysout("commandLine.destroyContext; ERROR active commandlinehandler for: " +
712                 context.getName());
713         }
714     },
715 
716     showPanel: function(browser, panel)
717     {
718         if (!Firebug.currentContext)
719             return;
720 
721         var chrome = Firebug.chrome;
722         var panelState = Persist.getPersistedState(this, "console");
723         if (panelState.commandLineText)
724         {
725             var value = panelState.commandLineText;
726             var commandLine = this.getCommandLine(browser);
727             Firebug.currentContext.commandLineText = value;
728 
729             commandLine.value = value;
730 
731             // We don't need the persistent value in this session/context any more. The showPanel
732             // method is called every time the panel is selected and the text could have been
733             // changed in this session/context already.
734             delete panelState.commandLineText;
735         }
736 
737         this.autoCompleter.hide();
738     },
739 
740     updateOption: function(name, value)
741     {
742         if (name == "commandEditor")
743             this.setMultiLine(value, Firebug.chrome);
744         else if (name == "commandLineShowCompleterPopup")
745             this.setAutoCompleter();
746     },
747 
748     // called by users of command line, currently:
749     // 1) Console on focus command line,
750     // 2) Watch onfocus, and
751     // 3) debugger loadedContext if watches exist
752     isReadyElsePreparing: function(context, win)
753     {
754         if (FBTrace.DBG_COMMANDLINE)
755         {
756             FBTrace.sysout("commandLine.isReadyElsePreparing " + context.getName() + " win: " +
757                 (win ? win.location : "not given"), context);
758         }
759 
760         if (this.isSandbox(context))
761             return;
762 
763         if (Xml.isXMLPrettyPrint(context, win))
764             return false;
765 
766         if (win)
767         {
768             Firebug.CommandLine.injector.attachCommandLine(context, win);
769         }
770         else
771         {
772             Firebug.CommandLine.injector.attachCommandLine(context, context.window);
773             for (var i=0; i<context.windows.length; i++)
774                 Firebug.CommandLine.injector.attachCommandLine(context, context.windows[i]);
775         }
776 
777         var contentView = Wrapper.getContentView(context.window);
778         if (!contentView)
779         {
780             if (FBTrace.DBG_ERRORS)
781                 FBTrace.sysout("CommandLine ERROR context.window invalid", context.window);
782             return false;
783         }
784 
785         // the attach is asynchronous, we can report when it is complete:
786         return contentView._FirebugCommandLine;
787     },
788 
789     onCommandLineKeyUp: function(event)
790     {
791     },
792 
793     onCommandLineKeyDown: function(event)
794     {
795         // XXX: Temporary hack to make FireClosure work (until that gets a new
796         // release out)
797         if (!this.autoCompleter.shouldIncludeHint && Firebug.JSAutoCompleter.transformScopeExpr)
798             this.setAutoCompleter();
799 
800         var context = Firebug.currentContext;
801 
802         this.autoCompleter.handleKeyDown(event, context);
803 
804         if (event.keyCode === KeyEvent.DOM_VK_H && Events.isControl(event))
805         {
806             event.preventDefault();
807             this.autoCompleter.hide();
808             this.commandHistory.show(Firebug.chrome.$("fbCommandLineHistoryButton"));
809             return true;
810         }
811 
812         // Parts of the code moved into key-press handler due to bug 613752
813     },
814 
815     onCommandLineKeyPress: function(event)
816     {
817         var context = Firebug.currentContext;
818 
819         if (!this.autoCompleter.handleKeyPress(event, context))
820         {
821             this.handleKeyPress(event);
822         }
823     },
824 
825     handleKeyPress: function(event)
826     {
827         switch (event.keyCode)
828         {
829             case KeyEvent.DOM_VK_RETURN:
830             case KeyEvent.DOM_VK_ENTER:
831                 event.preventDefault();
832 
833                 if (!event.metaKey && !event.shiftKey)
834                 {
835                     Firebug.CommandLine.enter(Firebug.currentContext);
836                     this.commandHistory.hide();
837                     return true;
838                 }
839                 else if(!event.metaKey && event.shiftKey)
840                 {
841                     Firebug.CommandLine.enterInspect(Firebug.currentContext);
842                     this.commandHistory.hide();
843                     return true;
844                 }
845                 break;
846 
847             case KeyEvent.DOM_VK_UP:
848                 event.preventDefault();
849                 this.commandHistory.cycleCommands(Firebug.currentContext, -1);
850                 return true;
851 
852             case KeyEvent.DOM_VK_DOWN:
853                 event.preventDefault();
854                 this.commandHistory.cycleCommands(Firebug.currentContext, 1);
855                 return true;
856 
857             case KeyEvent.DOM_VK_ESCAPE:
858                 event.preventDefault();
859                 if (Firebug.CommandLine.cancel(Firebug.currentContext))
860                     Events.cancelEvent(event);
861                 this.commandHistory.hide();
862                 return true;
863         }
864 
865         if (this.commandHistory.isOpen && !event.metaKey && !event.ctrlKey && !event.altKey)
866             this.commandHistory.hide();
867 
868         return false;
869     },
870 
871     onCommandLineInput: function(event)
872     {
873         var context = Firebug.currentContext;
874 
875         this.autoCompleter.complete(context);
876         this.update(context);
877     },
878 
879     isAttached: function(context, win)
880     {
881         if (!context)
882             return false;
883 
884         return Firebug.CommandLine.injector.isAttached(win ? win : context.window);
885     },
886 
887     onPanelEnable: function(panelName)
888     {
889         Dom.collapse(Firebug.chrome.$("fbCommandBox"), true);
890         Dom.collapse(Firebug.chrome.$("fbPanelSplitter"), true);
891         Dom.collapse(Firebug.chrome.$("fbSidePanelDeck"), true);
892 
893         this.setMultiLine(Firebug.commandEditor, Firebug.chrome);
894     },
895 
896     onPanelDisable: function(panelName)
897     {
898         if (panelName != "console")  // we don't care about other panels
899             return;
900 
901         Dom.collapse(Firebug.chrome.$("fbCommandBox"), true);
902         Dom.collapse(Firebug.chrome.$("fbPanelSplitter"), true);
903         Dom.collapse(Firebug.chrome.$("fbSidePanelDeck"), true);
904     },
905 
906     getCommandLine: function(context)
907     {
908         return (!this.isInOtherPanel(context) && Firebug.commandEditor) ? 
909                 this.getCommandEditor():
910                 this.getSingleRowCommandLine();
911     },
912 
913     isInOtherPanel: function(context)
914     {
915         // Command line on other panels is never multiline.
916         var visible = Firebug.CommandLine.Popup.isVisible();
917         return visible && context.panelName != "console";
918     },
919 
920     getExpression: function(context)
921     {
922         return (!this.isInOtherPanel(context) && Firebug.commandEditor) ? 
923                 this.getCommandEditor().getExpression() :
924                 this.getSingleRowCommandLine().value;
925     },
926 
927     getCompletionBox: function()
928     {
929         return Firebug.chrome.$("fbCommandLineCompletion");
930     },
931 
932     getSingleRowCommandLine: function()
933     {
934         return Firebug.chrome.$("fbCommandLine");
935     },
936 
937     getCommandEditor: function()
938     {
939         return Firebug.CommandEditor;
940     }
941 
942 });
943 
944 // ********************************************************************************************* //
945 // Shared Helpers
946 
947 Firebug.CommandLine.CommandHandler = Obj.extend(Object,
948 {
949     handle: function(event, api, win)
950     {
951         var element = event.target;
952         var methodName = Dom.getMappedData(win.document, "firebug-methodName");
953 
954         // We create this array in the page using JS, so we need to look on the
955         // wrappedJSObject for it.
956         var contentView = Wrapper.getContentView(win);
957         if (contentView)
958             var hosed_userObjects = contentView._FirebugCommandLine.userObjects;
959 
960         var userObjects = hosed_userObjects ? Arr.cloneArray(hosed_userObjects) : [];
961 
962         if (FBTrace.DBG_COMMANDLINE)
963             FBTrace.sysout("commandLine.CommandHandler for " + Win.getWindowId(win) +
964                 ": method " + methodName + " userObjects:",  userObjects);
965 
966         var subHandler = api[methodName];
967         if (!subHandler)
968             return false;
969 
970         Dom.deleteMappedData(win.document, "firebug-retValueType");
971         var result = subHandler.apply(api, userObjects);
972         if (typeof result != "undefined")
973         {
974             if (result instanceof window.Array)
975             {
976                 Dom.setMappedData(win.document, "firebug-retValueType", "array");
977                 for (var item in result)
978                     hosed_userObjects.push(result[item]);
979             }
980             else
981             {
982                 hosed_userObjects.push(result);
983             }
984         }
985 
986         return true;
987     }
988 });
989 
990 // ********************************************************************************************* //
991 // Command Line API
992 
993 /**
994  * These functions will be called in the extension like this:
995  *
996  * subHandler.apply(api, userObjects);
997  *
998  * Where subHandler is one of the entries below, api is this object and userObjects
999  * are entries in an array we created in the web page.
1000  */
1001 function FirebugCommandLineAPI(context)
1002 {
1003     // returns unwrapped elements from the page
1004     this.$ = function(selector, start)
1005     {
1006         if (start && start.querySelector && (
1007             start.nodeType == Node.ELEMENT_NODE ||
1008             start.nodeType == Node.DOCUMENT_NODE ||
1009             start.nodeType == Node.DOCUMENT_FRAGMENT_NODE))
1010         {
1011             return start.querySelector(selector);
1012         }
1013 
1014         var result = context.baseWindow.document.querySelector(selector);
1015         if (result == null && (selector || "")[0] !== "#")
1016         {
1017             if (context.baseWindow.document.getElementById(selector))
1018             {
1019                 // This should be removed in the next minor (non-bugfix) version
1020                 var msg = Locale.$STRF("warning.dollar_change", [selector]);
1021                 Firebug.Console.log(msg, context, "warn");
1022                 result = null;
1023             }
1024         }
1025 
1026         return result;
1027     };
1028 
1029     // returns unwrapped elements from the page
1030     this.$$ = function(selector, start)
1031     {
1032         var result;
1033 
1034         if (start && start.querySelectorAll && (
1035             start.nodeType == Node.ELEMENT_NODE ||
1036             start.nodeType == Node.DOCUMENT_NODE ||
1037             start.nodeType == Node.DOCUMENT_FRAGMENT_NODE))
1038         {
1039             result = start.querySelectorAll(selector);
1040         }
1041         else
1042         {
1043             result = context.baseWindow.document.querySelectorAll(selector);
1044         }
1045 
1046         return Arr.cloneArray(result);
1047     };
1048 
1049     // returns unwrapped elements from the page
1050     this.$x = function(xpath, contextNode, resultType)
1051     {
1052         var XPathResultType = XPathResult.ANY_TYPE;
1053 
1054         switch (resultType)
1055         {
1056             case "number":
1057                 XPathResultType = XPathResult.NUMBER_TYPE;
1058                 break;
1059 
1060             case "string":
1061                 XPathResultType = XPathResult.STRING_TYPE;
1062                 break;
1063 
1064             case "bool":
1065                 XPathResultType = XPathResult.BOOLEAN_TYPE;
1066                 break;
1067 
1068             case "node":
1069                 XPathResultType = XPathResult.FIRST_ORDERED_NODE_TYPE;
1070                 break;
1071 
1072             case "nodes":
1073                 XPathResultType = XPathResult.UNORDERED_NODE_ITERATOR_TYPE;
1074                 break;
1075         }
1076 
1077         var doc = Wrapper.unwrapObject(context.baseWindow.document);
1078         return Xpath.evaluateXPath(doc, xpath, contextNode, XPathResultType);
1079     };
1080 
1081     // values from the extension space
1082     this.$n = function(index)
1083     {
1084         var htmlPanel = context.getPanel("html", true);
1085         if (!htmlPanel)
1086             return null;
1087 
1088         if (index < 0 || index >= htmlPanel.inspectorHistory.length)
1089             return null;
1090 
1091         var node = htmlPanel.inspectorHistory[index];
1092         if (!node)
1093             return node;
1094 
1095         return Wrapper.unwrapObject(node);
1096     };
1097 
1098     this.cd = function(object)
1099     {
1100         if (!(object instanceof window.Window))
1101             throw "Object must be a window.";
1102 
1103         // Make sure the command line is attached into the target iframe.
1104         var consoleReady = Firebug.Console.isReadyElsePreparing(context, object);
1105         if (FBTrace.DBG_COMMANDLINE)
1106             FBTrace.sysout("commandLine.cd; console ready: " + consoleReady);
1107 
1108         // The window object parameter uses XPCSafeJSObjectWrapper, but we need XPCNativeWrapper
1109         // So, look within all registered consoleHandlers for
1110         // the same window (from tabWatcher) that uses uses XPCNativeWrapper (operator "==" works).
1111         var entry = Firebug.Console.injector.getConsoleHandler(context, object);
1112         if (entry)
1113             context.baseWindow = entry.win;
1114 
1115         var format = Locale.$STR("commandline.CurrentWindow") + " %o";
1116         Firebug.Console.logFormatted([format, context.baseWindow], context, "info");
1117         return Firebug.Console.getDefaultReturnValue(context.window);
1118     };
1119 
1120     // no web page interaction
1121     this.clear = function()
1122     {
1123         Firebug.Console.clear(context);
1124         return Firebug.Console.getDefaultReturnValue(context.window);
1125     };
1126 
1127     // no web page interaction
1128     this.inspect = function(obj, panelName)
1129     {
1130         Firebug.chrome.select(obj, panelName);
1131         return Firebug.Console.getDefaultReturnValue(context.window);
1132     };
1133 
1134     this.keys = function(o)
1135     {
1136         // the object is from the page, unwrapped
1137         return Arr.keys(o);
1138     };
1139 
1140     this.values = function(o)
1141     {
1142         // the object is from the page, unwrapped
1143         return Arr.values(o);
1144     };
1145 
1146     this.debug = function(fn)
1147     {
1148         Firebug.Debugger.monitorFunction(fn, "debug");
1149         return Firebug.Console.getDefaultReturnValue(context.window);
1150     };
1151 
1152     this.undebug = function(fn)
1153     {
1154         Firebug.Debugger.unmonitorFunction(fn, "debug");
1155         return Firebug.Console.getDefaultReturnValue(context.window);
1156     };
1157 
1158     this.monitor = function(fn)
1159     {
1160         Firebug.Debugger.monitorFunction(fn, "monitor");
1161         return Firebug.Console.getDefaultReturnValue(context.window);
1162     };
1163 
1164     this.unmonitor = function(fn)
1165     {
1166         Firebug.Debugger.unmonitorFunction(fn, "monitor");
1167         return Firebug.Console.getDefaultReturnValue(context.window);
1168     };
1169 
1170     this.traceAll = function()
1171     {
1172         Firebug.Debugger.traceAll(Firebug.currentContext);
1173         return Firebug.Console.getDefaultReturnValue(context.window);
1174     };
1175 
1176     this.untraceAll = function()
1177     {
1178         Firebug.Debugger.untraceAll(Firebug.currentContext);
1179         return Firebug.Console.getDefaultReturnValue(context.window);
1180     };
1181 
1182     this.traceCalls = function(fn)
1183     {
1184         Firebug.Debugger.traceCalls(Firebug.currentContext, fn);
1185         return Firebug.Console.getDefaultReturnValue(context.window);
1186     };
1187 
1188     this.untraceCalls = function(fn)
1189     {
1190         Firebug.Debugger.untraceCalls(Firebug.currentContext, fn);
1191         return Firebug.Console.getDefaultReturnValue(context.window);
1192     };
1193 
1194     this.copy = function(x)
1195     {
1196         System.copyToClipboard(x);
1197         return Firebug.Console.getDefaultReturnValue(context.window);
1198     };
1199 
1200     // xxxHonza: removed from 1.10 (issue 5599)
1201     /*this.memoryProfile = function(title)
1202     {
1203         Firebug.MemoryProfiler.start(context, title);
1204         return Firebug.Console.getDefaultReturnValue(context.window);
1205     };
1206 
1207     this.memoryProfileEnd = function()
1208     {
1209         Firebug.MemoryProfiler.stop(context);
1210         return Firebug.Console.getDefaultReturnValue(context.window);
1211     };*/
1212 
1213     function createHandler(config, name)
1214     {
1215         return function()
1216         {
1217             try
1218             {
1219                 return config.handler.call(null, context, arguments);
1220             }
1221             catch (err)
1222             {
1223                 Firebug.Console.log(err, context, "errorMessage");
1224 
1225                 if (FBTrace.DBG_ERRORS)
1226                 {
1227                     FBTrace.sysout("commandLine.api; EXCEPTION when executing " +
1228                         "a command: " + name + ", " + err, err);
1229                 }
1230             }
1231         }
1232     }
1233 
1234     // Register user commands.
1235     var commands = CommandLineExposed.userCommands;
1236     for (var name in commands)
1237     {
1238         var config = commands[name];
1239         this[name] = createHandler(config, name);
1240     }
1241 }
1242 
1243 // ********************************************************************************************* //
1244 // CommandLine Injector
1245 
1246 Firebug.CommandLine.injector =
1247 {
1248     isAttached: function(win)
1249     {
1250         var contentView = Wrapper.getContentView(win);
1251         return contentView._FirebugCommandLine ? true : false;
1252     },
1253 
1254     attachCommandLine: function(context, win)
1255     {
1256         win = win ? win : context.window;
1257         if (win instanceof win.Window)
1258         {
1259             // If the command line is already attached then end.
1260             if (this.isAttached(win))
1261                 return;
1262 
1263             var contentView = Wrapper.getContentView(win);
1264             contentView._FirebugCommandLine =
1265                 Firebug.CommandLineExposed.createFirebugCommandLine(context, win);
1266 
1267             this.addCommandLineListener(context, win);
1268         }
1269         else if (Firebug.CommandLine.isSandbox(context))
1270         {
1271             if (FBTrace.DBG_COMMANDLINE)
1272             {
1273                 FBTrace.sysout("commandLine.injector context.global " + context.global,
1274                     context.global);
1275             }
1276         }
1277         else
1278         {
1279             if (FBTrace.DBG_COMMANDLINE)
1280             {
1281                 FBTrace.sysout("commandLine.injector, win: " + win +
1282                     " not a Window or Sandbox", win);
1283             }
1284         }
1285     },
1286 
1287     detachCommandLine: function(context, win)
1288     {
1289         win = win ? win : context.window;
1290         if (this.isAttached(win))
1291         {
1292             function failureCallback(result, context)
1293             {
1294                 if (FBTrace.DBG_ERRORS)
1295                     FBTrace.sysout("Firebug.CommandLine.evaluate FAILS  " + result, result);
1296             }
1297 
1298             //Firebug.CommandLine.evaluate("window._FirebugCommandLine.detachCommandLine()",
1299             //    context, null, win, null, failureCallback );
1300             var contentView = Wrapper.getContentView(win);
1301             contentView._FirebugCommandLine.detachCommandLine();
1302 
1303             this.removeCommandLineListener(context, win);
1304         }
1305     },
1306 
1307     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1308     // Listener
1309 
1310     addCommandLineListener: function(context, win)
1311     {
1312         // Register listener for command-line execution events.
1313         var handler = new CommandLineHandler(context, win);
1314         var boundHandler = Obj.bind(handler.handleEvent, handler);
1315 
1316         if (!context.activeCommandLineHandlers)
1317             context.activeCommandLineHandlers = {};
1318 
1319         var consoleHandler = Firebug.Console.injector.getConsoleHandler(context, win);
1320         if (!consoleHandler)
1321         {
1322             if (FBTrace.DBG_ERRORS || FBTrace.DBG_COMMANDLINE)
1323                 FBTrace.sysout("commandLine.addCommandLineListener; No console handler! " +
1324                     " Command line listener can't be created." +  context.getName());
1325             return;
1326         }
1327 
1328         context.activeCommandLineHandlers[consoleHandler.token] = boundHandler;
1329 
1330         Events.addEventListener(win.document, "firebugExecuteCommand", boundHandler, true);
1331 
1332         if (FBTrace.DBG_COMMANDLINE)
1333         {
1334             FBTrace.sysout("commandLine.addCommandLineListener to document in window" +
1335                 win.location + " with console ");
1336         }
1337     },
1338 
1339     removeCommandLineListener: function(context, win)
1340     {
1341         var boundHandler = this.getCommandLineListener(context, win);
1342         if (boundHandler)
1343         {
1344             Events.removeEventListener(win.document, "firebugExecuteCommand", boundHandler, true);
1345 
1346             var consoleHandler = Firebug.Console.injector.getConsoleHandler(context, win);
1347             delete context.activeCommandLineHandlers[consoleHandler.token];
1348 
1349             if (FBTrace.DBG_COMMANDLINE)
1350             {
1351                 FBTrace.sysout("commandLine.detachCommandLineListener " + boundHandler +
1352                     " in window with console " + win.location);
1353             }
1354         }
1355         else
1356         {
1357             if (FBTrace.DBG_ERRORS || FBTrace.DBG_COMMANDLINE)
1358             {
1359                 FBTrace.sysout("commandLine.removeCommandLineListener; ERROR no handler! " +
1360                     "This could cause memory leaks, please report an issue if you see this. " +
1361                     context.getName());
1362             }
1363         }
1364     },
1365 
1366     getCommandLineListener: function(context, win)
1367     {
1368         if (context.activeCommandLineHandlers)
1369         {
1370             var consoleHandler = Firebug.Console.injector.getConsoleHandler(context, win);
1371             if (consoleHandler)
1372                 return context.activeCommandLineHandlers[consoleHandler.token];
1373 
1374             if (FBTrace.DBG_CONSOLE)
1375             {
1376                 FBTrace.sysout("getCommandLineListener no consoleHandler for " +
1377                     context.getName() + " win " + Win.safeGetWindowLocation(win));
1378             }
1379         }
1380     },
1381 };
1382 
1383 // ********************************************************************************************* //
1384 // CommandLine Handler
1385 
1386 /**
1387  * This object is responsible for handling commands executing in the page context.
1388  * When a command (CMD API) is being executed, the page sends a DOM event that is
1389  * handled by 'handleEvent' method.
1390  *
1391  * @param {Object} context
1392  * @param {Object} win is the window the handler is bound into
1393  */
1394 function CommandLineHandler(context, win)
1395 {
1396     this.handleEvent = function(event)
1397     {
1398         context.baseWindow = context.baseWindow || context.window;
1399         this.api = new FirebugCommandLineAPI(context);
1400 
1401         if (FBTrace.DBG_COMMANDLINE)
1402         {
1403             FBTrace.sysout("commandLine.handleEvent('firebugExecuteCommand') " +
1404                 "event in context.baseWindow " + context.baseWindow.location, event);
1405         }
1406 
1407         // Appends variables into the api.
1408         var htmlPanel = context.getPanel("html", true);
1409         var vars = htmlPanel ? htmlPanel.getInspectorVars() : null;
1410 
1411         for (var prop in vars)
1412         {
1413             function createHandler(p)
1414             {
1415                 return function()
1416                 {
1417                     if (FBTrace.DBG_COMMANDLINE)
1418                         FBTrace.sysout("commandLine.getInspectorHistory: " + p, vars);
1419 
1420                     return Wrapper.unwrapObject(vars[p]);
1421                 }
1422             }
1423 
1424             // XXXjjb should these be removed?
1425             this.api[prop] = createHandler(prop);
1426         }
1427 
1428         if (!Firebug.CommandLine.CommandHandler.handle(event, this.api, win))
1429         {
1430             var methodName = Dom.getMappedData(win.document, "firebug-methodName");
1431             Firebug.Console.log(Locale.$STRF("commandline.MethodNotSupported", [methodName]));
1432         }
1433 
1434         if (FBTrace.DBG_COMMANDLINE)
1435         {
1436             FBTrace.sysout("commandLine.handleEvent() " +
1437                 Dom.getMappedData(win.document, "firebug-methodName") +
1438                 " context.baseWindow: " +
1439                 (context.baseWindow ? context.baseWindow.location : "no basewindow"),
1440                 context.baseWindow);
1441         }
1442     };
1443 }
1444 
1445 function getNoScript()
1446 {
1447     // The wrappedJSObject here is not a security wrapper, it is a property set by the service.
1448     if (!this.noscript)
1449         this.noscript = Cc["@maone.net/noscript-service;1"] &&
1450             Cc["@maone.net/noscript-service;1"].getService().wrappedJSObject;
1451     return this.noscript;
1452 }
1453 
1454 
1455 // ********************************************************************************************* //
1456 // Registration
1457 
1458 Firebug.registerModule(Firebug.CommandLine);
1459 
1460 return Firebug.CommandLine;
1461 
1462 // ********************************************************************************************* //
1463 });
1464