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