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/xpcom",
  8     "firebug/console/console",
  9     "firebug/lib/css",
 10     "firebug/chrome/window",
 11     "firebug/lib/array",
 12     "firebug/lib/string"
 13 ],
 14 function(Obj, Firebug, FirebugReps, Xpcom, Console, Css, Win, Arr, Str) {
 15 
 16 // ********************************************************************************************* //
 17 // Constants
 18 
 19 const Cc = Components.classes;
 20 const Ci = Components.interfaces;
 21 const nsIScriptError = Ci.nsIScriptError;
 22 const nsIConsoleMessage = Ci.nsIConsoleMessage;
 23 
 24 const WARNING_FLAG = nsIScriptError.warningFlag;
 25 
 26 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 27 
 28 const urlRe = new RegExp("([^:]*):(//)?([^/]*)");
 29 const reUncaught = /uncaught exception/;
 30 // regular expessions for parsing uncaught exceptions
 31 // see http://lxr.mozilla.org/mozilla/source/js/src/xpconnect/src/xpcexception.cpp#347
 32 // and http://lxr.mozilla.org/mozilla/source/js/src/xpconnect/src/xpcstack.cpp#318
 33 // and http://lxr.mozilla.org/mozilla/source/dom/src/base/nsDOMException.cpp#351
 34 const reException1 = /^(?:uncaught exception: )?\[Exception... "(?!<no message>)([\s\S]+)"  nsresult: "0x\S+ \((.+)\)"  location: "(?:(?:JS|native) frame :: (?!<unknown filename>)(.+) :: .+ :: line (\d+)|<unknown>)"  data: (?:yes|no)\]$/
 35 const reException2 = /^(?:uncaught exception: )?\[Exception... "(?!<no message>)([\s\S]+)"  code: "\d+" nsresult: "0x\S+ \((.+)\)"  location: "(?:(.+) Line: (\d+)|<unknown>)"\]$/
 36 const pointlessErrors =
 37 {
 38     "uncaught exception: Permission denied to call method Location.toString": 1,
 39     "uncaught exception: Permission denied to get property Window.writeDebug": 1,
 40     "uncaught exception: Permission denied to get property XULElement.accessKey": 1,
 41     "this.docShell has no properties": 1,
 42     "aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation).currentURI has no properties": 1,
 43     "Deprecated property window.title used. Please use document.title instead.": 1,
 44     "Key event not available on GTK2:": 1
 45 };
 46 
 47 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 48 
 49 Components.utils["import"]("resource://firebug/firebug-service.js");
 50 
 51 const consoleService = Xpcom.CCSV("@mozilla.org/consoleservice;1", "nsIConsoleService");
 52 const domWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
 53     .getInterface(Ci.nsIDOMWindowUtils);
 54 
 55 // ********************************************************************************************* //
 56 
 57 var Errors = Firebug.Errors = Obj.extend(Firebug.Module,
 58 {
 59     dispatchName: "errors",
 60 
 61     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 62     // extends Module
 63 
 64     shutdown: function()
 65     {
 66         // Make sure the error obsever is removed.
 67         this.stopObserving();
 68 
 69         Firebug.Module.shutdown.apply(this, arguments);
 70     },
 71 
 72     initContext: function(context)
 73     {
 74         this.clear(context);
 75     },
 76 
 77     showContext: function(browser, context)
 78     {
 79         this.showCount(context ? context.errorCount : 0);
 80     },
 81 
 82     // called for top window and frames.
 83     unwatchWindow: function(context, win)
 84     {
 85         // If we ever get errors by window from Firefox we can cache by window.
 86         this.clear(context);
 87     },
 88 
 89     destroyContext: function(context, persistedState)
 90     {
 91         this.showCount(0);
 92 
 93         if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS && "initTime" in this)
 94         {
 95             var deltaT = new Date().getTime() - this.initTime.getTime();
 96 
 97             FBTrace.sysout("errors.destroyContext sheets: " + Css.totalSheets + " rules: " +
 98                 Css.totalRules + " time: " + deltaT);
 99         }
100     },
101 
102     updateOption: function(name, value)
103     {
104         this.checkEnabled();
105 
106         if (name == "showErrorCount")
107             this.toggleShowErrorCount();
108     },
109 
110     toggleShowErrorCount: function()
111     {
112         this.showContext(null, Firebug.currentContext);
113     },
114 
115     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
116 
117     clear: function(context)
118     {
119         // reset the UI counter
120         this.setCount(context, 0);
121 
122         // clear the counts of dropped errors
123         delete context.droppedErrors;
124     },
125 
126     increaseCount: function(context)
127     {
128         this.setCount(context, context.errorCount + 1)
129     },
130 
131     setCount: function(context, count)
132     {
133         context.errorCount = count;
134 
135         if (context == Firebug.currentContext)
136             this.showCount(context.errorCount);
137     },
138 
139     showCount: function(errorCount)
140     {
141         Firebug.StartButton.showCount(errorCount);
142     },
143 
144     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
145     // Called by Console
146 
147     startObserving: function()
148     {
149         if (this.isObserving)
150             return;
151 
152         if (FBTrace.DBG_ERRORLOG)
153             FBTrace.sysout("Errors.startObserving");
154 
155         if (consoleService)
156             consoleService.registerListener(this);
157 
158         this.isObserving = true;
159     },
160 
161     stopObserving: function()
162     {
163         if (!this.isObserving)
164             return;
165 
166         if (FBTrace.DBG_ERRORLOG)
167             FBTrace.sysout("Errors.stopObserving");
168 
169         if (consoleService)
170             consoleService.unregisterListener(this);
171 
172         this.isObserving = false;
173     },
174 
175     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
176     // extends consoleListener
177 
178     observe: function(object)
179     {
180         // Make sure the argument is an error object. 'instanceof' also
181         // queries the object so e.g. outerWindowID (nsIScriptError2) is available.
182         // nsIScriptError2 was merged with nsIScriptError, see
183         // https://bugzilla.mozilla.org/show_bug.cgi?id=711721
184         if (!(object instanceof Ci.nsIScriptError) ||
185             (Ci.nsIScriptError2 && !(object instanceof Ci.nsIScriptError2)))
186         {
187             return;
188         }
189 
190         try
191         {
192             if (window.closed)
193                 this.stopObserving();
194 
195             if (typeof FBTrace == "undefined")
196                 return;
197 
198             if (!FBTrace)
199                 return;
200         }
201         catch (exc)
202         {
203             return;
204         }
205 
206         try
207         {
208             this.onConsoleLog(object);
209         }
210         catch (exc)
211         {
212             // Errors prior to console init will come out here, eg error message
213             // from Firefox startup jjb.
214             if (FBTrace.DBG_ERRORLOG)
215             {
216                 FBTrace.sysout("errors.observe FAILS " + exc, exc);
217                 FBTrace.sysout("errors.observe object " + object, object);
218             }
219         }
220     },
221 
222     onConsoleLog: function(object)
223     {
224         var ScriptError = object instanceof nsIScriptError;
225         var ConsoleMessage = object instanceof nsIConsoleMessage;
226 
227         // This cannot be pulled in front of the instanceof
228         var isWarning = object && object.flags & WARNING_FLAG;
229         var CSSParser = object && object.category == "CSS Parser";
230         var XPConnect = object && object.category &&
231             object.category.split(' ').indexOf("XPConnect") != -1;
232 
233         // Some categories say "content javascript" even if they come from chrome space.
234         var sourceName = (object && object.sourceName) ? object.sourceName : "";
235         if (Str.hasPrefix(sourceName, "chrome:") || Str.hasPrefix(sourceName, "resource:"))
236             XPConnect = true;
237 
238         if (FBTrace.DBG_ERRORLOG)
239             FBTrace.sysout("errors.observe; ScriptError: " + ScriptError +
240                 ", XPConnect: " + XPConnect + ", sourceName: " + sourceName);
241 
242         if (ScriptError && !XPConnect)  // all branches should trace 'object'
243         {
244             if (FBTrace.DBG_ERRORLOG)
245             {
246                 FBTrace.sysout("errors.observe nsIScriptError: " + object.errorMessage,
247                     object);
248             }
249 
250             // after instanceof
251             var context = this.getErrorContext(object);
252             if (context)
253                 return this.logScriptError(context, object, isWarning);
254 
255             if (FBTrace.DBG_ERRORLOG)
256             {
257                 FBTrace.sysout("errors.observe nsIScriptError no context! " +
258                     object.errorMessage, object);
259             }
260         }
261         else
262         {
263             if (Firebug.showChromeMessages)
264             {
265                 if (ConsoleMessage)
266                 {
267                     if (FBTrace.DBG_ERRORLOG)
268                     {
269                         FBTrace.sysout("errors.observe nsIConsoleMessage: " +
270                             object.message, object);
271                     }
272 
273                     var context = this.getErrorContext(object);  // after instanceof
274                     if (!context)
275                         context = Firebug.currentContext;
276 
277                     var msgId = lessTalkMoreAction(context, object, isWarning);
278                     if (!msgId)
279                         return;
280 
281                     if (context)
282                     {
283                         // Even chrome errors can be nicely formatted in the Console panel
284                         this.logScriptError(context, object, isWarning);
285                         //Console.log(object.message, context, "consoleMessage",
286                         //FirebugReps.Text);
287                     }
288                 }
289                 else if (object.message)
290                 {
291                     if (FBTrace.DBG_ERRORLOG)
292                         FBTrace.sysout("errors.observe object.message:", object);
293 
294                     var context = this.getErrorContext(object);
295 
296                     if (!context)
297                         context = Firebug.currentContext;
298 
299                     if (context)
300                     {
301                         // Even chrome errors can be nicely formatted in the Console panel
302                         this.logScriptError(context, object, isWarning);
303                         //Console.log(object.message, context, "consoleMessage",
304                         //FirebugReps.Text);
305                     }
306                     else
307                     {
308                         FBTrace.sysout("errors.observe, no context for message", object);
309                     }
310                 }
311                 else
312                 {
313                     FBTrace.sysout("errors.observe, no message in object", object);
314                 }
315             }
316             else
317             {
318                 if (FBTrace.DBG_ERRORLOG)
319                     FBTrace.sysout("errors.observe showChromeMessages off, dropped:", object);
320                 return;
321             }
322         }
323 
324         if (FBTrace.DBG_ERRORLOG)
325         {
326             if (context)
327             {
328                 if (context.window)
329                 {
330                     FBTrace.sysout((isWarning?"warning":"error") + " logged to " +
331                         context.getName());
332                 }
333                 else
334                 {
335                     FBTrace.sysout("errors.observe, context with no window, " +
336                         (isWarning?"warning":"error")+" object:", object);
337 
338                     FBTrace.sysout("errors.observe, context with no window, context:",
339                         context);
340                 }
341             }
342             else
343             {
344                 FBTrace.sysout("errors.observe, no context!");
345             }
346         }
347     },
348 
349     logScriptError: function(context, object, isWarning)
350     {
351         if (!context)
352             return;
353 
354         if (FBTrace.DBG_ERRORLOG)
355         {
356             FBTrace.sysout("errors.observe logScriptError " +
357                 (Firebug.errorStackTrace ? "have " : "NO ") +
358                 (Firebug.showStackTrace ? "show stack trace" : "do not show stack trace ") +
359                 "errorStackTrace error object:",
360                 {object: object, errorStackTrace: Firebug.errorStackTrace});
361         }
362 
363         var category = getBaseCategory(object.category);
364         var isJSError = category == "js" && !isWarning;
365 
366         // the sourceLine will cause the source to be loaded.
367         var error = new FirebugReps.ErrorMessageObj(object.errorMessage, object.sourceName,
368             object.lineNumber, object.sourceLine, category, context, null, msgId);
369 
370         // Display column info only if it isn't zero.
371         if (object.columnNumber > 0)
372             error.colNumber = object.columnNumber;
373 
374         if (checkForException(context, object))
375         {
376             context = getExceptionContext(context, object);
377             correctLineNumbersOnExceptions(object, error);
378         }
379 
380         if (Firebug.errorStackTrace)
381         {
382             error.correctWithStackTrace(Firebug.errorStackTrace);
383             if (!Firebug.showStackTrace)
384                 error.trace = null;
385         }
386 
387         var msgId = lessTalkMoreAction(context, object, isWarning);
388         if (!msgId)
389             return null;
390 
391         // clear global: either we copied it or we don't use it.
392         Firebug.errorStackTrace = null;
393 
394         if (!isWarning)
395             this.increaseCount(context);
396 
397         var className = isWarning ? "warningMessage" : "errorMessage";
398 
399         if (FBTrace.DBG_ERRORLOG)
400             FBTrace.sysout("errors.observe delayed log to " + context.getName());
401 
402         // report later to avoid loading sourceS
403         context.throttle(this.delayedLogging, this, [msgId, context, error, context, className,
404             false, true], true);
405     },
406 
407     delayedLogging: function()
408     {
409         var args = Arr.cloneArray(arguments);
410         var msgId = args.shift();
411         var context = args.shift();
412         var row = Console.log.apply(Console, args);
413         return row;
414     },
415 
416     getErrorContext: function(object)
417     {
418         var url = object.sourceName;
419 
420         // If window is not associated bail out to avoid reporting errors that are not
421         // page related (issue 4991).
422         if (!url && !object.outerWindowID)
423         {
424             if (FBTrace.DBG_ERRORLOG)
425                 FBTrace.sysout("errors.getErrorContext; No URL & no outer-window. " +
426                     "url: " + url + ", outerWindowID: " + object.outerWindowID, object);
427             return null;
428         }
429 
430         // eg some XPCOM messages
431         // xxxHonza: this could cause appearing error messages in wrong tabs.
432         // Is this still necessary?
433         if (!url)
434             return Firebug.currentContext;
435 
436         if (url && url.indexOf("://chromebug/") > 0)
437             return Firebug.currentContext; // no context for self
438 
439         // Correct the error routing in the case that the new window id will work (R10860).
440         // Don't pass the current context (issue 4504)
441         var errorContext = getExceptionContext(null, object);
442         if (errorContext)
443             return errorContext;
444 
445         var errorContext = null;
446         Firebug.connection.eachContext(
447             function findContextByURL(context)
448             {
449                 if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS)
450                     FBTrace.sysout("findContextByURL " + context.getName());
451 
452                 if (!context.window || !context.getWindowLocation())
453                     return false;
454 
455                 // If the error's parent window is available, check if it
456                 // corresponds to the context.window. If not bail out to avoid
457                 // error reporting in a wrong window.
458                 var errorWindow = getErrorWindow(object);
459                 if (errorWindow && errorWindow != context.window)
460                     return false;
461 
462                 if (FBTrace.DBG_ERRORLOG)
463                 {
464                     FBTrace.sysout("findContextByURL seeking " + url + " in " +
465                         (context.loaded ? "loaded" : "not loaded") +
466                         " window location: " + context.getWindowLocation().toString());
467                 }
468 
469                 if (context.getWindowLocation().toString() == url)
470                 {
471                     if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS)
472                         FBTrace.sysout("findContextByURL found match to context window location");
473 
474                     return errorContext = context;
475                 }
476                 else
477                 {
478                     if (context.sourceFileMap && context.sourceFileMap[url])
479                     {
480                         if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS)
481                             FBTrace.sysout("findContextByURL found match in sourceFileMap");
482                         return errorContext = context;
483                     }
484                 }
485 
486                 if (context.loaded)
487                 {
488                     if (Css.getStyleSheetByHref(url, context))
489                     {
490                         if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS)
491                         {
492                             FBTrace.sysout("findContextByURL found match to in loaded " +
493                                 "styleSheetMap");
494                         }
495 
496                         return errorContext = context;
497                     }
498                     else
499                     {
500                         return false;
501                     }
502                 }
503                 else  // then new stylesheets are still coming in.
504                 {
505                     if (context.getCompilationUnit(url))
506                     {
507                         if (FBTrace.DBG_EERRORLOG)
508                             FBTrace.sysout("findContextByURL found match in compilationUnits");
509 
510                         return errorContext = context;
511                     }
512 
513                     if (Css.getStyleSheetByHref(url, context))
514                     {
515                         if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS)
516                         {
517                             FBTrace.sysout("findContextByURL found match to in non-loaded " +
518                                 "styleSheetMap");
519                         }
520 
521                         // but we already have this one.
522                         errorContext = context;
523                     }
524 
525                     // clear the cache for next time.
526                     delete context.styleSheetMap;
527                 }
528             });
529 
530         if (FBTrace.DBG_ERRORLOG && FBTrace.DBG_CSS && "initTime" in this)
531         {
532             var deltaT = new Date().getTime() - this.initTime.getTime();
533             FBTrace.sysout("errors.getErrorContext sheets: " + Css.totalSheets +
534                 " rules: " + Css.totalRules + " time: " + deltaT);
535         }
536 
537         if (!errorContext)
538         {
539             if (FBTrace.DBG_ERRORLOG)
540                 FBTrace.sysout("errors.getErrorContext no context from error filename:"+
541                     url, object);
542         }
543 
544         // Use nsIScriptError/nsIScriptError2 to compare the parent window
545         // guessed by Firebug with the window found through outerWindowID
546         if (FBTrace.DBG_ERRORLOG)
547         {
548             var win1 = getErrorWindow(object);
549             var win2 = errorContext ? errorContext.window : null;
550 
551             win1 = Win.getRootWindow(win1);
552             win2 = Win.getRootWindow(win2);
553 
554             var id1 = Win.getWindowProxyIdForWindow(win1);
555             var id2 = Win.getWindowProxyIdForWindow(win2);
556 
557             if (win1 && id1 != id2 && errorContext)
558             {
559                 var win1Name = Win.safeGetWindowLocation(win1);
560                 var win2Name = Win.safeGetWindowLocation(win2);
561                 var moreInfo = {object: object, fromError2: win1, fromFirebug: win2};
562 
563                 FBTrace.sysout("errors.getErrorContext; ERROR wrong parent window? " +
564                     win1Name + " !== " + win2Name, moreInfo);
565             }
566         }
567 
568         // we looked everywhere...
569         return errorContext;
570     },
571 
572     toggleWatchForErrors: function(watchForErrors)
573     {
574         var previous = this.watchForErrors;
575         this.watchForErrors = watchForErrors;
576         this.checkEnabled();
577 
578         return (previous !== this.watchForErrors);
579     },
580 
581     checkEnabled: function()
582     {
583         var beEnabled = this.watchForErrors && this.mustBeEnabled();
584         if (beEnabled)
585         {
586             if (!this.isObserving)
587                 this.startObserving();
588             // else we must be and we are observing
589         }
590         else
591         {
592             if (this.isObserving)
593                 this.stopObserving();
594             // else we must not be and we are not
595         }
596 
597         if (FBTrace.DBG_ERRORLOG)
598             FBTrace.sysout("errors.checkEnabled mustBeEnabled: " + this.mustBeEnabled() +
599                 " Console.isAlwaysEnabled " + Console.isAlwaysEnabled() +
600                 " isObserving:" + this.isObserving);
601     },
602 
603     mustBeEnabled: function()
604     {
605         var optionMap =
606         {
607             showJSErrors:1,
608             showJSWarnings:1,
609             showCSSErrors:1,
610             showXMLErrors: 1,
611             showChromeErrors: 1,
612             showChromeMessages: 1,
613             showXMLHttpRequests: 1,
614             showStackTrace: 1
615         };
616 
617         for (var p in optionMap)
618         {
619             if (Firebug[p])
620                 return true;
621         }
622 
623         return false;
624     },
625 
626     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
627 
628     reparseXPC: function(errorMessage, context)
629     {
630         var reXPCError = /JavaScript Error:\s*\"([^\"]*)\"/;
631         var reFile = /file:\s*\"([^\"]*)\"/;
632         var reLine = /line:\s*(\d*)/;
633         var m = reXPCError.exec(errorMessage);
634         if (!m)
635             return null;
636 
637         var msg = m[1];
638         var sourceFile = null;
639         m = reFile.exec(errorMessage);
640         if (m)
641             sourceFile = m[1];
642 
643         var sourceLineNo = 0;
644         m = reLine.exec(errorMessage);
645         if (m)
646             sourceLineNo = m[1];
647 
648         var sourceLine = null;
649         if (sourceFile && sourceLineNo && sourceLineNo != 0)
650         {
651             if (context.sourceCache)
652             {
653                 sourceLine = context.sourceCache.getLine(sourceFile, sourceLineNo);
654             }
655             else if (FBTrace.DBG_ERRORS)
656             {
657                 FBTrace.sysout("errors.reparseXPC; ERROR, NULL context.sourceCache, " +
658                     sourceFile + ", " + sourceLineNo);
659             }
660         }
661 
662         var error = new FirebugReps.ErrorMessageObj(msg, sourceFile,
663             sourceLineNo, sourceLine, "error", context, null);
664         return error;
665     }
666 });
667 
668 // ********************************************************************************************* //
669 // Local Helpers
670 
671 const categoryMap =
672 {
673     "javascript": "js",
674     "JavaScript": "js",
675     "DOM": "js",
676     "Events": "js",
677     "CSS": "css",
678     "HTML": "xml",
679     "XML": "xml",
680     "malformed-xml": "xml"
681 };
682 
683 function getBaseCategory(categories)
684 {
685     var categoryList = categories.split(" ");
686     for (var i=0; i<categoryList.length; ++i)
687     {
688         var category = categoryList[i];
689         if (categoryMap.hasOwnProperty(category))
690             return categoryMap[category];
691     }
692 }
693 
694 function whyNotShown(url, categoryList, isWarning)
695 {
696     var m = urlRe.exec(url);
697     var errorScheme = m ? m[1] : "";
698     if (errorScheme == "javascript")
699         return null;
700 
701     var isChrome = false;
702 
703     if (!categoryList)
704     {
705         return Firebug.showChromeErrors ? null :
706             "no category, assume chrome, showChromeErrors false";
707     }
708 
709     var categories = categoryList.split(" ");
710     for (var i=0; i<categories.length; ++i)
711     {
712         var category = categories[i];
713         if (category == "CSS" && !Firebug.showCSSErrors)
714         {
715             return "showCSSErrors";
716         }
717         else if ((category == "HTML" || category == "XML" || category == "malformed-xml" ) &&
718             !Firebug.showXMLErrors)
719         {
720             return "showXMLErrors";
721         }
722         else if ((category == "javascript" || category == "JavaScript" || category == "DOM")
723                 && !isWarning && !Firebug.showJSErrors)
724         {
725             return "showJSErrors";
726         }
727         else if ((category == "javascript" || category == "JavaScript" || category == "DOM")
728                 && isWarning && !Firebug.showJSWarnings)
729         {
730             return "showJSWarnings";
731         }
732         else if (errorScheme == "chrome" || category == "XUL" || category == "chrome" ||
733                 category == "XBL" || category == "component")
734         {
735             isChrome = true;
736         }
737     }
738 
739     if (isChrome && !Firebug.showChromeErrors)
740         return "showChromeErrors";
741 
742     return null;
743 }
744 
745 function lessTalkMoreAction(context, object, isWarning)
746 {
747     if (!context)
748     {
749         if (FBTrace.DBG_ERRORLOG)
750             FBTrace.sysout("errors.observe dropping " + object.category + " no context");
751         return false;
752     }
753 
754     var enabled = Console.isAlwaysEnabled();
755     if (!enabled)
756         return null;
757 
758     var why = whyNotShown(object.sourceName, object.category, isWarning);
759     if (why)
760     {
761         if (FBTrace.DBG_ERRORLOG)
762             FBTrace.sysout("errors.observe dropping " + object.category + " because: " + why);
763 
764         context.droppedErrors = context.droppedErrors || {};
765 
766         if (!context.droppedErrors[object.category])
767             context.droppedErrors[object.category] = 1;
768         else
769             context.droppedErrors[object.category] += 1;
770 
771         return null;
772     }
773 
774     // nsIScriptError
775     var incoming_message = object.errorMessage;
776 
777     // nsIConsoleMessage
778     if (!incoming_message)
779         incoming_message = object.message;
780 
781     if (Firebug.suppressPointlessErrors)
782     {
783         for (var msg in pointlessErrors)
784         {
785             if (msg.charAt(0) == incoming_message.charAt(0))
786             {
787                 if (Str.hasPrefix(incoming_message, msg))
788                 {
789                     if (FBTrace.DBG_ERRORLOG)
790                         FBTrace.sysout("errors.observe dropping pointlessError: " + msg);
791                     return null;
792                 }
793             }
794         }
795     }
796 
797     var msgId = [incoming_message, object.sourceName, object.lineNumber].join("/");
798     return msgId;
799 }
800 
801 function checkForException(context, object)
802 {
803     if (object.flags & object.exceptionFlag)
804     {
805         if (FBTrace.DBG_ERRORLOG)
806             FBTrace.sysout("errors.observe is exception");
807 
808         if (context.thrownStackTrace)
809         {
810             Firebug.errorStackTrace = context.thrownStackTrace;
811 
812             if (FBTrace.DBG_ERRORLOG)
813                 FBTrace.sysout("errors.observe trace.frames", context.thrownStackTrace.frames);
814 
815             delete context.thrownStackTrace;
816         }
817         else
818         {
819              if (FBTrace.DBG_ERRORLOG)
820                 FBTrace.sysout("errors.observe NO context.thrownStackTrace");
821         }
822         return true;
823     }
824 
825     delete context.thrownStackTrace;
826     return false;
827 }
828 
829 /**
830  * Returns a parent window (outer window) for given error object (an object
831  * that is passed into a consoleListener).
832  * This method should be the primary way how to find the parent window for any
833  * error object.
834  *
835  * @param {Object} object Error object (implementing nsIScriptError2 or nsIScriptError)
836  */
837 function getErrorWindow(object)
838 {
839     try
840     {
841         // nsIScriptError2 is merged into nsIScriptError in Firefox 12 (bug
842         // 711721), so check for the one that is relevant.
843         var why;
844         if (object instanceof (Ci["nsIScriptError2"] || Ci["nsIScriptError"]))
845         {
846             if (object.outerWindowID)
847             {
848                 var win = domWindowUtils.getOuterWindowWithId(object.outerWindowID);
849                 if (win)
850                     return win;
851                 else
852                     why = "no getOuterWindowWithId";
853             }
854             else
855             {
856                 why = "no outerWindowID";
857             }
858         }
859         else
860         {
861             why = "not an nsIScriptError";
862         }
863 
864         if (FBTrace.DBG_ERRORS)
865             FBTrace.sysout("errors.getErrorWindow failed " + why, object);
866     }
867     catch (err)
868     {
869         if (FBTrace.DBG_ERRORS)
870             FBTrace.sysout("errors.getErrorWindow; EXCEPTION" + err, err);
871     }
872 
873     return null;
874 }
875 
876 function getExceptionContext(context, object)
877 {
878     var errorWin = getErrorWindow(object);
879     if (errorWin)
880     {
881         var errorContext = Firebug.connection.getContextByWindow(errorWin);
882         if (FBTrace.DBG_ERRORLOG)
883         {
884             FBTrace.sysout("errors.observe exception context: " +
885                 (errorContext ? errorContext.getName() : "none") + " errorWin: " +
886                     Win.safeGetWindowLocation(errorWin));
887         }
888 
889         if (errorContext)
890             return errorContext;
891     }
892 
893     return context;
894 }
895 
896 function correctLineNumbersOnExceptions(object, error)
897 {
898     var m = reException1.exec(object.errorMessage) || reException2.exec(object.errorMessage);
899     if (m)
900     {
901         var exception = m[1];
902         if (exception)
903             error.message = exception;
904         var sourceName = m[3];
905         var lineNumber = parseInt(m[4]);
906 
907         error.correctSourcePoint(sourceName, lineNumber);
908 
909         if (FBTrace.DBG_ERRORLOG)
910         {
911             FBTrace.sysout("errors.correctLineNumbersOnExceptions corrected message " +
912                 "with sourceName: "+ sourceName + "@" + lineNumber);
913         }
914     }
915 }
916 
917 // ********************************************************************************************* //
918 // Registration
919 
920 Firebug.registerModule(Errors);
921 
922 return Firebug.Errors;
923 
924 // ********************************************************************************************* //
925 });
926