1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/object",
  5     "firebug/firebug",
  6     "firebug/chrome/firefox",
  7     "firebug/lib/events",
  8     "firebug/chrome/window",
  9     "firebug/lib/search",
 10     "firebug/lib/xml",
 11     "firebug/lib/options",
 12     "firebug/console/profiler",
 13     "firebug/chrome/searchBox",
 14     "firebug/console/consolePanel",
 15     "firebug/console/commandEditor",
 16     "firebug/console/functionMonitor",
 17     "firebug/console/performanceTiming",
 18 ],
 19 function(Obj, Firebug, Firefox, Events, Win, Search, Xml, Options) {
 20 
 21 // ********************************************************************************************* //
 22 // Constants
 23 
 24 const Cc = Components.classes;
 25 const Ci = Components.interfaces;
 26 
 27 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 28 
 29 var maxQueueRequests = 500;
 30 
 31 // ********************************************************************************************* //
 32 
 33 Firebug.ConsoleBase =
 34 {
 35     log: function(object, context, className, rep, noThrottle, sourceLink)
 36     {
 37         Events.dispatch(this.fbListeners,"log",[context, object, className, sourceLink]);
 38         return this.logRow(appendObject, object, context, className, rep, sourceLink, noThrottle);
 39     },
 40 
 41     logFormatted: function(objects, context, className, noThrottle, sourceLink)
 42     {
 43         Events.dispatch(this.fbListeners,"logFormatted",[context, objects, className, sourceLink]);
 44         return this.logRow(appendFormatted, objects, context, className, null, sourceLink,
 45             noThrottle);
 46     },
 47 
 48     openGroup: function(objects, context, className, rep, noThrottle, sourceLink, noPush)
 49     {
 50         return this.logRow(appendOpenGroup, objects, context, className, rep, sourceLink,
 51             noThrottle);
 52     },
 53 
 54     openCollapsedGroup: function(objects, context, className, rep, noThrottle, sourceLink, noPush)
 55     {
 56         return this.logRow(appendCollapsedGroup, objects, context, className, rep, sourceLink,
 57             noThrottle);
 58     },
 59 
 60     closeGroup: function(context, noThrottle)
 61     {
 62         return this.logRow(appendCloseGroup, null, context, null, null, null, noThrottle, true);
 63     },
 64 
 65     logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
 66     {
 67         if (!context)
 68             context = Firebug.currentContext;
 69 
 70         if (FBTrace.DBG_ERRORS && FBTrace.DBG_CONSOLE && !context)
 71             FBTrace.sysout("Console.logRow has no context, skipping objects", objects);
 72 
 73         if (!context)
 74             return;
 75 
 76         if (noThrottle || !context)
 77         {
 78             var panel = this.getPanel(context);
 79             if (panel)
 80             {
 81                 var row = panel.append(appender, objects, className, rep, sourceLink, noRow);
 82                 var container = panel.panelNode;
 83                 var template = Firebug.NetMonitor.NetLimit;
 84 
 85                 while (container.childNodes.length > maxQueueRequests + 1)
 86                 {
 87                     container.removeChild(container.firstChild.nextSibling);
 88                     panel.limit.limitInfo.totalCount++;
 89                     template.updateCounter(panel.limit);
 90                 }
 91                 Events.dispatch(this.fbListeners, "onLogRowCreated", [panel, row]);
 92                 return row;
 93             }
 94         }
 95         else
 96         {
 97             if (!context.throttle)
 98             {
 99                 FBTrace.sysout("console.logRow has not context.throttle! ");
100                 return;
101             }
102             var args = [appender, objects, context, className, rep, sourceLink, true, noRow];
103             context.throttle(this.logRow, this, args);
104         }
105     },
106 
107     appendFormatted: function(args, row, context)
108     {
109         if (!context)
110             context = Firebug.currentContext;
111 
112         var panel = this.getPanel(context);
113         panel.appendFormatted(args, row);
114     },
115 
116     clear: function(context)
117     {
118         if (!context)
119             context = Firebug.currentContext;
120 
121         if (context)
122         {
123             // There could be some logs waiting in the throttle queue, so
124             // clear asynchronously after the queue is flushed.
125             context.throttle(this.clearPanel, this, [context]);
126 
127             // Also clear now
128             this.clearPanel(context);
129 
130             // Let listeners react to console clearing
131             Events.dispatch(this.fbListeners, "onConsoleCleared", [context]);
132         }
133     },
134 
135     clearPanel: function(context)
136     {
137         Firebug.Errors.clear(context);
138 
139         var panel = this.getPanel(context, true);
140         if (panel)
141             panel.clear();
142     },
143 
144     // Override to direct output to your panel
145     getPanel: function(context, noCreate)
146     {
147         if (context)
148             return context.getPanel("console", noCreate);
149     },
150 };
151 
152 // ********************************************************************************************* //
153 
154 var ActivableConsole = Obj.extend(Firebug.ActivableModule, Firebug.ConsoleBase);
155 
156 Firebug.Console = Obj.extend(ActivableConsole,
157 {
158     dispatchName: "console",
159     toolName: "console",
160 
161     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
162     // extends Module
163 
164     showPanel: function(browser, panel)
165     {
166     },
167 
168     // this is the only code that should call injector.attachIfNeeded
169     isReadyElsePreparing: function(context, win)
170     {
171         if (FBTrace.DBG_CONSOLE)
172             FBTrace.sysout("console.isReadyElsePreparing, win is " +
173                 (win?"an argument: ":"null, context.window: ") +
174                 (win?win.location:context.window.location));
175 
176         if (Xml.isXMLPrettyPrint(context, win))
177             return false;
178 
179         if (win)
180         {
181             return this.injector.attachIfNeeded(context, win);
182         }
183         else
184         {
185             var attached = true;
186             for (var i = 0; i < context.windows.length; i++)
187                 attached = attached && this.injector.attachIfNeeded(context, context.windows[i]);
188 
189             // already in the list above:
190             // attached = attached && this.injector.attachIfNeeded(context, context.window);
191             if (context.windows.indexOf(context.window) == -1)
192                 FBTrace.sysout("isReadyElsePreparing: context.window not in context.windows");
193 
194             if (FBTrace.DBG_CONSOLE)
195                 FBTrace.sysout("console.isReadyElsePreparing attached to " +
196                     context.windows.length + " and returns "+attached);
197 
198             return attached;
199         }
200     },
201 
202     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
203     // extends Module
204 
205     initialize: function()
206     {
207         // Initialize log limit.
208         this.updateMaxLimit();
209 
210         Firebug.ActivableModule.initialize.apply(this, arguments);
211 
212         Firebug.connection.addListener(this);
213 
214         this.syncFilterButtons(Firebug.chrome);
215     },
216 
217     shutdown: function()
218     {
219         Firebug.connection.removeListener(this);
220         Firebug.ActivableModule.shutdown.apply(this, arguments);
221     },
222 
223     initContext: function(context, persistedState)
224     {
225         Firebug.ActivableModule.initContext.apply(this, arguments);
226         context.consoleReloadWarning = true;  // mark as need to warn.
227     },
228 
229     loadedContext: function(context)
230     {
231         for (var url in context.sourceFileMap)
232             return;  // if there are any sourceFiles, then do nothing
233 
234         // Inject console handler if not injected yet. It's injected only in the case that
235         // the page has JS (and thus may call console) and Firebug has been activated after
236         // the first JS call (and thus we have not already injected).
237         if (!this.injector.isAttached(context, context.window) && !context.jsDebuggerCalledUs)
238             this.isReadyElsePreparing(context);
239 
240         // else we saw no JS, so the reload warning is not needed.
241         this.clearReloadWarning(context);
242     },
243 
244     clearReloadWarning: function(context) // remove the warning about reloading.
245     {
246         if (context.consoleReloadWarning)
247         {
248             var panel = context.getPanel("console");
249             if (panel)
250             {
251                 panel.clearReloadWarning();
252                 delete context.consoleReloadWarning;
253             }
254         }
255     },
256 
257     togglePersist: function(context)
258     {
259         var panel = context.getPanel("console");
260         panel.persistContent = panel.persistContent ? false : true;
261 
262         Firebug.chrome.setGlobalAttribute("cmd_firebug_togglePersistConsole", "checked",
263             panel.persistContent);
264     },
265 
266     showContext: function(browser, context)
267     {
268         Firebug.chrome.setGlobalAttribute("cmd_firebug_clearConsole", "disabled", !context);
269 
270         Firebug.ActivableModule.showContext.apply(this, arguments);
271     },
272 
273     destroyContext: function(context, persistedState)
274     {
275         Win.iterateWindows(context.window, function detachOneConsole(win)
276         {
277             // remove this first since it needs the console
278             Firebug.CommandLine.injector.detachCommandLine(context, win);
279             Firebug.Console.injector.detachConsole(context, win);
280         });
281     },
282 
283     unwatchWindow: function(context, win)
284     {
285         Firebug.Console.injector.detachConsole(context, win);
286     },
287 
288     updateOption: function(name, value)
289     {
290         if (name == "console.logLimit")
291             this.updateMaxLimit();
292     },
293 
294     updateMaxLimit: function()
295     {
296         var value = Options.get("console.logLimit");
297         maxQueueRequests =  value ? value : maxQueueRequests;
298     },
299 
300     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
301     // extend ActivableModule
302 
303     onObserverChange: function(observer)
304     {
305         if (this.isAlwaysEnabled())
306         {
307             // we inject the console during JS compiles so we need jsd
308             Firebug.Debugger.addObserver(this);
309         }
310         else
311         {
312             Firebug.Debugger.removeObserver(this);
313         }
314 
315         if (!Firebug.getSuspended())  // then Firebug is in action
316             this.onResumeFirebug();   // and we need to test to see if we need to addObserver
317         else
318             this.onSuspendFirebug();
319     },
320 
321     onSuspendFirebug: function()
322     {
323         if (FBTrace.DBG_CONSOLE)
324             FBTrace.sysout("console.onSuspendFirebug isAlwaysEnabled:" +
325                 Firebug.Console.isAlwaysEnabled());
326 
327         if (Firebug.Errors.toggleWatchForErrors(false))
328         {
329             this.setStatus();
330             // Make sure possible errors coming from the page and displayed in the Firefox
331             // status bar are removed.
332             this.clear();
333         }
334     },
335 
336     onResumeFirebug: function()
337     {
338         if (FBTrace.DBG_CONSOLE)
339             FBTrace.sysout("console.onResumeFirebug\n");
340 
341         var watchForErrors = Firebug.Console.isAlwaysEnabled() || Firebug.Console.hasObservers();
342         if (Firebug.Errors.toggleWatchForErrors(watchForErrors))
343             this.setStatus();
344     },
345 
346     onToggleFilter: function(context, filterType)
347     {
348         if (!context)
349             context = Firebug.currentContext;
350 
351         /* Preparation for multiple filters (see issue 4621)
352         if (filterType == "")
353             Firebug.consoleFilterTypes = "";
354         else
355         {
356             var index = Firebug.consoleFilterTypes.indexOf(filterType);
357             if (index >= 0)
358                 Firebug.consoleFilterTypes = Firebug.consoleFilterTypes.substr(0, index-1) +
359                     Firebug.consoleFilterTypes.substr(index+filterType.length);
360             else
361                 Firebug.consoleFilterTypes += " " + filterType;
362         }
363         */
364 
365         Firebug.consoleFilterTypes = filterType;
366 
367         Options.set("consoleFilterTypes", Firebug.consoleFilterTypes);
368 
369         var panel = this.getPanel(context, true);
370         if (panel)
371         {
372             panel.setFilter(Firebug.consoleFilterTypes);
373             Firebug.Search.update(context);
374         }
375     },
376 
377     syncFilterButtons: function(chrome)
378     {
379         if (Firebug.consoleFilterTypes == "")
380         {
381             var button = chrome.$("fbConsoleFilter-all");
382             button.checked = true;
383         }
384         else
385         {
386             var filterTypes = Firebug.consoleFilterTypes.split(" ");
387             for (var type = 0; type < filterTypes.length; type++)
388             {
389                 var button = chrome.$("fbConsoleFilter-" + filterTypes[type]);
390                 button.checked = true;
391             }
392         }
393     },
394 
395     setStatus: function()
396     {
397         var fbStatus = Firefox.getElementById("firebugStatus");
398         if (fbStatus)
399         {
400             if (Firebug.Errors.watchForErrors)
401                 fbStatus.setAttribute("console", "on");
402             else
403                 fbStatus.removeAttribute("console");
404         }
405         else
406         {
407             if (FBTrace.DBG_ERRORS)
408                 FBTrace.sysout("console.setStatus ERROR no firebugStatus element");
409         }
410     },
411 
412     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
413     // BTI
414 
415     /**
416      * A previously enabled tool becomes active and sends us an event.
417      */
418     onActivateTool: function(toolname, active)
419     {
420         if (FBTrace.DBG_ACTIVATION)
421             FBTrace.sysout("Console.onActivateTool "+toolname+" = "+active);
422 
423         // Console depends on script to get injected (for now)
424         if (toolname === "script")
425         {
426             if (this.isAlwaysEnabled())
427             {
428                 //this.asTool.setActive(active);  // then track the activation of the debugger;
429             }
430         }
431     },
432 
433     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
434 
435     logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
436     {
437         if (!context)
438             context = Firebug.currentContext;
439 
440         if (FBTrace.DBG_WINDOWS && !context)
441             FBTrace.sysout("Console.logRow: no context \n");
442 
443         if (this.isAlwaysEnabled())
444             return Firebug.ConsoleBase.logRow.apply(this, arguments);
445     },
446 
447     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
448 
449     getDefaultReturnValue: function(win)
450     {
451         var defaultValue = "_firebugIgnore";
452         var console = win.wrappedJSObject.console;
453         if (!console)
454             return defaultValue;
455 
456         if (Obj.isNonNativeGetter(console, "__returnValue__"))
457             return defaultValue;
458 
459         var returnValue = console.__returnValue__;
460         if (returnValue)
461             return returnValue;
462 
463         return defaultValue;
464     }
465 });
466 
467 // ********************************************************************************************* //
468 
469 Firebug.ConsoleListener =
470 {
471     log: function(context, object, className, sourceLink)
472     {
473     },
474 
475     logFormatted: function(context, objects, className, sourceLink)
476     {
477     }
478 };
479 
480 // ********************************************************************************************* //
481 
482 var appendObject = Firebug.ConsolePanel.prototype.appendObject;
483 var appendFormatted = Firebug.ConsolePanel.prototype.appendFormatted;
484 var appendOpenGroup = Firebug.ConsolePanel.prototype.appendOpenGroup;
485 var appendCollapsedGroup = Firebug.ConsolePanel.prototype.appendCollapsedGroup;
486 var appendCloseGroup = Firebug.ConsolePanel.prototype.appendCloseGroup;
487 
488 // ********************************************************************************************* //
489 // Registration
490 
491 Firebug.registerActivableModule(Firebug.Console);
492 
493 return Firebug.Console;
494 
495 // ********************************************************************************************* //
496 });
497