1 /* See license.txt for terms of usage */
  2 
  3 FBL.ns(function() { with (FBL) {
  4 
  5 // ************************************************************************************************
  6 // Constants
  7 const Cc = Components.classes;
  8 const Ci = Components.interfaces;
  9 
 10 const commandHistoryMax = 1000;
 11 const commandPrefix = ">>>";
 12 
 13 const reOpenBracket = /[\[\(\{]/;
 14 const reCloseBracket = /[\]\)\}]/;
 15 const reCmdSource = /^with\(_FirebugCommandLine\){(.*)};$/;
 16 
 17 // ************************************************************************************************
 18 // GLobals
 19 
 20 var commandHistory = [""];
 21 var commandPointer = 0;
 22 var commandInsertPointer = -1;
 23 
 24 // ************************************************************************************************
 25 
 26 Firebug.CommandLine = extend(Firebug.Module,
 27 {
 28     dispatchName: "commandLine",
 29     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 30 
 31     // targetWindow was needed by evaluateInSandbox, let's leave it for a while in case we rethink this yet again
 32 
 33     initializeCommandLineIfNeeded: function (context, win)
 34     {
 35       if (context == null) return;
 36       if (win == null) return;
 37 
 38       // The command-line requires that the console has been initialized first,
 39       // so make sure that's so.  This call should have no effect if the console
 40       // is already initialized.
 41       var consoleIsReady = Firebug.Console.isReadyElsePreparing(context, win);
 42 
 43       // Make sure the command-line is initialized.  This call should have no
 44       // effect if the command-line is already initialized.
 45       var commandLineIsReady = Firebug.CommandLine.isReadyElsePreparing(context, win);
 46 
 47       if (FBTrace.DBG_CONSOLE)
 48           FBTrace.sysout("initializeCommandLineIfNeeded console ready: "+consoleIsReady +" commandLine ready: "+commandLineIsReady);
 49     },
 50 
 51     evaluate: function(expr, context, thisValue, targetWindow, successConsoleFunction, exceptionFunction) // returns user-level wrapped object I guess.
 52     {
 53         if (!context)
 54             return;
 55 
 56         var debuggerState = Firebug.Debugger.beginInternalOperation();
 57         try
 58         {
 59             var result = null;
 60 
 61             if (context.stopped)
 62             {
 63                 result = this.evaluateInDebugFrame(expr, context, thisValue, targetWindow,  successConsoleFunction, exceptionFunction);
 64             }
 65             else
 66             {
 67                 result = this.evaluateByEventPassing(expr, context, thisValue, targetWindow,  successConsoleFunction, exceptionFunction);
 68             }
 69 
 70             context.invalidatePanels('dom', 'html');
 71         }
 72         catch (exc)  // XXX jjb, I don't expect this to be taken, the try here is for the finally
 73         {
 74             if (FBTrace.DBG_ERRORS)
 75                 FBTrace.sysout("CommandLine.evaluate with context.stopped:"+context.stopped+" FAILS:"+exc, exc);
 76         }
 77         finally
 78         {
 79             Firebug.Debugger.endInternalOperation(debuggerState);
 80         }
 81 
 82         return result;
 83     },
 84 
 85     evaluateByEventPassing: function(expr, context, thisValue, targetWindow, successConsoleFunction, exceptionFunction)
 86     {
 87         var win = targetWindow ? targetWindow : ( context.baseWindow ? context.baseWindow : context.window );
 88         if (!win)
 89         {
 90             if (FBTrace.DBG_ERRORS) FBTrace.sysout("commandLine.evaluateByEventPassing: no targetWindow!\n");
 91             return;
 92         }
 93 
 94         // We're going to use some command-line facilities, but it may not have initialized yet.
 95         this.initializeCommandLineIfNeeded(context, win);
 96 
 97         // Make sure the command line script is attached.
 98         var element = Firebug.Console.getFirebugConsoleElement(context, win);
 99         if (element)
100         {
101             var attached = element.getAttribute("firebugCommandLineAttached");
102             if (!attached)
103             {
104                 FBTrace.sysout("Firebug console element does not have command line attached its too early for command line", element);
105                 Firebug.Console.logFormatted(["Firebug cannot find firebugCommandLineAttached attribute on firebug console element, its too early for command line", element, win], context, "error", true);
106                 return;
107             }
108         }
109         else
110         {
111             if (FBTrace.DBG_ERRORS) FBTrace.sysout("commandLine.evaluateByEventPassing: no firebug console element", win);
112             return;  // we're in trouble here
113         }
114 
115         var event = document.createEvent("Events");
116         event.initEvent("firebugCommandLine", true, false);
117         element.setAttribute("methodName", "evaluate");
118 
119         expr = expr.toString();
120         expr = "with(_FirebugCommandLine){" + expr + "\n};";
121         element.setAttribute("expr", expr);
122 
123         var consoleHandler;
124         for (var i=0; i<context.activeConsoleHandlers.length; i++)
125         {
126             if (context.activeConsoleHandlers[i].window == win)
127             {
128                 consoleHandler = context.activeConsoleHandlers[i];
129                 break;
130             }
131         }
132 
133         if (successConsoleFunction)
134         {
135             consoleHandler.evaluated = function useConsoleFunction(result)
136             {
137                 successConsoleFunction(result, context);  // result will be pass thru this function
138             }
139         }
140 
141         if (exceptionFunction)
142         {
143             consoleHandler.evaluateError = function useExceptionFunction(result)
144             {
145                 exceptionFunction(result, context);
146             }
147         }
148         else
149         {
150             consoleHandler.evaluateError = function useErrorFunction(result)
151             {
152                 if (result)
153                 {
154                     var m = reCmdSource.exec(result.source);
155                     if (m && m.length > 0)
156                         result.source = m[1];
157                 }
158 
159                 Firebug.Console.logFormatted([result], context, "error", true);
160             }
161         }
162 
163         if (FBTrace.DBG_CONSOLE)
164             FBTrace.sysout("evaluateByEventPassing \'"+expr+"\' using consoleHandler:", consoleHandler);
165         element.dispatchEvent(event);
166         if (FBTrace.DBG_CONSOLE)
167             FBTrace.sysout("evaluateByEventPassing return after firebugCommandLine event:", event);
168     },
169 
170     evaluateInDebugFrame: function(expr, context, thisValue, targetWindow,  successConsoleFunction, exceptionFunction)
171     {
172         var result = null;
173 
174         // targetWindow may be frame in HTML
175         var win = targetWindow ? targetWindow : ( context.baseWindow ? context.baseWindow : context.window );
176 
177         if (!context.commandLineAPI)
178             context.commandLineAPI = new FirebugCommandLineAPI(context, (win.wrappedJSObject?win.wrappedJSObject:win));  // TODO should be baseWindow
179 
180         var htmlPanel = context.getPanel("html", true);
181         var scope = {
182             api       : context.commandLineAPI,
183             vars      : htmlPanel?htmlPanel.getInspectorVars():null,
184             thisValue : thisValue
185         };
186 
187         try
188         {
189             result = Firebug.Debugger.evaluate(expr, context, scope);
190             successConsoleFunction(result, context);  // result will be pass thru this function
191         }
192         catch (e)
193         {
194             exceptionFunction(e, context);
195         }
196         return result;
197     },
198 
199     //
200     evaluateInWebPage: function(expr, context, targetWindow)
201     {
202         var win = targetWindow ? targetWindow : context.window;
203         var doc = (win.wrappedJSObject ? win.wrappedJSObject.document : win.document);
204         var element = addScript(doc, "_firebugInWebPage", expr);
205         element.parentNode.removeChild(element);  // we don't need the script element, result is in DOM object
206         return "true";
207     },
208 
209     // TODO: strip down to minimum, have one global sandbox that is reused.
210     evaluateInSandbox: function(expr, context, thisValue, targetWindow, skipNotDefinedMessages)  // returns user-level wrapped object I guess.
211     {
212         // targetWindow may be frame in HTML
213         var win = targetWindow ? targetWindow : ( context.baseWindow ? context.baseWindow : context.window );
214 
215         if (!context.sandboxes)
216             context.sandboxes = [];
217 
218         if (win.wrappedJSObject) //  XPCNativeWrapper vs  XPCSafeJSObjectWrapper
219         {
220             // in FF3.1, this path fails.
221             var sandbox = new Components.utils.Sandbox(win.location.toString());
222             //sandbox.__proto__ = win.wrappedJSObject;
223             sandbox.__proto__ = win;
224         }
225         else
226         {
227             // in FF3.1, this path works
228             var sandbox = new Components.utils.Sandbox(win); // Use DOM Window
229             sandbox.__proto__ = win;
230         }
231 
232         var scriptToEval = expr;
233 
234         // If we want to use a specific |this|, wrap the expression with Function.apply()
235         // and inject the new |this| into the sandbox so it's easily accessible.
236         if (thisValue) {
237             // XXXdolske is this safe if we're recycling the sandbox?
238             sandbox.__thisValue__ = thisValue;
239             scriptToEval = "(function() { return " + scriptToEval + " \n}).apply(__thisValue__);";
240         }
241 
242         // Page scripts expect |window| to be the global object, not the
243         // sandbox object itself. Stick window into the scope chain so
244         // assignments like |foo = bar| are effectively |window.foo =
245         // bar|, else the page won't see the new value.
246         scriptToEval = "with (window?window:null) { " + scriptToEval + " \n};";
247 
248         try {
249             result = Components.utils.evalInSandbox(scriptToEval, sandbox);
250             if (FBTrace.DBG_CONSOLE)
251                 FBTrace.sysout("commandLine.evaluateInSandbox success for "+win.location, scriptToEval);
252         } catch (e) {
253             if (FBTrace.DBG_ERRORS)
254             {
255                 FBTrace.sysout("commandLine.evaluateInSandbox FAILED:"+e, e);
256                 FBTrace.sysout("commandLine.evaluateInSandbox FAILED with [win, scriptToEval, sandbox]: "+win.location, [win, scriptToEval, sandbox]);
257             }
258             result = new FBL.ErrorMessage("commandLine.evaluateInSandbox FAILED: " + e, FBL.getDataURLForContent(scriptToEval, "FirebugCommandLineEvaluate"), e.lineNumber, 0, "js", context, null);
259         }
260         return result;
261     },
262 
263     getSandboxByWindow: function(context, win)
264     {
265         for (var i = 0; i < context.sandboxes.length; i++) {
266             // XXXdolske is accessing .window safe after untrusted script has run?
267             if (context.sandboxes[i].window === win.wrappedJSObject)
268                 return context.sandboxes[i];
269         }
270         return null;
271     },
272 
273     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
274 
275     enter: function(context, command)
276     {
277         var commandLine = getCommandLine(context);
278         var expr = command ? command : commandLine.value;
279         if (expr == "")
280             return;
281         var MozJSEnabled = navigator.preference("javascript.enabled");
282 
283         if(MozJSEnabled)
284         {
285             if (!Firebug.largeCommandLine)
286             {
287                 this.clear(context);
288                 this.appendToHistory(expr);
289                 Firebug.Console.log(commandPrefix + " " + expr, context, "command", FirebugReps.Text);
290             }
291             else
292             {
293                 var shortExpr = cropString(stripNewLines(expr), 100);
294                 Firebug.Console.log(commandPrefix + " " + shortExpr, context, "command", FirebugReps.Text);
295             }
296 
297             var goodOrBad = FBL.bind(Firebug.Console.log, Firebug.Console);
298 
299             var noscript = getNoScript();
300             var uri = noscript && noscript.getSite(Firebug.chrome.getCurrentURI().spec);
301 
302             if(noscript && !(noscript.jsEnabled || noscript.isJSEnabled(uri)))
303             {
304 
305                 noscript.setJSEnabled(uri, true);
306                 this.evaluate(expr, context, null, null, goodOrBad);
307                 noscript.setJSEnabled(uri, false);
308             }
309             else
310                 this.evaluate(expr, context, null, null, goodOrBad);
311         }
312         else
313             Firebug.Console.log($STR("console.JSDisabledInFirefoxPrefs"), context, "info");
314     },
315 
316     enterMenu: function(context)
317     {
318         var commandLine = getCommandLine(context);
319         var expr = commandLine.value;
320         if (expr == "")
321             return;
322 
323         this.appendToHistory(expr, true);
324 
325         this.evaluate(expr, context, null, null, function(result, context)
326         {
327             if (typeof(result) != "undefined")
328             {
329                 Firebug.chrome.contextMenuObject = result;
330 
331                 var popup = Firebug.chrome.$("fbContextMenu");
332                 popup.showPopup(commandLine, -1, -1, "popup", "bottomleft", "topleft");
333             }
334         });
335     },
336 
337     enterInspect: function(context)
338     {
339         var commandLine = getCommandLine(context);
340         var expr = commandLine.value;
341         if (expr == "")
342             return;
343 
344         this.clear(context);
345         this.appendToHistory(expr);
346 
347         this.evaluate(expr, context, null, null, function(result, context)
348         {
349             if (typeof(result) != undefined)
350                 Firebug.chrome.select(result);
351         });
352     },
353 
354     reenter: function(context)
355     {
356         var command = commandHistory[commandInsertPointer];
357         if (command)
358             this.enter(context, command);
359     },
360 
361     copyBookmarklet: function(context)
362     {
363         var commandLine = getCommandLine(context);
364         var expr = "javascript: " + stripNewLines(commandLine.value);
365         copyToClipboard(expr);
366     },
367 
368     focus: function(context)
369     {
370         if (Firebug.isDetached())
371             Firebug.chrome.focus();
372         else
373             Firebug.toggleBar(true);
374 
375         if (!context.panelName)
376             Firebug.chrome.selectPanel("console");
377         else if (context.panelName != "console")
378         {
379             Firebug.chrome.switchToPanel(context, "console");
380 
381             var commandLine = getCommandLine(context);
382             setTimeout(function() { commandLine.select(); });
383         }
384         else // then we are already on the console, toggle back
385         {
386             Firebug.chrome.unswitchToPanel(context, "console", true);
387         }
388     },
389 
390     clear: function(context)
391     {
392         var commandLine = getCommandLine(context);
393         commandLine.value = context.commandLineText = "";
394         this.autoCompleter.reset();
395     },
396 
397     cancel: function(context)
398     {
399         var commandLine = getCommandLine(context);
400         if (!this.autoCompleter.revert(commandLine))
401             this.clear(context);
402     },
403 
404     update: function(context)
405     {
406         var commandLine = getCommandLine(context);
407         context.commandLineText = commandLine.value;
408         this.autoCompleter.reset();
409     },
410 
411     complete: function(context, reverse)
412     {
413         var commandLine = getCommandLine(context);
414         this.autoCompleter.complete(context, commandLine, true, reverse);
415         context.commandLineText = commandLine.value;
416     },
417 
418     setMultiLine: function(multiLine, chrome, saveMultiLine)
419     {
420         if (FirebugContext && FirebugContext.panelName != "console")
421             return;
422 
423         collapse(chrome.$("fbCommandBox"), multiLine);
424         collapse(chrome.$("fbPanelSplitter"), !multiLine);
425         collapse(chrome.$("fbSidePanelDeck"), !multiLine);
426 
427         if (multiLine)
428             chrome.$("fbSidePanelDeck").selectedPanel = chrome.$("fbLargeCommandBox");
429 
430         var commandLineSmall = chrome.$("fbCommandLine");
431         var commandLineLarge = chrome.$("fbLargeCommandLine");
432 
433         if (saveMultiLine)  // we are just closing the view
434         {
435             commandLineSmall.value = commandLineLarge.value;
436             return;
437         }
438 
439         if (multiLine)
440             commandLineLarge.value = cleanIndentation(commandLineSmall.value);
441         else
442             commandLineSmall.value = stripNewLines(commandLineLarge.value);
443     },
444 
445     toggleMultiLine: function(forceLarge)
446     {
447         var large = forceLarge || !Firebug.largeCommandLine;
448         if (large != Firebug.largeCommandLine)
449             Firebug.setPref(Firebug.prefDomain, "largeCommandLine", large);
450     },
451 
452     checkOverflow: function(context)
453     {
454         if (!context)
455             return;
456 
457         var commandLine = getCommandLine(context);
458         if (commandLine.value.indexOf("\n") >= 0)
459         {
460             setTimeout(bindFixed(function()
461             {
462                 Firebug.setPref(Firebug.prefDomain, "largeCommandLine", true);
463             }, this));
464         }
465     },
466 
467     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
468 
469     appendToHistory: function(command, unique)
470     {
471         if (unique && commandHistory[commandInsertPointer] == command)
472             return;
473 
474         ++commandInsertPointer;
475         if (commandInsertPointer >= commandHistoryMax)
476             commandInsertPointer = 0;
477 
478         commandPointer = commandInsertPointer+1;
479         commandHistory[commandInsertPointer] = command;
480     },
481 
482     cycleCommandHistory: function(context, dir)
483     {
484         var commandLine = getCommandLine(context);
485 
486         commandHistory[commandPointer] = commandLine.value;
487 
488         if (dir < 0)
489         {
490             --commandPointer;
491             if (commandPointer < 0)
492                 commandPointer = 0;
493         }
494         else
495         {
496             ++commandPointer;
497             if (commandPointer > commandInsertPointer+1)
498                 commandPointer = commandInsertPointer+1;
499         }
500 
501         var command = commandHistory[commandPointer];
502 
503         this.autoCompleter.reset();
504 
505         commandLine.value = context.commandLineText = command;
506         commandLine.inputField.setSelectionRange(command.length, command.length);
507     },
508 
509     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
510     // extends Module
511 
512     initialize: function()
513     {
514         Firebug.Module.initialize.apply(this, arguments);
515 
516         this.autoCompleter = new Firebug.AutoCompleter(getExpressionOffset, getDot,
517             autoCompleteEval, false, true);
518 
519         if (Firebug.largeCommandLine)
520             this.setMultiLine(true, Firebug.chrome);
521     },
522 
523     initializeUI: function()
524     {
525         this.attachListeners();
526     },
527 
528     reattachContext: function(browser, context)
529     {
530         this.attachListeners();
531     },
532 
533     attachListeners: function()
534     {
535         Firebug.chrome.$("fbLargeCommandLine").addEventListener('focus', this.onCommandLineFocus, true);
536         Firebug.chrome.$("fbCommandLine").addEventListener('focus', this.onCommandLineFocus, true);
537 
538         Firebug.Console.addListener(this);  // to get onConsoleInjection
539     },
540 
541     showContext: function(browser, context)
542     {
543         var command = Firebug.chrome.$("cmd_focusCommandLine");
544         command.setAttribute("disabled", !context);
545     },
546 
547     showPanel: function(browser, panel)
548     {
549         var chrome = Firebug.chrome;
550 
551         var isConsole = panel && panel.name == "console";
552         if (Firebug.largeCommandLine)
553         {
554             if (isConsole)
555             {
556                 collapse(chrome.$("fbPanelSplitter"), false);
557                 collapse(chrome.$("fbSidePanelDeck"), false);
558                 chrome.$("fbSidePanelDeck").selectedPanel = chrome.$("fbLargeCommandBox");
559                 collapse(chrome.$("fbCommandBox"), true);
560             }
561         }
562         else
563             collapse(chrome.$("fbCommandBox"), !isConsole);
564 
565         var value = panel ? panel.context.commandLineText : null;
566         var commandLine = getCommandLine(browser);
567         commandLine.value = value ? value : "";
568     },
569 
570     updateOption: function(name, value)
571     {
572         if (name == "largeCommandLine")
573             this.setMultiLine(value, Firebug.chrome);
574     },
575 
576     // called by users of command line, currently:
577     // 1) Console on focus command line, 2) Watch onfocus, and 3) debugger loadedContext if watches exist
578     isReadyElsePreparing: function(context, win)
579     {
580         if (FBTrace.DBG_CONSOLE)
581             FBTrace.sysout("command line isReadyElsePreparing ", context);
582 
583         if (win)
584             Firebug.CommandLine.injector.attachCommandLine(context, win);
585         else
586         {
587             Firebug.CommandLine.injector.attachCommandLine(context, context.window);
588             for (var i = 0; i < context.windows.length; i++)
589                 Firebug.CommandLine.injector.attachCommandLine(context, context.windows[i]);
590         }
591 
592         if (!context.window.wrappedJSObject)
593         {
594             FBTrace.sysout("context.window with no wrappedJSObject!", context.window);
595             return false;
596         }
597 
598         // the attach is asynchronous, we can report when it is complete:
599         if (context.window.wrappedJSObject._FirebugCommandLine)
600             return true;
601         else
602             return false;
603     },
604 
605     onCommandLineFocus: function(event)
606     {
607         Firebug.CommandLine.attachConsoleOnFocus();
608 
609         if (!Firebug.CommandLine.isAttached(FirebugContext))
610         {
611             return Firebug.CommandLine.isReadyElsePreparing(FirebugContext);
612         }
613         else
614         {
615             if (FBTrace.DBG_CONSOLE)
616             {
617                 try
618                 {
619                     var cmdLine = FirebugContext.window.wrappedJSObject._FirebugCommandLine
620                     FBTrace.sysout("onCommandLineFocus, attachCommandLine ", cmdLine);
621                 }
622                 catch (e)
623                 {
624                     FBTrace.sysout("onCommandLineFocus, did NOT attachCommandLine ", e);
625                 }
626             }
627             return true; // is attached.
628         }
629     },
630 
631     isAttached: function(context)
632     {
633         return context && context.window && context.window.wrappedJSObject && context.window.wrappedJSObject._FirebugCommandLine;
634     },
635 
636     attachConsoleOnFocus: function()
637     {
638         if (!FirebugContext)
639         {
640             if (FBTrace.DBG_ERRORS)
641                 FBTrace.sysout("commandLine.attachConsoleOnFocus no FirebugContext");
642             return;
643         }
644 
645         if (FBTrace.DBG_CONSOLE)
646             FBTrace.sysout("attachConsoleOnFocus: FirebugContext is "+FirebugContext.getName() +" in window "+window.location);
647 
648 
649         // User has decided to use the command line, but the web page may not have the console if the page has no javascript
650         if (Firebug.Console.isReadyElsePreparing(FirebugContext))
651         {
652             // the page had _firebug so we know that consoleInjected.js compiled and ran.
653             if (FBTrace.DBG_CONSOLE)
654             {
655                 if (FirebugContext)
656                     FBTrace.sysout("attachConsoleOnFocus: ", (FirebugContext.window?FirebugContext.window.wrappedJSObject._firebug:"No FirebugContext.window"));
657                 else
658                     FBTrace.sysout("attachConsoleOnFocus: No FirebugContext\n");
659             }
660         }
661         else
662         {
663             Firebug.Console.injector.forceConsoleCompilationInPage(FirebugContext, FirebugContext.window);
664 
665             if (FBTrace.DBG_CONSOLE)
666                 FBTrace.sysout("attachConsoleOnFocus, attachConsole "+FirebugContext.window.location+"\n");
667         }
668     },
669 
670     onPanelEnable: function(panelName)
671     {
672         collapse(Firebug.chrome.$("fbCommandBox"), true);
673         collapse(Firebug.chrome.$("fbPanelSplitter"), true);
674         collapse(Firebug.chrome.$("fbSidePanelDeck"), true);
675 
676         this.setMultiLine(Firebug.largeCommandLine, Firebug.chrome);
677     },
678 
679     onPanelDisable: function(panelName)
680     {
681         if (panelName != 'console')  // we don't care about other panels
682             return;
683 
684         collapse(Firebug.chrome.$("fbCommandBox"), true);
685         collapse(Firebug.chrome.$("fbPanelSplitter"), true);
686         collapse(Firebug.chrome.$("fbSidePanelDeck"), true);
687     },
688 
689     // *********************************************************************************************
690     // Firebug.Console listener
691     onConsoleInjected: function(context, win)
692     {
693         // for some reason the console has been injected. If the user had focus in the command line they want it added in the page also.
694         // If the user has the cursor in the command line and reloads, the focus will already be there. issue 1339
695         var isFocused = ($("fbLargeCommandLine").getAttribute("focused") == "true");
696         isFocused = isFocused || ($("fbCommandLine").getAttribute("focused") == "true");
697         if (isFocused)
698             setTimeout(this.onCommandLineFocus);
699     },
700 });
701 
702 // ************************************************************************************************
703 // Shared Helpers
704 
705 Firebug.CommandLine.CommandHandler = extend(Object,
706 {
707     handle: function(event, api, win)
708     {
709         var element = event.target;
710         var methodName = element.getAttribute("methodName");
711 
712         var hosed_userObjects = (win.wrappedJSObject?win.wrappedJSObject:win)._firebug.userObjects;
713 
714         var userObjects = hosed_userObjects ? cloneArray(hosed_userObjects) : [];
715 
716         if (FBTrace.DBG_CONSOLE)
717         {
718             var uid = element.getAttribute('uid');  // set if // DBG removed from Injected
719             FBTrace.sysout("Firebug.CommandLine.CommandHandler: ("+uid+") "+methodName+" userObjects:",  userObjects);
720             FBTrace.sysout("Firebug.CommandLine.CommandHandler: "+(win.wrappedJSObject?"win.wrappedJSObject._firebug":"win._firebug"), (win.wrappedJSObject?win.wrappedJSObject._firebug:win._firebug));
721             if (!userObjects)
722                 debugger;
723         }
724 
725         var subHandler = api[methodName];
726         if (!subHandler)
727             return false;
728 
729         element.removeAttribute("retValueType");
730         var result = subHandler.apply(api, userObjects);
731         if (typeof result != "undefined")
732         {
733             if (result instanceof Array)
734             {
735                 element.setAttribute("retValueType", "array");
736                 for (var item in result)
737                     hosed_userObjects.push(result[item]);
738             }
739             else
740             {
741                 hosed_userObjects.push(result);
742             }
743         }
744 
745         return true;
746     }
747 });
748 
749 // ************************************************************************************************
750 // Local Helpers
751 
752 function getExpressionOffset(command, offset)
753 {
754     // XXXjoe This is kind of a poor-man's JavaScript parser - trying
755     // to find the start of the expression that the cursor is inside.
756     // Not 100% fool proof, but hey...
757 
758     var bracketCount = 0;
759 
760     var start = command.length-1;
761     for (; start >= 0; --start)
762     {
763         var c = command[start];
764         if ((c == "," || c == ";" || c == " ") && !bracketCount)
765             break;
766         if (reOpenBracket.test(c))
767         {
768             if (bracketCount)
769                 --bracketCount;
770             else
771                 break;
772         }
773         else if (reCloseBracket.test(c))
774             ++bracketCount;
775     }
776 
777     return start + 1;
778 }
779 
780 function getDot(expr, offset)
781 {
782     var lastDot = expr.lastIndexOf(".");
783     if (lastDot == -1)
784         return null;
785     else
786         return {start: lastDot+1, end: expr.length-1};
787 }
788 
789 function autoCompleteEval(preExpr, expr, postExpr, context)
790 {
791     try
792     {
793         if (preExpr)
794         {
795             // Remove the trailing dot (if there is one)
796             var lastDot = preExpr.lastIndexOf(".");
797             if (lastDot != -1)
798                 preExpr = preExpr.substr(0, lastDot);
799 
800             var self = this;
801             Firebug.CommandLine.evaluate(preExpr, context, context.thisValue, null,
802                 function found(result, context)
803                 {
804                     if (FBTrace.DBG_CONSOLE)
805                         FBTrace.sysout("commandLine autoCompleteEval \'"+preExpr+"\' found result", result);
806 
807                     self.complete = keys(result).sort();
808                 },
809                 function failed(result, context)
810                 {
811                     if (FBTrace.DBG_CONSOLE)
812                         FBTrace.sysout("commandLine autoCompleteEval \'"+preExpr+"\' failed result", result);
813 
814                     self.complete = [];
815                 }
816             );
817             return self.complete;
818         }
819         else
820         {
821             if (context.stopped)
822                 return Firebug.Debugger.getCurrentFrameKeys(context);
823             else
824                 return keys(context.window.wrappedJSObject).sort();  // return is safe
825         }
826     }
827     catch (exc)
828     {
829         if (FBTrace.DBG_ERRORS)
830             FBTrace.sysout("commandLine.autoCompleteEval FAILED", exc);
831         return [];
832     }
833 }
834 
835 function injectScript(script, win)
836 {
837     win.location = "javascript: " + script;
838 }
839 
840 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
841 
842 function getCommandLine(context)
843 {
844     return Firebug.largeCommandLine
845         ? Firebug.chrome.$("fbLargeCommandLine")
846         : Firebug.chrome.$("fbCommandLine");
847 }
848 
849 const reIndent = /^(\s+)/;
850 
851 function getIndent(line)
852 {
853     var m = reIndent.exec(line);
854     return m ? m[0].length : 0;
855 }
856 
857 function cleanIndentation(text)
858 {
859     var lines = splitLines(text);
860 
861     var minIndent = -1;
862     for (var i = 0; i < lines.length; ++i)
863     {
864         var line = lines[i];
865         var indent = getIndent(line);
866         if (minIndent == -1 && line && !isWhitespace(line))
867             minIndent = indent;
868         if (indent >= minIndent)
869             lines[i] = line.substr(minIndent);
870     }
871     return lines.join("");
872 }
873 
874 // ************************************************************************************************
875 // Command line APIs definition
876 
877 function FirebugCommandLineAPI(context, baseWindow)
878 {
879     this.$ = function(id)
880     {
881         var doc = baseWindow.document;
882         return baseWindow.document.getElementById(id);
883     };
884 
885     this.$$ = function(selector)
886     {
887         return FBL.getElementsBySelector(baseWindow.document, selector);
888     };
889 
890     this.$x = function(xpath)
891     {
892         return FBL.getElementsByXPath(baseWindow.document, xpath);
893     };
894 
895     this.$n = function(index)
896     {
897         var htmlPanel = context.getPanel("html", true);
898         if (!htmlPanel)
899             return null;
900 
901         if (index < 0 || index >= htmlPanel.inspectorHistory.length)
902             return null;
903 
904         var node = htmlPanel.inspectorHistory[index];
905         if (!node)
906             return node;
907 
908         return node.wrappedJSObject;
909     };
910 
911     this.cd = function(object)
912     {
913         if (!(object instanceof Window))
914             throw "Object must be a window.";
915 
916         // The window object parameter uses XPCSafeJSObjectWrapper, but we need XPCNativeWrapper
917         // (and its wrappedJSObject member). So, look within all registered consoleHandlers for
918         // the same window (from tabWatcher) that uses uses XPCNativeWrapper (operator "==" works).
919         for (var i=0; i<context.activeConsoleHandlers.length; i++) {
920             if (context.activeConsoleHandlers[i].window == object) {
921                 baseWindow = context.baseWindow = context.activeConsoleHandlers[i].window;
922                 break;
923             }
924         }
925 
926         Firebug.Console.log(["Current window:", context.baseWindow], context, "info");
927     };
928 
929     this.clear = function()
930     {
931         Firebug.Console.clear(context);
932     };
933 
934     this.inspect = function(obj, panelName)
935     {
936         Firebug.chrome.select(obj, panelName);
937     };
938 
939     this.keys = function(o)
940     {
941         return FBL.keys(o);
942     };
943 
944     this.values = function(o)
945     {
946         return FBL.values(o);
947     };
948 
949     this.debug = function(fn)
950     {
951         Firebug.Debugger.monitorFunction(fn, "debug");
952     };
953 
954     this.undebug = function(fn)
955     {
956         Firebug.Debugger.unmonitorFunction(fn, "debug");
957     };
958 
959     this.monitor = function(fn)
960     {
961         Firebug.Debugger.monitorFunction(fn, "monitor");
962     };
963 
964     this.unmonitor = function(fn)
965     {
966         Firebug.Debugger.unmonitorFunction(fn, "monitor");
967     };
968 
969     this.traceAll = function()
970     {
971         Firebug.Debugger.traceAll(FirebugContext);
972     };
973 
974     this.untraceAll = function()
975     {
976         Firebug.Debugger.untraceAll(FirebugContext);
977     };
978 
979     this.traceCalls = function(fn)
980     {
981         Firebug.Debugger.traceCalls(FirebugContext, fn);
982     };
983 
984     this.untraceCalls = function(fn)
985     {
986         Firebug.Debugger.untraceCalls(FirebugContext, fn);
987     };
988 
989     this.monitorEvents = function(object, types)
990     {
991         monitorEvents(object, types, context);
992     };
993 
994     this.unmonitorEvents = function(object, types)
995     {
996         unmonitorEvents(object, types, context);
997     };
998 
999     this.profile = function(title)
1000     {
1001         Firebug.Profiler.startProfiling(context, title);
1002     };
1003 
1004     this.profileEnd = function()
1005     {
1006         Firebug.Profiler.stopProfiling(context);
1007     };
1008 
1009     this.copy = function(x)
1010     {
1011         FBL.copyToClipboard(x);
1012     };
1013 }
1014 
1015 // ************************************************************************************************
1016 
1017 Firebug.CommandLine.injector = {
1018 
1019     attachCommandLine: function(context, win)
1020     {
1021         if (!win)
1022             return;
1023 
1024         // If the command line is already attached then end.
1025         var doc = win.document;
1026         if ($("_firebugCommandLineInjector", doc))
1027             return;
1028 
1029         if (context.stopped)
1030             Firebug.CommandLine.injector.evalCommandLineScript(context);
1031         else
1032             Firebug.CommandLine.injector.injectCommandLineScript(doc);
1033 
1034         Firebug.CommandLine.injector.addCommandLineListener(context, win, doc);
1035     },
1036 
1037     evalCommandLineScript: function(context)
1038     {
1039         var scriptSource = getResource("chrome://firebug/content/commandLineInjected.js");
1040         Firebug.Debugger.evaluate(scriptSource, context);
1041         if (FBTrace.DBG_CONSOLE)
1042             FBTrace.sysout("evalCommandLineScript ", scriptSource);
1043     },
1044 
1045     injectCommandLineScript: function(doc)
1046     {
1047         // Inject command line script into the page.
1048         var scriptSource = getResource("chrome://firebug/content/commandLineInjected.js");
1049         var addedElement = addScript(doc, "_firebugCommandLineInjector", scriptSource);
1050         if (FBTrace.DBG_CONSOLE)
1051             FBTrace.sysout("injectCommandLineScript ", addedElement);
1052     },
1053 
1054     addCommandLineListener: function(context, win, doc)
1055     {
1056         // Register listener for command-line execution events.
1057         var handler = new CommandLineHandler(context, win);
1058         var element = Firebug.Console.getFirebugConsoleElement(context, win);
1059         element.addEventListener("firebugExecuteCommand", bind(handler.handleEvent, handler) , true);
1060         if (FBTrace.DBG_CONSOLE)
1061             FBTrace.sysout("addCommandLineListener to element in window with console "+win.location, win.console);
1062     }
1063 };
1064 
1065 function CommandLineHandler(context, win)
1066 {
1067     this.handleEvent = function(event)  // win is the window the handler is bound into
1068     {
1069         var baseWindow = context.baseWindow? context.baseWindow : context.window;
1070         this.api = new FirebugCommandLineAPI(context,  baseWindow.wrappedJSObject);
1071 
1072         if (FBTrace.DBG_CONSOLE)
1073             FBTrace.sysout("commandline.handleEvent('firebugExecuteCommand') event in baseWindow "+baseWindow.location, event);
1074 
1075         // Appends variables into the api.
1076         var htmlPanel = context.getPanel("html", true);
1077         var vars = htmlPanel?htmlPanel.getInspectorVars():null;
1078         for (var prop in vars)
1079         {
1080             function createHandler(p) {
1081                 return function() {
1082                     if (FBTrace.DBG_CONSOLE)
1083                         FBTrace.sysout("commandline.getInspectorHistory: " + p, vars);
1084                     return vars[p] ? vars[p].wrappedJSObject : null;
1085                 }
1086             }
1087             this.api[prop] = createHandler(prop);  // XXXjjb should these be removed?
1088         }
1089 
1090         if (!Firebug.CommandLine.CommandHandler.handle(event, this.api, win))
1091         {
1092             var methodName = event.target.getAttribute("methodName");
1093             Firebug.Console.log($STRF("commandline.MethodNotSupported", [methodName]));
1094         }
1095         if (FBTrace.DBG_CONSOLE)
1096             FBTrace.sysout("commandline.handleEvent() "+event.target.getAttribute("methodName")+" context.baseWindow: "+(context.baseWindow?context.baseWindow.location:"no basewindow"), context.baseWindow);
1097     };
1098 }
1099 
1100 function getNoScript()
1101 {
1102     if (!this.noscript)
1103         this.noscript = Cc["@maone.net/noscript-service;1"] &&
1104             Cc["@maone.net/noscript-service;1"].getService().wrappedJSObject;
1105     return this.noscript;
1106 }
1107 
1108 // ************************************************************************************************
1109 
1110 Firebug.registerModule(Firebug.CommandLine);
1111 
1112 // ************************************************************************************************
1113 
1114 }});
1115