1 /* See license.txt for terms of usage */
  2 
  3 //
  4 FBL.ns(function() { with (FBL) {
  5 
  6 // ************************************************************************************************
  7 // Constants
  8 
  9 const Cc = Components.classes;
 10 const Ci = Components.interfaces;
 11 
 12 top.Firebug.Console.injector =
 13 {
 14     isAttached: function(context, win)
 15     {
 16         if (win.wrappedJSObject)
 17         {
 18             var attached = (win.wrappedJSObject._getFirebugConsoleElement ? true : false);
 19             if (FBTrace.DBG_CONSOLE)
 20                 FBTrace.sysout("Console.isAttached:"+attached+" to win.wrappedJSObject "+safeGetWindowLocation(win.wrappedJSObject));
 21 
 22             return attached;
 23         }
 24         else
 25         {
 26             if (FBTrace.DBG_CONSOLE)
 27                 FBTrace.sysout("Console.isAttached? to win "+win.location+" fnc:"+win._getFirebugConsoleElement);
 28             return (win._getFirebugConsoleElement ? true : false);
 29         }
 30     },
 31 
 32     attachIfNeeded: function(context, win)
 33     {
 34         if (FBTrace.DBG_CONSOLE)
 35             FBTrace.sysout("Console.attachIfNeeded has win "+(win? ((win.wrappedJSObject?"YES":"NO")+" wrappedJSObject"):"null") );
 36 
 37         if (this.isAttached(context, win))
 38             return true;
 39 
 40         if (FBTrace.DBG_CONSOLE)
 41             FBTrace.sysout("Console.attachIfNeeded found isAttached false ");
 42 
 43         this.attachConsoleInjector(context, win);
 44         this.addConsoleListener(context, win);
 45 
 46         Firebug.Console.clearReloadWarning(context);
 47 
 48         var attached =  this.isAttached(context, win);
 49         if (attached)
 50             dispatch(Firebug.Console.fbListeners, "onConsoleInjected", [context, win]);
 51 
 52         return attached;
 53     },
 54 
 55     attachConsoleInjector: function(context, win)
 56     {
 57         var consoleInjection = this.getConsoleInjectionScript();  // Do it all here.
 58 
 59         if (FBTrace.DBG_CONSOLE)
 60             FBTrace.sysout("attachConsoleInjector evaluating in "+win.location, consoleInjection);
 61 
 62         Firebug.CommandLine.evaluateInWebPage(consoleInjection, context, win);
 63 
 64         if (FBTrace.DBG_CONSOLE)
 65             FBTrace.sysout("attachConsoleInjector evaluation completed for "+win.location);
 66     },
 67 
 68     getConsoleInjectionScript: function() {
 69         if (!this.consoleInjectionScript)
 70         {
 71             var script = "";
 72             script += "window.__defineGetter__('console', function() {\n";
 73             script += " return (window._firebug ? window._firebug : window.loadFirebugConsole()); })\n\n";
 74 
 75             script += "window.loadFirebugConsole = function() {\n";
 76             script += "window._firebug =  new _FirebugConsole();";
 77 
 78             if (FBTrace.DBG_CONSOLE)
 79                 script += " window.dump('loadFirebugConsole '+window.location+'\\n');\n";
 80 
 81             script += " return window._firebug };\n";
 82 
 83             var theFirebugConsoleScript = getResource("chrome://firebug/content/consoleInjected.js");
 84             script += theFirebugConsoleScript;
 85 
 86 
 87             this.consoleInjectionScript = script;
 88         }
 89         return this.consoleInjectionScript;
 90     },
 91 
 92     forceConsoleCompilationInPage: function(context, win)
 93     {
 94         if (!win)
 95         {
 96             if (FBTrace.DBG_CONSOLE)
 97                 FBTrace.sysout("no win in forceConsoleCompilationInPage!");
 98             return;
 99         }
100 
101         var consoleForcer = "window.loadFirebugConsole();";
102 
103         if (context.stopped)
104             Firebug.Console.injector.evaluateConsoleScript(context);  // todo evaluate consoleForcer on stack
105         else
106             Firebug.CommandLine.evaluateInWebPage(consoleForcer, context, win);
107 
108         if (FBTrace.DBG_CONSOLE)
109             FBTrace.sysout("forceConsoleCompilationInPage "+win.location, consoleForcer);
110     },
111 
112     evaluateConsoleScript: function(context)
113     {
114         var scriptSource = this.getConsoleInjectionScript(); // TODO XXXjjb this should be getConsoleInjectionScript
115         Firebug.Debugger.evaluate(scriptSource, context);
116     },
117 
118     addConsoleListener: function(context, win)
119     {
120         if (!context.activeConsoleHandlers)  // then we have not been this way before
121             context.activeConsoleHandlers = [];
122         else
123         {   // we've been this way before...
124             for (var i=0; i<context.activeConsoleHandlers.length; i++)
125             {
126                 if (context.activeConsoleHandlers[i].window == win)
127                 {
128                     context.activeConsoleHandlers[i].detach();
129                     if (FBTrace.DBG_CONSOLE)
130                         FBTrace.sysout("consoleInjector addConsoleListener removed handler("+context.activeConsoleHandlers[i].handler_name+") from _firebugConsole in : "+win.location+"\n");
131                     context.activeConsoleHandlers.splice(i,1);
132                 }
133             }
134         }
135 
136         // We need the element to attach our event listener.
137         var element = Firebug.Console.getFirebugConsoleElement(context, win);
138         if (element)
139             element.setAttribute("FirebugVersion", Firebug.version); // Initialize Firebug version.
140         else
141             return false;
142 
143         var handler = new FirebugConsoleHandler(context, win);
144         handler.attachTo(element);
145 
146         context.activeConsoleHandlers.push(handler);
147 
148         if (FBTrace.DBG_CONSOLE)
149             FBTrace.sysout("consoleInjector addConsoleListener attached handler("+handler.handler_name+") to _firebugConsole in : "+win.location+"\n");
150         return true;
151     },
152 
153     detachConsole: function(context, win)
154     {
155         if (win && win.document)
156         {
157             var element = win.document.getElementById("_firebugConsole");
158             if (element)
159                 element.parentNode.removeChild(element);
160         }
161     },
162 }
163 
164 var total_handlers = 0;
165 function FirebugConsoleHandler(context, win)
166 {
167     this.window = win;
168 
169     this.attachTo = function(element)
170     {
171         this.element = element;
172         // When raised on our injected element, callback to Firebug and append to console
173         this.boundHandler = bind(this.handleEvent, this);
174         this.element.addEventListener('firebugAppendConsole', this.boundHandler, true); // capturing
175     };
176 
177     this.detach = function()
178     {
179         this.element.removeEventListener('firebugAppendConsole', this.boundHandler, true);
180     };
181 
182     this.handler_name = ++total_handlers;
183     this.handleEvent = function(event)
184     {
185         if (FBTrace.DBG_CONSOLE)
186             FBTrace.sysout("FirebugConsoleHandler("+this.handler_name+") "+event.target.getAttribute("methodName")+", event", event);
187         if (!Firebug.CommandLine.CommandHandler.handle(event, this, win))
188         {
189             if (FBTrace.DBG_CONSOLE)
190                 FBTrace.sysout("FirebugConsoleHandler", this);
191 
192             var methodName = event.target.getAttribute("methodName");
193             Firebug.Console.log($STRF("console.MethodNotSupported", [methodName]));
194         }
195     };
196 
197     this.firebug = Firebug.version;
198 
199     this.init = function()
200     {
201         var consoleElement = win.document.getElementById('_firebugConsole');
202         consoleElement.setAttribute("FirebugVersion", Firebug.version);
203     };
204 
205     this.log = function()
206     {
207         logFormatted(arguments, "log");
208     };
209 
210     this.debug = function()
211     {
212         logFormatted(arguments, "debug", true);
213     };
214 
215     this.info = function()
216     {
217         logFormatted(arguments, "info", true);
218     };
219 
220     this.warn = function()
221     {
222         logFormatted(arguments, "warn", true);
223     };
224 
225     this.error = function()
226     {
227         if (arguments.length == 1)
228         {
229             logAssert("error", arguments);  // add more info based on stack trace
230         }
231         else
232         {
233             Firebug.Errors.increaseCount(context);
234             logFormatted(arguments, "error", true);  // user already added info
235         }
236     };
237 
238     this.exception = function()
239     {
240         logAssert("error", arguments);
241     };
242 
243     this.assert = function(x)
244     {
245         if (!x)
246         {
247             var rest = [];
248             for (var i = 1; i < arguments.length; i++)
249                 rest.push(arguments[i]);
250             logAssert("assert", rest);
251         }
252     };
253 
254     this.dir = function(o)
255     {
256         Firebug.Console.log(o, context, "dir", Firebug.DOMPanel.DirTable);
257     };
258 
259     this.dirxml = function(o)
260     {
261         if (o instanceof Window)
262             o = o.document.documentElement;
263         else if (o instanceof Document)
264             o = o.documentElement;
265 
266         Firebug.Console.log(o, context, "dirxml", Firebug.HTMLPanel.SoloElement);
267     };
268 
269     this.group = function()
270     {
271         var sourceLink = getStackLink();
272         Firebug.Console.openGroup(arguments, null, "group", null, false, sourceLink);
273     };
274 
275     this.groupEnd = function()
276     {
277         Firebug.Console.closeGroup(context);
278     };
279 
280     this.groupCollapsed = function()
281     {
282         var sourceLink = getStackLink();
283         // noThrottle true is probably ok, openGroups will likely be short strings.
284         var row = Firebug.Console.openGroup(arguments, null, "group", null, true, sourceLink);
285         removeClass(row, "opened");
286     };
287 
288     this.profile = function(title)
289     {
290         Firebug.Profiler.startProfiling(context, title);
291     };
292 
293     this.profileEnd = function()
294     {
295         Firebug.Profiler.stopProfiling(context);
296     };
297 
298     this.count = function(key)
299     {
300         var frameId = FBL.getStackFrameId();
301         if (frameId)
302         {
303             if (!context.frameCounters)
304                 context.frameCounters = {};
305 
306             if (key != undefined)
307                 frameId += key;
308 
309             var frameCounter = context.frameCounters[frameId];
310             if (!frameCounter)
311             {
312                 var logRow = logFormatted(["0"], null, true, true);
313 
314                 frameCounter = {logRow: logRow, count: 1};
315                 context.frameCounters[frameId] = frameCounter;
316             }
317             else
318                 ++frameCounter.count;
319 
320             var label = key == undefined
321                 ? frameCounter.count
322                 : key + " " + frameCounter.count;
323 
324             frameCounter.logRow.firstChild.firstChild.nodeValue = label;
325         }
326     };
327 
328     this.clear = function()
329     {
330         Firebug.Console.clear(context);
331     };
332 
333     this.time = function(name, reset)
334     {
335         if (!name)
336             return;
337 
338         var time = new Date().getTime();
339 
340         if (!this.timeCounters)
341             this.timeCounters = {};
342 
343         var key = "KEY"+name.toString();
344 
345         if (!reset && this.timeCounters[key])
346             return;
347 
348         this.timeCounters[key] = time;
349     };
350 
351     this.timeEnd = function(name)
352     {
353         var time = new Date().getTime();
354 
355         if (!this.timeCounters)
356             return;
357 
358         var key = "KEY"+name.toString();
359 
360         var timeCounter = this.timeCounters[key];
361         if (timeCounter)
362         {
363             var diff = time - timeCounter;
364             var label = name + ": " + diff + "ms";
365 
366             this.info(label);
367 
368             delete this.timeCounters[key];
369         }
370         return diff;
371     };
372 
373     // These functions are over-ridden by commandLine
374     this.evaluated = function(result, context)
375     {
376         if (FBTrace.DBG_CONSOLE)
377             FBTrace.sysout("consoleInjector.FirebugConsoleHandler evalutated default called", result);
378 
379         Firebug.Console.log(result, context);
380     };
381     this.evaluateError = function(result, context)
382     {
383         Firebug.Console.log(result, context, "errorMessage");
384     };
385 
386     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
387 
388     function logFormatted(args, className, linkToSource, noThrottle)
389     {
390         var sourceLink = linkToSource ? getStackLink() : null;
391         return Firebug.Console.logFormatted(args, context, className, noThrottle, sourceLink);
392     }
393 
394     function logAssert(category, args)
395     {
396         Firebug.Errors.increaseCount(context);
397 
398         if (!args || !args.length || args.length == 0)
399             var msg = [FBL.$STR("Assertion")];
400         else
401             var msg = args[0];
402 
403         if (Firebug.errorStackTrace)
404         {
405             var trace = Firebug.errorStackTrace;
406             delete Firebug.errorStackTrace;
407             if (FBTrace.DBG_CONSOLE)
408                 FBTrace.sysout("logAssert trace from errorStackTrace", trace);
409         }
410         else if (msg.stack)
411         {
412             var trace = parseToStackTrace(msg.stack);
413             if (FBTrace.DBG_CONSOLE)
414                 FBTrace.sysout("logAssert trace from msg.stack", trace);
415         }
416         else
417         {
418             var trace = getJSDUserStack();
419             if (FBTrace.DBG_CONSOLE)
420                 FBTrace.sysout("logAssert trace from getJSDUserStack", trace);
421         }
422 
423         var errorObject = new FBL.ErrorMessage(msg, (msg.fileName?msg.fileName:win.location), (msg.lineNumber?msg.lineNumber:0), "", category, context, trace);
424 
425 
426         if (trace && trace.frames && trace.frames[0])
427            errorObject.correctWithStackTrace(trace);
428 
429         errorObject.resetSource();
430 
431         var objects = errorObject;
432         if (args.length > 1)
433         {
434             objects = [errorObject];
435             for (var i = 1; i < args.length; i++)
436                 objects.push(args[i]);
437         }
438 
439         var row = Firebug.Console.log(objects, context, "errorMessage", null, true); // noThrottle
440         row.scrollIntoView();
441     }
442 
443     function getComponentsStackDump()
444     {
445         // Starting with our stack, walk back to the user-level code
446         var frame = Components.stack;
447         var userURL = win.location.href.toString();
448 
449         if (FBTrace.DBG_CONSOLE)
450             FBTrace.sysout("consoleInjector.getComponentsStackDump initial stack for userURL "+userURL, frame);
451 
452         // Drop frames until we get into user code.
453         while (frame && FBL.isSystemURL(frame.filename) )
454             frame = frame.caller;
455 
456         // Drop two more frames, the injected console function and firebugAppendConsole()
457         if (frame)
458             frame = frame.caller;
459         if (frame)
460             frame = frame.caller;
461 
462         if (FBTrace.DBG_CONSOLE)
463             FBTrace.sysout("consoleInjector.getComponentsStackDump final stack for userURL "+userURL, frame);
464 
465         return frame;
466     }
467 
468     function getStackLink()
469     {
470         return FBL.getFrameSourceLink(getComponentsStackDump());
471     }
472 
473     function getJSDUserStack()
474     {
475         var trace = FBL.getCurrentStackTrace(context);
476 
477         var frames = trace ? trace.frames : null;
478         if (frames && (frames.length > 0) )
479         {
480             var oldest = frames.length - 1;  // 6 - 1 = 5
481             for (var i = 0; i < frames.length; i++)
482             {
483                 if (frames[oldest - i].href.indexOf("chrome:") == 0) break;
484                 var fn = frames[oldest - i].fn + "";
485                 if (fn && (fn.indexOf("_firebugEvalEvent") != -1) ) break;  // command line
486             }
487             FBTrace.sysout("consoleInjector getJSDUserStack: "+frames.length+" oldest: "+oldest+" i: "+i+" i - oldest + 2: "+(i - oldest + 2), trace);
488             trace.frames = trace.frames.slice(2 - i);  // take the oldest frames, leave 2 behind they are injection code
489 
490             return trace;
491         }
492         else
493             return "Firebug failed to get stack trace with any frames";
494     }
495 }
496 
497 }});
498