home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / traceModule.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  50.2 KB  |  1,679 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. /**
  4.  * UI control of debug Logging for Firebug internals
  5.  */
  6. FBL.ns(function() { with (FBL) {
  7.  
  8. // ***********************************************************************************
  9. // Shorcuts and Services
  10.  
  11. const Cc = Components.classes;
  12. const Ci = Components.interfaces;
  13.  
  14. const windowMediator = CCSV("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
  15. const clipboard = CCSV("@mozilla.org/widget/clipboard;1", "nsIClipboard");
  16.  
  17. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  18. const prefs = PrefService.getService(Ci.nsIPrefBranch2);
  19. const prefService = PrefService.getService(Ci.nsIPrefService);
  20.  
  21. const reDBG = /extensions\.([^\.]*)\.(DBG_.*)/;
  22. const reDBG_FBS = /DBG_FBS_(.*)/;
  23.  
  24. var EOF = "<br/>";
  25.  
  26. //************************************************************************************************
  27. //The controller for the prefDomain Model.
  28. //  getOptionsMenuItems to create View, onPrefChangeHandler for View update
  29. //  base for trace viewers like tracePanel and traceConsole
  30. //  binds  to the branch 'prefDomain' of prefs  
  31.  
  32. Firebug.TraceOptionsController = function(prefDomain, onPrefChangeHandler)
  33. {
  34.     this.prefDomain = prefDomain;
  35.     
  36.     this.traceService =  Cc["@joehewitt.com/firebug-trace-service;1"]
  37.                                          .getService(Ci.nsISupports).wrappedJSObject;
  38.     
  39.     this.addObserver = function()  
  40.     {
  41.         prefs.setBoolPref("browser.dom.window.dump.enabled", true);
  42.         this.observer = { observe: bind(this.observe, this) };
  43.         prefs.addObserver(prefDomain, this.observer, false);
  44.     };
  45.  
  46.     this.removeObserver = function()
  47.     {
  48.         prefs.removeObserver( prefDomain, this.observer, false);
  49.     };
  50.     
  51.     // nsIObserver
  52.     this.observe = function(subject, topic, data)  // 
  53.     {
  54.         if (topic == "nsPref:changed")
  55.         {
  56.             var m = reDBG.exec(data);  
  57.             if (m)
  58.             {
  59.                 var changedPrefDomain = "extensions." + m[1];
  60.                 if (changedPrefDomain == prefDomain) 
  61.                 {
  62.                     var optionName = data.substr(prefDomain.length+1); // skip dot
  63.                     var optionValue = Firebug.getPref(prefDomain, m[2]);
  64.                     if (this.prefEventToUserEvent)
  65.                         this.prefEventToUserEvent(optionName, optionValue);
  66.                 }
  67.             }
  68.             else
  69.             {
  70.             }
  71.         }
  72.     };
  73.  
  74. //*************** UI ***************************************************************
  75.  
  76.     this.getOptionsMenuItems = function()  // Firebug menu items from option map
  77.     {
  78.         var optionMap = this.traceService.getTracer(prefDomain);
  79.         var items = [];
  80.         for (var p in optionMap) 
  81.         { 
  82.             var m = p.indexOf("DBG_");
  83.             if (m != 0)
  84.                 continue;
  85.  
  86.             try 
  87.             {
  88.                 var prefValue = Firebug.getPref(this.prefDomain, p);
  89.                 var label = p.substr(4);
  90.                 items.push({
  91.                  label: label,
  92.                  nol10n: true,
  93.                  type: "checkbox",
  94.                  checked: prefValue,
  95.                  pref: p,
  96.                  command: bind(this.userEventToPrefEvent, this)
  97.                 });
  98.             }
  99.             catch (err) 
  100.             {
  101.             }
  102.         }
  103.  
  104.         items.sort(function(a, b) {
  105.             return a.label > b.label;
  106.         });
  107.  
  108.         return items;
  109.     };
  110.  
  111.     this.userEventToPrefEvent = function(event)  // use as an event listener on UI control
  112.     {
  113.         var menuitem = event.target.wrappedJSObject;
  114.         if (!menuitem)
  115.             menuitem = event.target;
  116.  
  117.         var label = menuitem.getAttribute("label");
  118.         var category = 'DBG_'+label;
  119.         var value = Firebug.getPref(this.prefDomain, category);
  120.         var newValue = !value;
  121.  
  122.         Firebug.setPref(this.prefDomain, category, newValue);
  123.         prefService.savePrefFile(null);
  124.  
  125.     };
  126.  
  127.     if (onPrefChangeHandler)
  128.         this.prefEventToUserEvent = onPrefChangeHandler;
  129.     else
  130.     {
  131.         this.prefEventToUserEvent = function(optionName, optionValue)
  132.         {
  133.             FBTrace.sysout("TraceOptionsController owner needs to implement prefEventToUser Event", {name: optionName, value: optionValue});
  134.         };
  135.     }
  136.   
  137. };
  138.  
  139. // ***********************************************************************************
  140. // Trace Module
  141.  
  142.  
  143. Firebug.TraceModule = extend(Firebug.Module,
  144. {
  145.     listeners: [],
  146.  
  147.     initialize: function(prefDomain, prefNames)  // prefDomain is the calling app, firebug or chromebug
  148.     {
  149.         FBTrace.DBG_OPTIONS = Firebug.getPref(prefDomain, "DBG_OPTIONS");
  150.  
  151.         this.prefDomain = prefDomain;
  152.  
  153.         // Create menu items for Trace Console 
  154.         // (Open Console and a new option Always Open Console)
  155.         this.initMenu();
  156.  
  157.         // Open console automatically if the pref says so.
  158.         if (Firebug.getPref(this.prefDomain, "alwaysOpenTraceConsole"))
  159.             this.openConsole();
  160.     },
  161.  
  162.     shutdown: function()
  163.     {
  164.         if (this.consoleWindow)
  165.             this.consoleWindow.TraceConsole.unregisterModule(this);
  166.     },
  167.  
  168.     initMenu: function()
  169.     {
  170.         // Create menu item "Open Firebug Tracing" within Firebug menu (the bug-menu on FB bar).
  171.         var popupMenu = $("fbFirebugMenuPopup");
  172.         this.createOpenTracingMenu(popupMenu);
  173.  
  174.         // Create menu item "Open Firebug Tracing" within Firefox Tools menu.
  175.         var toolsMenu = $("menu_firebug");
  176.         if (toolsMenu)  // then we are in Firefox not eg Chromebug
  177.             this.createOpenTracingMenu(toolsMenu.menupopup);
  178.  
  179.         // Create new option "Always Open Firebug Tracing" item within Firebug options menu.
  180.         var optionsMenu = $("FirebugMenu_OptionsPopup");
  181.         this.createAlwaysOpenTracingMenu(optionsMenu);
  182.     },
  183.  
  184.     reattachContext: function(browser, context) 
  185.     {
  186.         var viewMenu = browser.chrome.$("view-menu");
  187.         this.createOpenTracingMenu(viewMenu.menupopup);
  188.  
  189.         var optionsMenu = browser.chrome.$("FirebugMenu_OptionsPopup");
  190.         this.createAlwaysOpenTracingMenu(optionsMenu);
  191.     },
  192.  
  193.     createOpenTracingMenu: function(parentMenu)
  194.     {
  195.         if (!parentMenu)
  196.             return;
  197.  
  198.         var doc = parentMenu.ownerDocument;
  199.         var menuItem = doc.createElement("menuitem");
  200.         menuItem.setAttribute("label", $STR("Open Firebug Tracing"));
  201.         menuItem.setAttribute("oncommand", "Firebug.TraceModule.openConsole()");
  202.         var firstItem = parentMenu.firstChild;
  203.         parentMenu.insertBefore(menuItem, firstItem);
  204.         parentMenu.insertBefore(document.createElement("menuseparator"), firstItem);
  205.     },
  206.  
  207.     createAlwaysOpenTracingMenu: function(parentMenu)
  208.     {
  209.         if (!parentMenu)
  210.             return;
  211.  
  212.         var menuItemId = "FirebugMenu_Options_alwaysOpenTraceConsole";
  213.         var doc = parentMenu.ownerDocument;
  214.         if ($(menuItemId, doc))
  215.             return;
  216.  
  217.         var menuItem = doc.createElement("menuitem");
  218.         menuItem.setAttribute("id", menuItemId);
  219.         menuItem.setAttribute("label", $STR("Always Open Firebug Tracing"));
  220.         menuItem.setAttribute("type", "checkbox");
  221.         menuItem.setAttribute("oncommand", "FirebugChrome.onToggleOption(this)");
  222.         menuItem.setAttribute("option", "alwaysOpenTraceConsole");
  223.         parentMenu.appendChild(document.createElement("menuseparator"));
  224.         parentMenu.appendChild(menuItem);
  225.     },
  226.  
  227.     openConsole: function(prefDomain)
  228.     {
  229.         if (!prefDomain)
  230.             prefDomain = this.prefDomain;
  231.  
  232.         this.consoleWindow = windowMediator.getMostRecentWindow(
  233.             "FBTraceConsole." + prefDomain);
  234.  
  235.         // Try to connect an existing trace-console window first.
  236.         if (this.consoleWindow) {
  237.             this.consoleWindow.TraceConsole.registerModule(this);
  238.             this.consoleWindow.focus();
  239.             return;
  240.         }
  241.  
  242.         var self = this;
  243.         var args = {
  244.             FBL: FBL,
  245.             Firebug: Firebug,
  246.             traceModule: self,
  247.             prefDomain: prefDomain,
  248.         };
  249.  
  250.         this.consoleWindow = window.openDialog(
  251.             "chrome://firebug/content/traceConsole.xul",
  252.             "FBTraceConsole." + prefDomain,
  253.             "chrome,resizable,scrollbars=auto,minimizable,dialog=no",
  254.             args);
  255.     },
  256.  
  257.     // Trace console listeners
  258.     onLoadConsole: function(win, rootNode)
  259.     {
  260.         try
  261.         {
  262.             for (var i=0; i<this.listeners.length; i++) {
  263.                 if (this.listeners[i].onLoadConsole)
  264.                     this.listeners[i].onLoadConsole(win, rootNode);
  265.             }
  266.         }
  267.         catch (err)
  268.         {
  269.         }
  270.     },
  271.  
  272.     onUnloadConsole: function(win)
  273.     {
  274.         try
  275.         {
  276.             for (var i=0; i<this.listeners.length; i++) {
  277.                 if (this.listeners[i].onUnloadConsole)
  278.                     this.listeners[i].onUnloadConsole(win);
  279.             }
  280.         }
  281.         catch (err)
  282.         {
  283.         }
  284.     },
  285.  
  286.     onDump: function(message)
  287.     {
  288.         try
  289.         {
  290.             for (var i=0; i<this.listeners.length; i++) {
  291.                 if (this.listeners[i].onDump)
  292.                     this.listeners[i].onDump(message);
  293.             }
  294.         }
  295.         catch (err)
  296.         {
  297.         }
  298.     },
  299.  
  300.     addListener: function(listener)
  301.     {
  302.         this.listeners.push(listener);
  303.     },
  304.  
  305.     removeListener: function(listener)
  306.     {
  307.         remove(this.listeners, listener);
  308.     },
  309.  
  310.     dump: function(message, parentNode)
  311.     {
  312.         // xxxHonza: find better solution for checking an ERROR messages 
  313.         // (setup some rules).
  314.         var index = message.text.indexOf("ERROR");
  315.         if (index != -1)
  316.             message.type = "DBG_ERROR";
  317.  
  318.         index = message.text.indexOf("EXCEPTION");
  319.         if (index != -1)
  320.             message.type = "DBG_ERROR";
  321.  
  322.         Firebug.TraceModule.MessageTemplate.dump(message, parentNode);
  323.     },
  324. });
  325.  
  326. Firebug.TraceModule.CommonBaseUI = {
  327.  
  328.     destroy: function() 
  329.     {
  330.         this.optionsController.removeObserver();
  331.     },
  332.         
  333.     initializeContent: function(parentNode, prefDomain)
  334.     {
  335.         // Create basic layout for trace console content.
  336.         var rep = Firebug.TraceModule.PanelTemplate;
  337.         rep.tag.replace({}, parentNode, rep);
  338.  
  339.         // This node is the container for all logs.
  340.         var logTabContent = FBL.getElementByClass(parentNode, "traceInfoLogsText");
  341.         var logNode = Firebug.TraceModule.MessageTemplate.createTable(logTabContent);
  342.  
  343.         // Initialize content for Options tab (a button for each DBG_ option).
  344.         var optionsBody = FBL.getElementByClass(parentNode, "traceInfoOptionsText");
  345.         this.optionsController = new Firebug.TraceOptionsController(prefDomain, function updateButton(optionName, optionValue)
  346.         {
  347.             var button = parentNode.ownerDocument.getElementById(optionName);
  348.             if (button)
  349.                 button.setAttribute("checked", optionValue?"true":"false");
  350.             else
  351.                 FBTrace.sysout("traceModule onPrefChange no button with name "+optionName+ " in parentNode", parentNode);
  352.         });
  353.         
  354.         var menuitems = this.optionsController.getOptionsMenuItems();
  355.         var doc = parentNode.ownerDocument;
  356.         for (var i=0; i<menuitems.length; i++)
  357.         {
  358.             var menuitem = menuitems[i];
  359.             var button = doc.createElement("button");
  360.             FBL.setClass(button, "traceOption");
  361.             FBL.setItemIntoElement(button, menuitem);
  362.             button.innerHTML = menuitem.label;
  363.             button.setAttribute("id", menuitem.pref);
  364.             button.removeAttribute("type");
  365.             button.addEventListener("click", menuitem.command, false);
  366.             optionsBody.appendChild(button);
  367.         }
  368.  
  369.         // Select default tab.
  370.         rep.selectTabByName(parentNode, "Logs");
  371.  
  372.         this.optionsController.addObserver();
  373.         
  374.         return logNode;
  375.     },
  376. };
  377.  
  378.  
  379. //************************************************************************************************
  380. //Trace Console Rep
  381.  
  382. Firebug.TraceModule.PanelTemplate = domplate({
  383.  
  384.     tag:
  385.         TABLE({class: "traceTable", cellpadding: 0, cellspacing: 0},
  386.             TBODY(
  387.                 TR({class: "traceInfoRow"},
  388.                     TD({class: "traceInfoCol"},
  389.                         DIV({class: "traceInfoBody"},
  390.                             DIV({class: "traceInfoTabs"},
  391.                                 A({class: "traceInfoLogsTab traceInfoTab", onclick: "$onClickTab",
  392.                                     view: "Logs"},
  393.                                     $STR("Logs")
  394.                                 ),
  395.                                 A({class: "traceInfoOptionsTab traceInfoTab", onclick: "$onClickTab",
  396.                                     view: "Options"},
  397.                                     $STR("Options")
  398.                                 )
  399.                             ),
  400.                             DIV({class: "traceInfoLogsText traceInfoText"}),
  401.                             DIV({class: "traceInfoOptionsText traceInfoText"})
  402.                         )
  403.                     )
  404.                 )
  405.             )
  406.         ),
  407.  
  408.     onClickTab: function(event)
  409.     {
  410.         this.selectTab(event.currentTarget);
  411.     },
  412.  
  413.     selectTabByName: function(parentNode, tabName)
  414.     {
  415.         var tab = getElementByClass(parentNode, "traceInfo" + tabName + "Tab");
  416.         if (tab)
  417.             this.selectTab(tab);
  418.     },
  419.  
  420.     selectTab: function(tab)
  421.     {
  422.         var messageInfoBody = tab.parentNode.parentNode;
  423.  
  424.         var view = tab.getAttribute("view");
  425.         if (messageInfoBody.selectedTab)
  426.         {
  427.             messageInfoBody.selectedTab.removeAttribute("selected");
  428.             messageInfoBody.selectedText.removeAttribute("selected");
  429.         }
  430.  
  431.         var textBodyName = "traceInfo" + view + "Text";
  432.  
  433.         messageInfoBody.selectedTab = tab;
  434.         messageInfoBody.selectedText = getChildByClass(messageInfoBody, textBodyName);
  435.  
  436.         messageInfoBody.selectedTab.setAttribute("selected", "true");
  437.         messageInfoBody.selectedText.setAttribute("selected", "true");
  438.     }
  439. });
  440.  
  441. // ************************************************************************************************
  442. // Trace message 
  443.  
  444. Firebug.TraceModule.MessageTemplate = domplate(Firebug.Rep,
  445. {
  446.     inspectable: false,
  447.  
  448.     tableTag:
  449.         TABLE({class: "messageTable", cellpadding: 0, cellspacing: 0},
  450.             TBODY()
  451.         ),
  452.  
  453.     rowTag:
  454.         TR({class: "messageRow $message|getMessageType",
  455.             _repObject: "$message",
  456.             $exception: "$message|isException",
  457.             onclick: "$onClickRow"},
  458.             TD({class: "messageNameCol messageCol"},
  459.                 DIV({class: "messageNameLabel messageLabel"},
  460.                     "$message|getMessageIndex")
  461.             ),
  462.             TD({class: "messageCol"},
  463.                 DIV({class: "messageLabel", title: "$message|getMessageTitle"},
  464.                     "$message|getMessageLabel")
  465.             )
  466.         ),
  467.  
  468.     separatorTag:
  469.         TR({class: "messageRow separatorRow"},
  470.             TD({class: "messageCol", colspan: "2"},
  471.                 DIV("$message|getMessageIndex")
  472.             )
  473.         ),
  474.  
  475.     bodyRow:
  476.         TR({class: "messageInfoRow"},
  477.             TD({class: "messageInfoCol", colspan: 8})
  478.         ),
  479.  
  480.     bodyTag:
  481.         DIV({class: "messageInfoBody", _repObject: "$message"},
  482.             DIV({class: "messageInfoTabs"},
  483.                 A({class: "messageInfoStackTab messageInfoTab", onclick: "$onClickTab",
  484.                     view: "Stack"},
  485.                     $STR("tracing.tab.Stack")
  486.                 ),
  487.                 A({class: "messageInfoExcTab messageInfoTab", onclick: "$onClickTab",
  488.                     view: "Exc",
  489.                     $collapsed: "$message|hideException"},
  490.                     $STR("tracing.tab.Exception")
  491.                 ),
  492.                 A({class: "messageInfoPropsTab messageInfoTab", onclick: "$onClickTab",
  493.                     view: "Props",
  494.                     $collapsed: "$message|hideProperties"},
  495.                     $STR("tracing.tab.Properties")
  496.                 ),
  497.                 A({class: "messageInfoScopeTab messageInfoTab", onclick: "$onClickTab",
  498.                     view: "Scope",
  499.                     $collapsed: "$message|hideScope"},
  500.                     $STR("tracing.tab.Scope")
  501.                 ),
  502.                 A({class: "messageInfoResponseTab messageInfoTab", onclick: "$onClickTab",
  503.                     view: "Response",
  504.                     $collapsed: "$message|hideResponse"},
  505.                     $STR("tracing.tab.Response")
  506.                 ),
  507.                 A({class: "messageInfoSourceTab messageInfoTab", onclick: "$onClickTab",
  508.                     view: "Source",
  509.                     $collapsed: "$message|hideSource"},
  510.                     $STR("tracing.tab.Source")
  511.                 ),
  512.                 A({class: "messageInfoIfacesTab messageInfoTab", onclick: "$onClickTab",
  513.                     view: "Ifaces",
  514.                     $collapsed: "$message|hideInterfaces"},
  515.                     $STR("tracing.tab.Interfaces")
  516.                 ),
  517.                 // xxxHonza: this doesn't seem to be much useful.
  518.                 /*A({class: "messageInfoTypesTab messageInfoTab", onclick: "$onClickTab",
  519.                     view: "Types",
  520.                     $collapsed: "$message|hideTypes"},
  521.                     "Types"
  522.                 ),*/
  523.                 A({class: "messageInfoObjectTab messageInfoTab", onclick: "$onClickTab",
  524.                     view: "Types",
  525.                     $collapsed: "$message|hideObject"},
  526.                     $STR("tracing.tab.Object")
  527.                 ),
  528.                 A({class: "messageInfoEventTab messageInfoTab", onclick: "$onClickTab",
  529.                     view: "Event",
  530.                     $collapsed: "$message|hideEvent"},
  531.                     $STR("tracing.tab.Event")
  532.                 )
  533.             ),
  534.             DIV({class: "messageInfoStackText messageInfoText"},
  535.                 TABLE({class: "messageInfoStackTable", cellpadding: 0, cellspacing: 0},
  536.                     TBODY(
  537.                         FOR("stack", "$message|stackIterator",
  538.                             TR(
  539.                                 TD({class: "stackFrame"},
  540.                                     A({class: "stackFrameLink", onclick: "$onClickStackFrame",
  541.                                         lineNumber: "$stack.lineNumber"},
  542.                                         "$stack.fileName"),
  543.                                     SPAN(" "),
  544.                                     SPAN("(", "$stack.lineNumber", ")"),
  545.                                     SPAN(" "),
  546.                                     A({class: "openDebugger", onclick: "$onOpenDebugger",
  547.                                         lineNumber: "$stack.lineNumber",
  548.                                         fileName: "$stack.fileName"},
  549.                                         "[...]")
  550.                                 )
  551.                             )
  552.                         )
  553.                     )
  554.                 )
  555.             ),
  556.             DIV({class: "messageInfoExcText messageInfoText"}),
  557.             DIV({class: "messageInfoPropsText messageInfoText"}),
  558.             DIV({class: "messageInfoResponseText messageInfoText"},
  559.                 IFRAME({class: "messageInfoResponseFrame"})
  560.             ),
  561.             DIV({class: "messageInfoSourceText messageInfoText"}),
  562.             DIV({class: "messageInfoIfacesText messageInfoText"}),
  563.             DIV({class: "messageInfoScopeText messageInfoText"}),
  564.             DIV({class: "messageInfoTypesText messageInfoText"}),
  565.             DIV({class: "messageInfoObjectText messageInfoText"}),
  566.             DIV({class: "messageInfoEventText messageInfoText"})
  567.         ),
  568.  
  569.     // Data providers
  570.     getMessageType: function(message)
  571.     {
  572.         return message.getType();
  573.     },
  574.  
  575.     getMessageIndex: function(message)
  576.     {
  577.         return message.index + 1;
  578.     },
  579.  
  580.     getMessageLabel: function(message)
  581.     {
  582.         var maxLength = Firebug.getPref(Firebug.TraceModule.prefDomain, 
  583.             "trace.maxMessageLength");
  584.         return message.getLabel(maxLength);
  585.     },
  586.  
  587.     getMessageTitle: function(message)
  588.     {
  589.         return message.getLabel(-1);
  590.     },
  591.  
  592.     isException: function(message)
  593.     {
  594.         return message.getException();
  595.     },
  596.  
  597.     hideProperties: function(message)
  598.     {
  599.         var props = message.getProperties();
  600.         for (var name in props)
  601.             return false;
  602.  
  603.         return true;
  604.     },
  605.  
  606.     hideScope: function(message)
  607.     {
  608.         return !message.getScope();
  609.     },
  610.  
  611.     hideInterfaces: function(message)
  612.     {
  613.         var ifaces = message.getInterfaces();
  614.         for (var name in ifaces)
  615.             return false;
  616.  
  617.         return true;
  618.     },
  619.  
  620.     hideTypes: function(message)
  621.     {
  622.         return !message.getTypes();
  623.     },
  624.  
  625.     hideObject: function(message)
  626.     {
  627.         return !message.getObject();
  628.     },
  629.  
  630.     hideEvent: function(message)
  631.     {
  632.         return !message.getEvent();
  633.     },
  634.  
  635.     hideException: function(message)
  636.     {
  637.         return !message.getException();
  638.     },
  639.  
  640.     hideResponse: function(message)
  641.     {
  642.         return !(message.obj instanceof Ci.nsIHttpChannel);
  643.     },
  644.  
  645.     hideSource: function(message)
  646.     {
  647.         return !(message.obj instanceof Ci.nsIHttpChannel);
  648.     },
  649.  
  650.     // Stack frame support
  651.     stackIterator: function(message)
  652.     {
  653.         return message.getStackArray();
  654.     },
  655.  
  656.     onClickStackFrame: function(event)
  657.     {
  658.         var winType = "FBTraceConsole-SourceView";
  659.         var url = event.target.innerHTML;
  660.         var lineNumber = event.target.getAttribute("lineNumber");
  661.  
  662.         openDialog("chrome://global/content/viewSource.xul",
  663.             winType, "all,dialog=no",
  664.             event.target.innerHTML, null, null, lineNumber, false);
  665.     },
  666.  
  667.     onOpenDebugger: function(event)
  668.     {
  669.         var target = event.target;
  670.         var lineNumber = target.getAttribute("lineNumber");
  671.         var fileName = target.getAttribute("fileName");
  672.  
  673.         if (typeof(ChromeBugOpener) == "undefined")
  674.             return;
  675.  
  676.         // Open Chromebug window.
  677.         var cbWindow = ChromeBugOpener.openNow();
  678.         FBTrace.dumpProperties("Chromebug window has been opened", cbWindow);
  679.  
  680.         // xxxHonza: Open Chromebug with the source code file, scrolled automatically
  681.         // to the specified line number. Currently chrome bug doesn't return the window
  682.         // from ChromeBugOpener.openNow method. If it would be following code opens
  683.         // the source code file and scrolls to the given line.
  684.  
  685.         // Register onLoad listener and open the source file at the specified line.
  686.         if (cbWindow) {
  687.             cbWindow.addEventListener("load", function() {
  688.                 var context = cbWindow.FirebugContext;
  689.                 var link = new cbWindow.FBL.SourceLink(fileName, lineNumber, "js");
  690.                 context.chrome.select(link, "script");
  691.             }, true);
  692.         }
  693.     },
  694.  
  695.     // Firebug rep support
  696.     supportsObject: function(message, type)
  697.     {
  698.         return message instanceof Firebug.TraceModule.TraceMessage;
  699.     },
  700.  
  701.     browseObject: function(message, context)
  702.     {
  703.         return false;
  704.     },
  705.  
  706.     getRealObject: function(message, context)
  707.     {
  708.         return message;
  709.     },
  710.  
  711.     // Context menu
  712.     getContextMenuItems: function(message, target, context)
  713.     {
  714.         var items = [];
  715.  
  716.         if (getAncestorByClass(target, "messageRow"))
  717.         {
  718.             items.push({
  719.               label: $STR("Cut"),
  720.               nol10n: true,
  721.               command: bindFixed(this.onCutMessage, this, message)
  722.             });
  723.  
  724.             items.push({
  725.               label: $STR("Copy"),
  726.               nol10n: true,
  727.               command: bindFixed(this.onCopyMessage, this, message)
  728.             });
  729.  
  730.             items.push("-");
  731.  
  732.             items.push({
  733.               label: $STR("Remove"),
  734.               nol10n: true,
  735.               command: bindFixed(this.onRemoveMessage, this, message)
  736.             });
  737.         }
  738.  
  739.         if (getAncestorByClass(target, "messageInfoStackText"))
  740.         {
  741.             items.push({
  742.               label: $STR("Copy Stack"),
  743.               nol10n: true,
  744.               command: bindFixed(this.onCopyStack, this, message)
  745.             });
  746.         }
  747.  
  748.         if (getAncestorByClass(target, "messageInfoExcText"))
  749.         {
  750.             items.push({
  751.               label: $STR("Copy Exception"),
  752.               nol10n: true,
  753.               command: bindFixed(this.onCopyException, this, message)
  754.             });
  755.         }
  756.  
  757.         if (items.length > 0)
  758.             items.push("-");
  759.  
  760.         items.push(this.optionMenu($STR("tracing.Show Scope Variables"), "trace.enableScope"));
  761.  
  762.         return items;
  763.     },
  764.  
  765.     optionMenu: function(label, option)
  766.     {
  767.         var checked = Firebug.getPref(Firebug.TraceModule.prefDomain, option);
  768.         return {label: label, type: "checkbox", checked: checked, nol10n: true,
  769.             command: bindFixed(Firebug.setPref, Firebug, Firebug.TraceModule.prefDomain,
  770.                 option, !checked) };
  771.     },
  772.  
  773.     getTooltip: function(message)
  774.     {
  775.         return message.text;
  776.     },
  777.  
  778.     // Context menu commands
  779.     onCutMessage: function(message)
  780.     {
  781.         this.onCopyMessage(message);
  782.         this.onRemoveMessage(message);
  783.     },
  784.  
  785.     onCopyMessage: function(message)
  786.     {
  787.         copyToClipboard(message.text);
  788.     },
  789.  
  790.     onRemoveMessage: function(message)
  791.     {
  792.         var parentNode = message.row.parentNode;
  793.         parentNode.removeChild(message.row);
  794.     },
  795.  
  796.     onCopyStack: function(message)
  797.     {
  798.         copyToClipboard(message.getStack());
  799.     },
  800.  
  801.     onCopyException: function(message)
  802.     {
  803.         copyToClipboard(message.getException());
  804.     },
  805.  
  806.     // Clipboard helpers
  807.     copyToClipboard: function(text)
  808.     {
  809.         if (!text)
  810.             return;
  811.  
  812.         // Initialize transfer data.
  813.         var trans = CCIN("@mozilla.org/widget/transferable;1", "nsITransferable");
  814.         var wrapper = CCIN("@mozilla.org/supports-string;1", "nsISupportsString");
  815.         wrapper.data = text;
  816.         trans.addDataFlavor("text/unicode");
  817.         trans.setTransferData("text/unicode", wrapper, text.length * 2);
  818.  
  819.         // Set the data into the global clipboard
  820.         clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
  821.     },
  822.  
  823.     // Implementation
  824.     createTable: function(parentNode)
  825.     {
  826.         return HelperDomplate.replace(this.tableTag, {}, parentNode, this);
  827.     },
  828.  
  829.     dump: function(message, parentNode, index)
  830.     {
  831.         var panelNode = parentNode.parentNode.parentNode;
  832.         var scrolledToBottom = isScrolledToBottom(panelNode);
  833.  
  834.         // Set message index
  835.         if (index)
  836.             message.index = index;
  837.         else
  838.             message.index = parentNode.childNodes.length;
  839.  
  840.         // Insert log into the console.
  841.         var row = HelperDomplate.insertRows(this.rowTag, {message: message},
  842.             parentNode, this)[0];
  843.  
  844.         message.row = row;
  845.  
  846.         // Only if the manifest uses useNativeWrappers=no.
  847.         // The row in embedded frame, which uses type="content-primary", from some
  848.         // reason, this conten type changes wrapper around the row, so let's set
  849.         // directly thte wrappedJSObject here, so row-expand works.
  850.         if (row.wrappedJSObject)
  851.             row.wrappedJSObject.repObject = message;
  852.  
  853.         if (scrolledToBottom)
  854.             scrollToBottom(panelNode);
  855.     },
  856.  
  857.     dumpSeparator: function(parentNode)
  858.     {
  859.         var panelNode = parentNode.parentNode.parentNode;
  860.         var scrolledToBottom = isScrolledToBottom(panelNode);
  861.  
  862.         var fakeMessage = {};
  863.         fakeMessage.index = parentNode.childNodes.length;
  864.  
  865.         var row = HelperDomplate.insertRows(this.separatorTag, {message: fakeMessage},
  866.             parentNode, this)[0];
  867.  
  868.         if (scrolledToBottom)
  869.             scrollToBottom(panelNode);
  870.  
  871.         panelNode.scrollTop = panelNode.scrollHeight - panelNode.offsetHeight + 50;
  872.     },
  873.  
  874.     // Body of the message.
  875.     onClickRow: function(event)
  876.     {
  877.         if (isLeftClick(event))
  878.         {
  879.             var row = getAncestorByClass(event.target, "messageRow");
  880.             if (row)
  881.             {
  882.                 this.toggleRow(row);
  883.                 cancelEvent(event);
  884.             }
  885.         }
  886.     },
  887.  
  888.     toggleRow: function(row, forceOpen)
  889.     {
  890.         var opened = hasClass(row, "opened");
  891.         if (opened && forceOpen)
  892.             return;
  893.  
  894.         toggleClass(row, "opened");
  895.  
  896.         if (hasClass(row, "opened"))
  897.         {
  898.             var message = row.repObject;
  899.             if (!message && row.wrappedJSObject)
  900.                 message = row.wrappedJSObject.repObject;
  901.  
  902.             var bodyRow = HelperDomplate.insertRows(this.bodyRow, {}, row)[0];
  903.             var messageInfo = HelperDomplate.replace(this.bodyTag,
  904.                 {message: message}, bodyRow.firstChild);
  905.             message.bodyRow = bodyRow;
  906.  
  907.  
  908.             this.selectTabByName(messageInfo, "Stack");
  909.         }
  910.         else
  911.         {
  912.             row.parentNode.removeChild(row.nextSibling);
  913.         }
  914.     },
  915.  
  916.     selectTabByName: function(messageInfoBody, tabName)
  917.     {
  918.         var tab = getChildByClass(messageInfoBody, "messageInfoTabs",
  919.             "messageInfo" + tabName + "Tab");
  920.         if (tab)
  921.             this.selectTab(tab);
  922.     },
  923.  
  924.     onClickTab: function(event)
  925.     {
  926.         this.selectTab(event.currentTarget);
  927.     },
  928.  
  929.     selectTab: function(tab)
  930.     {
  931.         var messageInfoBody = tab.parentNode.parentNode;
  932.  
  933.         var view = tab.getAttribute("view");
  934.         if (messageInfoBody.selectedTab)
  935.         {
  936.             messageInfoBody.selectedTab.removeAttribute("selected");
  937.             messageInfoBody.selectedText.removeAttribute("selected");
  938.         }
  939.  
  940.         var textBodyName = "messageInfo" + view + "Text";
  941.  
  942.         messageInfoBody.selectedTab = tab;
  943.         messageInfoBody.selectedText = getChildByClass(messageInfoBody, textBodyName);
  944.  
  945.         messageInfoBody.selectedTab.setAttribute("selected", "true");
  946.         messageInfoBody.selectedText.setAttribute("selected", "true");
  947.  
  948.         var message = Firebug.getRepObject(messageInfoBody);
  949.  
  950.         // Make sure the original Domplate is *not* tracing for now.
  951.         var dumpDOM = FBTrace.DBG_DOM;
  952.         FBTrace.DBG_DOM = false;
  953.         this.updateInfo(messageInfoBody, view, message);
  954.         FBTrace.DBG_DOM = dumpDOM;
  955.     },
  956.  
  957.     updateInfo: function(messageInfoBody, view, message)
  958.     {
  959.         var tab = messageInfoBody.selectedTab;
  960.         if (hasClass(tab, "messageInfoStackTab"))
  961.         {
  962.             // The content is generated by domplate template.
  963.         }
  964.         else if (hasClass(tab, "messageInfoPropsTab"))
  965.         {
  966.             this.updateInfoImpl(messageInfoBody, view, message, message.getProperties,
  967.                 function (message, valueBox, text) {
  968.                     Firebug.TraceModule.Tree.tag.replace({object: message.props}, valueBox,
  969.                         Firebug.TraceModule.Tree);
  970.                 });
  971.         }
  972.         else if (hasClass(tab, "messageInfoScopeTab"))
  973.         {
  974.             this.updateInfoImpl(messageInfoBody, view, message, message.getScope,
  975.                 function (message, valueBox, text) {
  976.                     Firebug.TraceModule.PropertyTree.tag.replace({object: message.scope}, valueBox,
  977.                         Firebug.TraceModule.PropertyTree);
  978.                 });
  979.         }
  980.         else if (hasClass(tab, "messageInfoIfacesTab"))
  981.         {
  982.             this.updateInfoImpl(messageInfoBody, view, message, message.getInterfaces,
  983.                 function (message, valueBox, text) {
  984.                     Firebug.TraceModule.Tree.tag.replace({object: message.ifaces}, valueBox,
  985.                         Firebug.TraceModule.Tree);
  986.                 });
  987.         }
  988.         else if (hasClass(tab, "messageInfoTypesTab"))
  989.         {
  990.             this.updateInfoImpl(messageInfoBody, view, message, message.getTypes);
  991.         }
  992.         else if (hasClass(tab, "messageInfoEventTab"))
  993.         {
  994.             this.updateInfoImpl(messageInfoBody, view, message, message.getEvent);
  995.         }
  996.         else if (hasClass(tab, "messageInfoObjectTab"))
  997.         {
  998.             this.updateInfoImpl(messageInfoBody, view, message, message.getProperties,
  999.                 function (message, valueBox, text) {
  1000.                     if (message.obj instanceof Element)
  1001.                         Firebug.HTMLPanel.CompleteElement.tag.replace({object: message.obj}, valueBox,
  1002.                             Firebug.HTMLPanel.CompleteElement);
  1003.                     else
  1004.                         Firebug.TraceModule.PropertyTree.tag.replace({object: message.obj}, valueBox,
  1005.                             Firebug.TraceModule.PropertyTree);
  1006.                 });
  1007.         }
  1008.         else if (hasClass(tab, "messageInfoExcTab"))
  1009.         {
  1010.             this.updateInfoImpl(messageInfoBody, view, message, message.getException);
  1011.         }
  1012.         else if (hasClass(tab, "messageInfoResponseTab"))
  1013.         {
  1014.             this.updateInfoImpl(messageInfoBody, view, message, message.getResponse,
  1015.                 function (message, valueBox, text) {
  1016.                     var iframe = getChildByClass(valueBox, "messageInfoResponseFrame");
  1017.                     iframe.contentWindow.document.body.innerHTML = text;
  1018.                 });
  1019.         }
  1020.         else if (hasClass(tab, "messageInfoSourceTab"))
  1021.         {
  1022.             this.updateInfoImpl(messageInfoBody, view, message, message.getResponse,
  1023.                 function (message, valueBox, text) {
  1024.                     if (text)
  1025.                         insertWrappedText(text, valueBox);
  1026.                 });
  1027.         }
  1028.     },
  1029.  
  1030.     updateInfoImpl: function(messageInfoBody, view, message, getter, setter)
  1031.     {
  1032.         var valueBox = getChildByClass(messageInfoBody, "messageInfo" + view + "Text");
  1033.         if (!valueBox.valuePresented)
  1034.         {
  1035.             var text = getter.apply(message);
  1036.             if (typeof(text) != "undefined")
  1037.             {
  1038.                 valueBox.valuePresented = true;
  1039.  
  1040.                 if (setter)
  1041.                     setter(message, valueBox, text);
  1042.                 else
  1043.                     valueBox.innerHTML = text;
  1044.             }
  1045.         }
  1046.     }
  1047. });
  1048.  
  1049. // ************************************************************************************************
  1050. // Helper Domplate object that doesn't trace.
  1051.  
  1052. var HelperDomplate = (function()
  1053. {
  1054.     // Private helper function.
  1055.     function execute()
  1056.     {
  1057.         var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  1058.  
  1059.         // Make sure the original Domplate is *not* tracing for now.
  1060.         if (typeof FBTrace != "undefined") {
  1061.             var dumpDOM = FBTrace.DBG_DOM;
  1062.             FBTrace.DBG_DOM = false;
  1063.         }
  1064.  
  1065.         var retValue = fn.apply(object, args);
  1066.  
  1067.         if (typeof FBTrace != "undefined")
  1068.             FBTrace.DBG_DOM = dumpDOM;
  1069.  
  1070.         return retValue;
  1071.     }
  1072.  
  1073.     return {
  1074.         insertRows: function(tag, args, parentNode, self)
  1075.         {
  1076.             return execute(tag.insertRows, tag, args, parentNode, self);
  1077.         },
  1078.  
  1079.         replace: function(tag, args, parentNode, self)
  1080.         {
  1081.             return execute(tag.replace, tag, args, parentNode, self);
  1082.         }
  1083.    }
  1084. }());
  1085.  
  1086. // ************************************************************************************************
  1087. // Trace Message Object
  1088.  
  1089. Firebug.TraceModule.TraceMessage = function(type, text, obj)
  1090. {
  1091.     this.type = type;
  1092.     this.text = text;
  1093.     this.obj = obj;
  1094.     this.stack = [];
  1095.  
  1096.     if (this.obj instanceof Ci.nsIScriptError)
  1097.     {
  1098.         var trace = Firebug.errorStackTrace;
  1099.         if (trace)
  1100.         {
  1101.             if (Firebug.Errors.correctLineNumbersWithStack(trace, obj))
  1102.             {
  1103.                 for (var i=0; i<trace.frames.length; i++) {
  1104.                     var frame = trace.frames[i];
  1105.                     if (frame.href && frame.lineNo)
  1106.                         this.stack.push({fileName:frame.href, lineNumber:frame.lineNo});
  1107.                 }
  1108.             }
  1109.         }
  1110.         else  
  1111.         {
  1112.             // Put info about the script error location into the stack.
  1113.             this.stack.push({fileName:this.obj.sourceName, lineNumber:this.obj.lineNumber});
  1114.         }
  1115.     }
  1116.     else
  1117.     {
  1118.         // Initialize stack trace info. This must be done now, when the stack
  1119.         // is available.
  1120.         for (var frame = Components.stack, i=0; frame; frame = frame.caller, i++)
  1121.         {
  1122.             // Skip frames related to the tracing code.
  1123.             var fileName = unescape(frame.filename ? frame.filename : "");
  1124.             var traceServiceFile = "firebug@software.joehewitt.com/components/firebug-trace-service.js";
  1125.             if (i < 6 || fileName.indexOf(traceServiceFile) != -1)
  1126.                 continue;
  1127.  
  1128.             var sourceLine = frame.sourceLine ? frame.sourceLine : "";
  1129.             var lineNumber = frame.lineNumber ? frame.lineNumber : "";
  1130.             this.stack.push({fileName:fileName, lineNumber:lineNumber});
  1131.         }
  1132.     }
  1133.  
  1134.     if (this.obj instanceof Ci.nsIHttpChannel)
  1135.     {
  1136.         //firebug.netModule.getHttpHeaders(this.obj, this);
  1137.     }
  1138.  
  1139.     if (this.obj instanceof Ci.nsICachingChannel)
  1140.     {
  1141.         try
  1142.         {
  1143.             var cacheToken = this.obj.cacheToken;
  1144.             if (cacheToken instanceof Ci.nsICacheEntryDescriptor)
  1145.             {
  1146.                 this.cacheClient = cacheToken.clientID;
  1147.                 this.cacheKey = cacheToken.key;
  1148.             }
  1149.         }
  1150.         catch (e)
  1151.         {
  1152.         }
  1153.     }
  1154.  
  1155.     if (this.obj instanceof Error || 
  1156.         this.obj instanceof Ci.nsIException || 
  1157.         this.obj instanceof Ci.nsIScriptError)
  1158.     {
  1159.         // Put the error message into the title so, it's immediately visible.
  1160.         this.text += " " + this.obj.message;
  1161.     }
  1162.  
  1163.     // Get snapshot of all properties now, as they can be changed.
  1164.     this.getProperties();
  1165.  
  1166.     // Get current scope
  1167.     this.getScope();
  1168. }
  1169.  
  1170. // ************************************************************************************************
  1171.  
  1172. Firebug.TraceModule.TraceMessage.prototype =
  1173. {
  1174.     reXPConnect: /\[xpconnect wrapped ([^\]]*)\]/,
  1175.  
  1176.     getType: function()
  1177.     {
  1178.         return this.type;
  1179.     },
  1180.  
  1181.     getLabel: function(maxLength)
  1182.     {
  1183.         if (maxLength <= 10 || this.text.length <= maxLength)
  1184.             return this.text.replace(/[\n]/g,"");
  1185.  
  1186.         return this.text.substr(0, maxLength - 3) + "...";
  1187.     },
  1188.  
  1189.     getStackArray: function()
  1190.     {
  1191.         return this.stack;
  1192.     },
  1193.  
  1194.     getStack: function()
  1195.     {
  1196.         var result = "";
  1197.         for (var i=0; i<this.stack.length; i++) {
  1198.             var frame = this.stack[i];
  1199.             result += frame.fileName + " (" + frame.lineNumber + ")\n";
  1200.         }
  1201.  
  1202.         return result;
  1203.     },
  1204.  
  1205.     getProperties: function()
  1206.     {
  1207.         if (this.props)
  1208.             return this.props;
  1209.  
  1210.         this.props = [];
  1211.  
  1212.         if (this.obj instanceof Array)
  1213.         {
  1214.             if (this.obj.length)
  1215.             {
  1216.                 for (var p=0; p<this.obj.length; p++)
  1217.                 {
  1218.                     try
  1219.                     {
  1220.                         this.props[p] = "" + this.obj[p];
  1221.                     }
  1222.                     catch (e)
  1223.                     {
  1224.                         onPanic(e);
  1225.                     }
  1226.                 }
  1227.             }
  1228.             else
  1229.             {
  1230.                 for (var p in this.obj)
  1231.                 {
  1232.                     try
  1233.                     {
  1234.                         var subProps = this.props[p] = [];
  1235.                         var subobj = this.obj[p];
  1236.                         for (var p1 in subobj)
  1237.                             subProps[p1] = "" + subobj[p1];
  1238.                     }
  1239.                     catch (e)
  1240.                     {
  1241.                         onPanic(e);
  1242.                     }
  1243.                 }
  1244.             }
  1245.         }
  1246.         else if (typeof(this.obj) == "string")
  1247.         {
  1248.             this.props = this.obj;
  1249.         }
  1250.         else if (this.obj instanceof Ci.jsdIValue)
  1251.         {
  1252.             var listValue = {value: null}, lengthValue = {value: 0};
  1253.             this.obj.getProperties(listValue, lengthValue);
  1254.             for (var i = 0; i < lengthValue.value; ++i)
  1255.             {
  1256.                 var prop = listValue.value[i];
  1257.                 try {
  1258.                     var name = prop.name.getWrappedValue();
  1259.                     this.props[name] = "" + prop.value.getWrappedValue();
  1260.                 } catch (e) {
  1261.                     onPanic(e);
  1262.                 }
  1263.             }
  1264.         }
  1265.         else if (this.obj instanceof Ci.nsISupportsCString)
  1266.         {
  1267.             this.props = this.obj.data;
  1268.         }
  1269.         else
  1270.         {
  1271.             var propsTotal = 0;
  1272.             for (var p in this.obj)
  1273.             {
  1274.                 propsTotal++;
  1275.                 try
  1276.                 {
  1277.                     var pAsString = p + "";
  1278.                     var m = this.reXPConnect.exec(pAsString);
  1279.                     if (m)
  1280.                     {
  1281.                         var kind = m[1];
  1282.                         if (!this.obj[p] instanceof Ci[kind])
  1283.                         {
  1284.                             var xpobj = "" + this.obj[p].wrappedJSObject;
  1285.                             this.props[p] = xpobj;
  1286.                         }
  1287.                     }
  1288.  
  1289.                     try {
  1290.                         var value = "" + this.obj[p];
  1291.                         this.props[p] = value;
  1292.                     }
  1293.                     catch (err) {
  1294.                         this.props[p] = "{Error}";
  1295.                     }
  1296.                 }
  1297.                 catch (err)
  1298.                 {
  1299.                 }
  1300.             }
  1301.         }
  1302.  
  1303.         return this.props;
  1304.     },
  1305.  
  1306.     getInterfaces: function()
  1307.     {
  1308.         if (this.ifaces)
  1309.             return this.ifaces;
  1310.  
  1311.         this.ifaces = [];
  1312.         for (var iface in Ci) {
  1313.             if (this.obj instanceof Ci[iface]) {
  1314.                 var ifaceProps = this.ifaces[iface] = [];
  1315.                 for (p in Ci[iface])
  1316.                     ifaceProps[p] = this.obj[p];
  1317.             }
  1318.         }
  1319.         return this.ifaces;
  1320.     },
  1321.  
  1322.    getScope: function()
  1323.    {
  1324.        if (!Firebug.getPref(Firebug.prefDomain, "trace.enableScope"))
  1325.            return null;
  1326.  
  1327.        if (this.scope)
  1328.            return this.scope;
  1329.  
  1330.        var scope = {};
  1331.        Firebug.Debugger.halt(function(frame)
  1332.        {
  1333.            for (var i=0; i<4 && frame; i++)
  1334.                frame = frame.callingFrame;
  1335.  
  1336.            if (frame)
  1337.            {
  1338.                var listValue = {value: null}, lengthValue = {value: 0};
  1339.                frame.scope.getProperties(listValue, lengthValue);
  1340.  
  1341.                for (var i=lengthValue.value-1; i>=0; i--)
  1342.                {
  1343.                    var prop = listValue.value[i];
  1344.                    var name = prop.name.getWrappedValue();
  1345.                    var value = prop.value.getWrappedValue();
  1346.  
  1347.                    if ((typeof(value) != "function") && name && value)
  1348.                        scope[name.toString()] = value.toString();
  1349.                }
  1350.            }
  1351.        });
  1352.  
  1353.        return this.scope = scope;
  1354.    },
  1355.  
  1356.     getResponse: function()
  1357.     {
  1358.         var result = null;
  1359.         try
  1360.         {
  1361.             var self = this;
  1362.             TabWatcher.iterateContexts(function(context) {
  1363.                 var url = self.obj.originalURI.spec;
  1364.                 result = context.sourceCache.loadText(url);
  1365.                 if (result)
  1366.                     throw "OK"; // Break the cycle if the response is there.
  1367.             });
  1368.         }
  1369.         catch (err)
  1370.         {
  1371.         }
  1372.  
  1373.         return result;
  1374.     },
  1375.  
  1376.     getException: function()
  1377.     {
  1378.         if (this.err)
  1379.             return this.err;
  1380.  
  1381.         this.err = "";
  1382.  
  1383.         if (this.obj && this.obj.message)
  1384.             return this.obj.message;
  1385.  
  1386.         // xxxJJB: this isn't needed, instanceof does QI. try {this.obj = this.obj.QueryInterface(Ci.nsIException);} catch (err){}
  1387.         if (!this.obj)
  1388.             return null;
  1389.  
  1390.         if (this.obj instanceof Error || this.obj instanceof Ci.nsIException)
  1391.         {
  1392.             try
  1393.             {
  1394.                 this.err += "<span class='ExceptionMessage'>" + this.obj.message + "</span>" + EOF;
  1395.                 this.err += this.obj.name + EOF;
  1396.                 this.err += this.obj.fileName + "(" + this.obj.lineNumber+ ")" + EOF;
  1397.             }
  1398.             catch (err)
  1399.             {
  1400.                 onPanic(e);
  1401.             }
  1402.         }
  1403.  
  1404.         return this.err;
  1405.     },
  1406.  
  1407.     getTypes: function()
  1408.     {
  1409.         if (this.types)
  1410.             return this.types;
  1411.  
  1412.         this.types = "";
  1413.  
  1414.         try {
  1415.             var obj = this.obj;
  1416.             while (obj)
  1417.             {
  1418.                 this.types += "typeof = " + typeof(obj) + EOF;
  1419.                 if (obj)
  1420.                     this.types += "    constructor = " + obj.constructor + EOF;
  1421.  
  1422.                 obj = obj.prototype;
  1423.             }
  1424.         }
  1425.         catch (e)
  1426.         {
  1427.             onPanic(e);
  1428.         }
  1429.  
  1430.         return this.types;
  1431.     },
  1432.  
  1433.     getEvent: function()
  1434.     {
  1435.         if (!(this.obj instanceof Event))
  1436.             return;
  1437.  
  1438.         if (this.eventInfo)
  1439.             return this.eventInfo;
  1440.  
  1441.         this.eventInfo = "";
  1442.  
  1443.         try
  1444.         {
  1445.             if (this.obj.eventPhase == this.obj.AT_TARGET)
  1446.                 this.eventInfo += " at target ";
  1447.             else if (this.obj.eventPhase == this.obj.BUBBLING_PHASE)
  1448.                 this.eventInfo += " bubbling phase ";
  1449.             else
  1450.                 this.eventInfo += " capturing phase ";
  1451.  
  1452.             if (this.obj.relatedTarget)
  1453.                 this.eventInfo += this.obj.relatedTarget.tagName + "->";
  1454.  
  1455.             if (this.obj.currentTarget)
  1456.             {
  1457.                 if (this.obj.currentTarget.tagName)
  1458.                     this.eventInfo += this.obj.currentTarget.tagName + "->";
  1459.                 else
  1460.                     this.eventInfo += this.obj.currentTarget.nodeName + "->";
  1461.             }
  1462.  
  1463.             this.eventInfo += this.obj.target.tagName;
  1464.         }
  1465.         catch (err)
  1466.         {
  1467.             onPanic(err);
  1468.         }
  1469.  
  1470.         return this.eventInfo;
  1471.     },
  1472.  
  1473.     getObject: function()
  1474.     {
  1475.         return this.obj;
  1476.     }
  1477. }
  1478.  
  1479. var lastPanic = null;
  1480. function onPanic(errorMessage)
  1481. {
  1482.     var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);                       /*@explore*/
  1483.     var win = appShellService.hiddenDOMWindow;
  1484.     // XXXjjb I cannot get these tests to work. 
  1485.     //if (win.lastPanic && (win.lastPanic == errorMessage))
  1486.         win.dump("Another panic attack "+errorMessage+"\n");
  1487.     //else
  1488.     //    alert("Firebug traceModule panics: "+errorMessage);    
  1489.     
  1490.     win.lastPanic = errorMessage;        
  1491. }
  1492.  
  1493. // ************************************************************************************************
  1494. // Domplate helpers - Tree (domplate widget)
  1495.  
  1496. /**
  1497.  * This object is intended as a domplate widget for displaying hierarchical
  1498.  * structure (tree). Specific tree should be derived from this object and
  1499.  * getMembers method should be implemented.
  1500.  */
  1501. Firebug.TraceModule.Tree = domplate(Firebug.Rep,
  1502. {
  1503.     tag:
  1504.         TABLE({class: "domTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
  1505.             TBODY(
  1506.                 FOR("member", "$object|memberIterator",
  1507.                     TAG("$member|getRowTag", {member: "$member"}))
  1508.             )
  1509.         ),
  1510.  
  1511.     rowTag:
  1512.         TR({class: "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren",
  1513.             _repObject: "$member", level: "$member.level"},
  1514.             TD({class: "memberLabelCell", 
  1515.                 style: "padding-left: $member.indent\\px; width:1%; white-space: nowrap"},
  1516.                 DIV({class: "memberLabel $member.type\\Label"}, "$member.name")
  1517.             ),
  1518.             TD({class: "memberValueCell", style: "width: 100%;"},
  1519.                 TAG("$member.tag", {object: "$member.value"})
  1520.             )
  1521.         ),
  1522.  
  1523.     loop:
  1524.         FOR("member", "$members",
  1525.             TAG("$member|getRowTag", {member: "$member"})),
  1526.  
  1527.     memberIterator: function(object)
  1528.     {
  1529.         return this.getMembers(object);
  1530.     },
  1531.  
  1532.     getRowTag: function(member)
  1533.     {
  1534.         return this.rowTag;
  1535.     },
  1536.  
  1537.     onClick: function(event)
  1538.     {
  1539.         if (!isLeftClick(event))
  1540.             return;
  1541.  
  1542.         var row = getAncestorByClass(event.target, "memberRow");
  1543.         var label = getAncestorByClass(event.target, "memberLabel");
  1544.         if (label && hasClass(row, "hasChildren"))
  1545.             this.toggleRow(row);
  1546.     },
  1547.  
  1548.     toggleRow: function(row)
  1549.     {
  1550.         var level = parseInt(row.getAttribute("level"));
  1551.  
  1552.         if (hasClass(row, "opened"))
  1553.         {
  1554.             removeClass(row, "opened");
  1555.  
  1556.             var tbody = row.parentNode;
  1557.             for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling) {
  1558.                 if (parseInt(firstRow.getAttribute("level")) <= level)
  1559.                     break;
  1560.  
  1561.                 tbody.removeChild(firstRow);
  1562.             }
  1563.         }
  1564.         else
  1565.         {
  1566.             setClass(row, "opened");
  1567.  
  1568.             var repObject = row.repObject;
  1569.             if (repObject) {
  1570.                 var members = this.getMembers(repObject.value, level+1);
  1571.                 if (members)
  1572.                     this.loop.insertRows({members: members}, row);
  1573.             }
  1574.         }
  1575.     },
  1576.  
  1577.     getMembers: function(object, level)
  1578.     {
  1579.         if (!level)
  1580.             level = 0;
  1581.  
  1582.         if (typeof(object) == "string")
  1583.             return [this.createMember("", "", object, level)];
  1584.  
  1585.         var members = [];
  1586.         for (var p in object) {
  1587.             var member = this.createMember("", p, object[p], level);
  1588.             if (object[p] instanceof Array)
  1589.                 member.tag = FirebugReps.Nada.tag;
  1590.             members.push(member);
  1591.         }
  1592.         return members;
  1593.     },
  1594.  
  1595.     createMember: function(type, name, value, level)
  1596.     {
  1597.         var rep = Firebug.getRep(value);
  1598.         var tag = rep.shortTag ? rep.shortTag : rep.tag;
  1599.         var valueType = typeof(value);
  1600.         var hasChildren = this.hasProperties(value) && (valueType == "object");
  1601.  
  1602.         return {
  1603.             name: name,
  1604.             value: value,
  1605.             type: type,
  1606.             rowClass: "memberRow-" + type,
  1607.             open: "",
  1608.             level: level,
  1609.             indent: level*16,
  1610.             hasChildren: hasChildren,
  1611.             tag: tag
  1612.         };
  1613.     },
  1614.  
  1615.     hasProperties: function(ob)
  1616.     {
  1617.         try {
  1618.             for (var name in ob)
  1619.                 return true;
  1620.         } catch (exc) {}
  1621.         return false;
  1622.     }
  1623. });
  1624.  
  1625. // ************************************************************************************************
  1626.  
  1627. Firebug.TraceModule.PropertyTree = domplate(Firebug.TraceModule.Tree,
  1628. {
  1629.     reXPConnect: /\[xpconnect wrapped ([^\]]*)\]/,
  1630.  
  1631.     getMembers: function(object, level)
  1632.     {
  1633.         if (!level)
  1634.             level = 0;
  1635.  
  1636.         try
  1637.         {
  1638.             var members = [];
  1639.             for (var p in object)
  1640.             {
  1641.                 try
  1642.                 {
  1643.                     var pAsString = p + "";
  1644.                     var m = this.reXPConnect.exec(pAsString);
  1645.                     if (m)
  1646.                     {
  1647.                         var kind = m[1];
  1648.                         if (!object[p] instanceof Ci[kind])
  1649.                         {
  1650.                             var xpobj = object[p].wrappedJSObject;
  1651.                             members.push(this.createMember("dom", p, xpobj, level));
  1652.                         }
  1653.                     }
  1654.                     members.push(this.createMember("dom", p, object[p], level));
  1655.                 }
  1656.                 catch (e)
  1657.                 {
  1658.                 }
  1659.             }
  1660.         }
  1661.         catch (err)
  1662.         {
  1663.             FBTrace.sysout("Exception", err);
  1664.         }
  1665.  
  1666.         return members;
  1667.     }
  1668. });
  1669.  
  1670. // ************************************************************************************************
  1671. // Registration
  1672.  
  1673. Firebug.registerModule(Firebug.TraceModule);
  1674. Firebug.registerRep(Firebug.TraceModule.MessageTemplate);
  1675.  
  1676. // ************************************************************************************************
  1677.  
  1678. }});
  1679.