1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/lib/domplate", 7 "firebug/chrome/reps", 8 "firebug/lib/locale", 9 "firebug/lib/wrapper", 10 "firebug/js/stackFrame", 11 "firebug/lib/dom", 12 "firebug/lib/css", 13 "firebug/lib/string", 14 "firebug/js/fbs", 15 ], 16 function(Obj, Firebug, Domplate, FirebugReps, Locale, Wrapper, StackFrame, Dom, Css, Str, FBS) { 17 18 // ********************************************************************************************* // 19 20 var Cc = Components.classes; 21 var Ci = Components.interfaces; 22 23 var RETURN_CONTINUE = Ci.jsdIExecutionHook.RETURN_CONTINUE; 24 25 var memoryReporterManager; 26 27 try 28 { 29 memoryReporterManager = Cc["@mozilla.org/memory-reporter-manager;1"]. 30 getService(Ci.nsIMemoryReporterManager); 31 } 32 catch (err) 33 { 34 if (FBTrace.DBG_MEMORY_PROFILER) 35 FBTrace.sysout("memoryProfiler; Looks like '@mozilla.org/memory-reporter-manager;1'" + 36 "is no available", err); 37 } 38 39 // List of memory reports displayed in the result. Append new path in the list in order 40 // to create a new column in the result report-table. 41 var MEMORY_PATHS = 42 { 43 "explicit/js": true, 44 "explicit/js/gc-heap": true, 45 "explicit/js/tjit-data": true, 46 "explicit/js/mjit-code": true, 47 "explicit/images/content/used/raw": true, 48 }; 49 50 // ********************************************************************************************* // 51 52 Firebug.MemoryProfiler = Obj.extend(Firebug.Module, 53 { 54 dispatchName: "memoryProfiler", 55 56 initialize: function() // called once 57 { 58 Firebug.Module.initialize.apply(this, arguments); 59 60 if (FBTrace.DBG_MEMORY_PROFILER) 61 FBTrace.sysout("memoryProfiler; initialize"); 62 }, 63 64 shutdown: function() 65 { 66 Firebug.Module.shutdown.apply(this, arguments); 67 }, 68 69 initContext: function(context) 70 { 71 Firebug.Module.initContext.apply(this, arguments); 72 73 // xxxHonza: If profiling is on and the user reloads,needs better testing 74 // Profilinig should support reloads to profile page load. 75 if (this.profiling) 76 this.start(context); 77 }, 78 79 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 80 81 onConsoleCleared: function(context) 82 { 83 if (this.isProfiling()) 84 this.stop(context, true); 85 }, 86 87 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 88 // Activation/deactivation 89 90 toggleProfiling: function(context) 91 { 92 try 93 { 94 if (this.profiling) 95 this.stop(context); 96 else 97 this.start(context); 98 } 99 catch (err) 100 { 101 if (FBTrace.DBG_ERRORS) 102 FBTrace.sysout("memoryProfiler; toggleProfiling EXCEPTION " + err, err); 103 } 104 }, 105 106 isProfiling: function() 107 { 108 return this.profiling; 109 }, 110 111 start: function(context, title) 112 { 113 if (!memoryReporterManager) 114 { 115 // xxxHonza: locale if memory profiler will be part of 1.8 116 Firebug.Console.log("Memory profiler component is not available on your platform."); 117 return; 118 } 119 120 Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleMemoryProfiling", "checked", "true"); 121 122 this.profiling = true; 123 FBS.addHandler(this); 124 125 // Initialize structures for collected memory data. 126 context.memoryProfileStack = []; // Holds memory reports for called fucntions. 127 context.memoryProfileResult = {}; // Holds differences between function-call and function-return. 128 context.memoryProfileTime = (new Date()).getTime(); 129 130 // Memory leak detection 131 this.mark(context); 132 133 var isCustomMessage = !!title; 134 if (!isCustomMessage) 135 title = Locale.$STR("firebug.Memory Profiler Started"); 136 137 context.memoryProfileRow = this.logProfileRow(context, title); 138 context.memoryProfileRow.customMessage = isCustomMessage; 139 140 // For summary numbers (difference between profiling-start and profiling-end) 141 context.memoryProfileStack.push(this.getMemoryReport()); 142 143 Firebug.Console.addListener(this); 144 }, 145 146 stop: function(context, cancelReport) 147 { 148 FBS.removeHandler(this); 149 this.profiling = false; 150 151 Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleMemoryProfiling", "checked", "false"); 152 153 // Calculate total diff 154 var oldReport = context.memoryProfileStack.pop(); 155 var newReport = this.getMemoryReport(); 156 157 context.memoryProfileSummary = this.diffMemoryReport(oldReport, newReport); 158 context.memoryProfileTime = (new Date()).getTime() - context.memoryProfileTime; 159 160 this.logProfileReport(context, context.memoryProfileResult); 161 162 delete context.memoryProfileRow; 163 delete context.memoryProfileStack; 164 delete context.memoryProfileResult; 165 166 // Memory leak detection 167 var deltaObjects = this.sweep(context); 168 this.cleanUp(context); 169 170 if (!cancelReport) 171 { 172 var title = Locale.$STR("firebug.Objects Added While Profiling"); 173 var row = Firebug.Console.openCollapsedGroup(title, context, "profile", 174 Firebug.MemoryProfiler.ProfileCaption, true, null, true); 175 176 Firebug.Console.log(deltaObjects, context, "memoryDelta", Firebug.DOMPanel.DirTable); 177 Firebug.Console.closeGroup(context, true); 178 } 179 180 Firebug.Console.removeListener(this); 181 182 //Firebug.Console.logFormatted([deltaObjects], context, "memoryDelta"); 183 }, 184 185 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 186 // JSD Handler 187 188 unhook: function() 189 { 190 }, 191 192 hook: function() 193 { 194 }, 195 196 onFunctionCall: function(frame, depth) 197 { 198 var context = Firebug.Debugger.getContextByFrame(frame); 199 if (!context) 200 return RETURN_CONTINUE; 201 202 context.memoryProfileStack.push(this.getMemoryReport()); 203 204 return RETURN_CONTINUE; 205 }, 206 207 onFunctionReturn: function(frame, depth) 208 { 209 var context = Firebug.Debugger.getContextByFrame(frame); 210 if (!context) 211 return RETURN_CONTINUE; 212 213 frame = StackFrame.getStackFrame(frame, context); 214 215 var oldReport = context.memoryProfileStack.pop(); 216 var newReport = this.getMemoryReport(); 217 var diff = this.diffMemoryReport(oldReport, newReport); 218 219 // Collect reports. 220 var entryId = frameId(frame); 221 var entry = context.memoryProfileResult[entryId]; 222 223 if (entry) 224 { 225 entry.callCount++; 226 entry.report = this.sumMemoryReport(entry.report, diff); 227 } 228 else 229 { 230 context.memoryProfileResult[entryId] = { 231 callCount: 1, 232 report: diff, 233 frame: frame 234 }; 235 } 236 237 return RETURN_CONTINUE; 238 }, 239 240 /*onInterrupt: function(frame, depth) 241 { 242 },*/ 243 244 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 245 // Memory 246 247 getMemoryReport: function() 248 { 249 var report = {}; 250 251 if (!memoryReporterManager) 252 return report; 253 254 var iter = memoryReporterManager.enumerateReporters(); 255 while (iter.hasMoreElements()) 256 { 257 var reporter = iter.getNext().QueryInterface(Ci.nsIMemoryReporter); 258 if (MEMORY_PATHS[reporter.path]) 259 report[reporter.path] = reporter.memoryUsed; 260 } 261 return report; 262 }, 263 264 diffMemoryReport: function(oldReport, newReport) 265 { 266 var diff = {}; 267 for (var p in oldReport) 268 { 269 var oldVal = oldReport[p]; 270 var newVal = newReport[p]; 271 diff[p] = newVal - oldVal; 272 } 273 return diff; 274 }, 275 276 sumMemoryReport: function(report1, report2) 277 { 278 var sum = []; 279 for (var p in report1) 280 { 281 var val1 = report1[p]; 282 var val2 = report2[p]; 283 sum[p] = val1 + val2; 284 } 285 return sum; 286 }, 287 288 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 289 // Memory leak detection 290 291 mark: function(context) 292 { 293 // Iterate all objects of the content window. 294 var iter = new ObjectIterator(); 295 var contentView = Wrapper.getContentView(context.window); 296 iter.iterate(contentView, "window", function(obj, path) 297 { 298 // We have been here, bail out. 299 if (obj.hasOwnProperty("__fbugMemMark")) 300 return false; 301 302 if (FirebugReps.Arr.isArray(obj, context.window)) 303 obj.__fbugMemMark = obj.length; 304 else 305 obj.__fbugMemMark = true; 306 307 //if (FBTrace.DBG_MEMORY_PROFILER) 308 // FBTrace.sysout("mark "+path+": "+obj.__fbugMemMark+" view: "+ 309 // Wrapper.getContentView(obj)); 310 311 // Continue with children 312 return true; 313 }); 314 }, 315 316 sweep: function(context) 317 { 318 var iter = new ObjectIterator(); 319 iter.deltaObjects = {}; 320 321 var contentView = Wrapper.getContentView(context.window); 322 iter.iterate(contentView, "window", function(obj, path) 323 { 324 //if (FBTrace.DBG_MEMORY_PROFILER) 325 // FBTrace.sysout("sweep "+path+" "+obj.hasOwnProperty("__fbugMemSweep")+" view: "+ 326 // Wrapper.getContentView(obj), obj); 327 328 if (obj.hasOwnProperty("__fbugMemSweep")) 329 return false; 330 331 obj.__fbugMemSweep = true; 332 333 if (!obj.hasOwnProperty("__fbugMemMark")) // then we did not see this object 'before' 334 { 335 this.deltaObjects[path] = obj; 336 } 337 else // we did see it 338 { 339 // but it was an array with a different size 340 if (FirebugReps.Arr.isArray(obj, context.window) && 341 (obj.__fbugMemMark !== obj.length)) 342 { 343 this.deltaObjects[path] = obj; 344 } 345 } 346 347 // Iterate children 348 return true; 349 }); 350 351 return iter.deltaObjects; 352 }, 353 354 cleanUp: function(context) 355 { 356 var iter = new ObjectIterator(); 357 var contentView = Wrapper.getContentView(context.window); 358 iter.iterate(contentView, "window", function(obj, path) 359 { 360 if (!obj.hasOwnProperty("__fbugMemSweep")) 361 return false; 362 363 // Clean up 364 delete obj.__fbugMemSweep; 365 delete obj.__fbugMemMark; 366 367 return true; 368 }); 369 }, 370 371 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 372 // UI 373 374 logProfileRow: function(context, title) 375 { 376 var row = Firebug.Console.openGroup(title, context, "profile", 377 Firebug.MemoryProfiler.ProfileCaption, true, null, true); 378 Css.setClass(row, "profilerRunning"); 379 380 Firebug.Console.closeGroup(context, true); 381 382 return row; 383 }, 384 385 logProfileReport: function(context, memoryReport, cancel) 386 { 387 if (FBTrace.DBG_MEMORY_PROFILER) 388 { 389 FBTrace.sysout("memoryProfiler; logProfileReport", memoryReport); 390 FBTrace.sysout("memoryProfiler; logProfileReport SUMMARY", context.memoryProfileSummary); 391 } 392 393 // Get an existing console log (with throbber) or create a new one. 394 var groupRow = context.memoryProfileRow && context.memoryProfileRow.ownerDocument 395 ? context.memoryProfileRow 396 : this.logProfileRow(context); 397 delete context.memoryProfileRow; 398 399 Css.removeClass(groupRow, "profilerRunning"); 400 401 var calls = []; 402 var totalCalls = 0; 403 var sourceFileMap = context.sourceFileMap; 404 405 for (var p in memoryReport) 406 { 407 if (!memoryReport.hasOwnProperty(p)) 408 continue; 409 410 var entry = memoryReport[p]; 411 totalCalls++; 412 413 if (!entry.frame) 414 { 415 if (FBTrace.DBG_MEMORY_PROFILER) 416 FBTrace.sysout("memoryProfiler no entry.frame? for p="+p, entry); 417 continue; 418 } 419 420 var script = entry.frame.script; 421 var sourceLink = Firebug.SourceFile.getSourceLinkForScript(script, context); 422 423 if (sourceLink && sourceLink.href in sourceFileMap) 424 { 425 var call = new MemoryProfileCall(script, context, entry.callCount, 426 entry.report, sourceLink); 427 calls.push(call); 428 } 429 } 430 431 // Summary log 432 var call = new MemoryProfileSummary(context, context.memoryProfileSummary); 433 calls.push(call); 434 totalCalls++; 435 436 if (totalCalls > 0) 437 { 438 var captionBox = groupRow.getElementsByClassName("profileCaption").item(0); 439 if (!groupRow.customMessage) 440 captionBox.textContent = Locale.$STR("firebug.Memory Profiler Results"); 441 442 var timeBox = groupRow.getElementsByClassName("profileTime").item(0); 443 timeBox.textContent = "(" + Str.formatTime(context.memoryProfileTime) + ")"; 444 445 var groupBody = groupRow.lastChild; 446 var sizer = Firebug.MemoryProfiler.ProfileTable.tag.replace( 447 {object: MEMORY_PATHS}, groupBody); 448 449 var table = sizer.firstChild; 450 var tHeader = table.lastChild; // no rows inserted. 451 452 var callTag = Firebug.MemoryProfiler.ProfileCall.tag; 453 var sumTag = Firebug.MemoryProfiler.ProfileSummary.tag; 454 455 for (var i = 0; i < calls.length; ++i) 456 { 457 var call = calls[i]; 458 call.index = i; 459 var tag = (call instanceof MemoryProfileCall) ? callTag : sumTag; 460 context.throttle(tag.insertRows, tag, [{object: call}, tHeader]); 461 } 462 463 context.throttle(groupRow.scrollIntoView, groupRow, []); 464 } 465 else 466 { 467 var captionBox = groupRow.getElementsByClassName("profileCaption").item(0); 468 captionBox.textContent = Locale.$STR("NothingToProfile"); 469 } 470 } 471 }); 472 473 // ********************************************************************************************* // 474 475 function MemoryProfileCall(script, context, callCount, report, sourceLink) 476 { 477 this.script = script; 478 this.context = context; 479 this.callCount = callCount; 480 this.report = report; 481 this.sourceLink = sourceLink; 482 } 483 484 function MemoryProfileSummary(context, report) 485 { 486 this.context = context; 487 this.report = report; 488 } 489 490 // ********************************************************************************************* // 491 // Object Iterator 492 493 /** 494 * Recursively iterates all children objects. 495 */ 496 function ObjectIterator() 497 { 498 } 499 500 ObjectIterator.prototype = 501 /** @lends ObjectIterator */ 502 { 503 /** 504 * Recursive iteration over all children of given object 505 * @param {Object} obj The object to iterate 506 * @param {String} path helper path for logging. 507 * @param {Function} callback Callback function executed for each object. 508 */ 509 iterate: function(obj, path, callback) 510 { 511 if (!callback.apply(this, [obj, path])) 512 return; 513 514 var names = Object.keys(obj); 515 for (var i=0; i<names.length; i++) 516 { 517 var name = names[i]; 518 519 // Ignore memory-profiler helper fields 520 if (name === "__fbugMemSweep" || name === "__fbugMemMark") 521 continue; 522 523 // Ignore built-in objects 524 if (Dom.isDOMMember(obj, name) || Dom.isDOMConstant(obj, name)) 525 continue; 526 527 try 528 { 529 var child = obj[name]; 530 531 // xxxHonza, xxxJJB: this should be removed once the problem is clear. 532 if (name === "HTMLBodyElement") 533 FBTrace.sysout("memoryProfiler; HTMLBodyElement " + name + " instanceof: " + 534 (prop instanceof window.HTMLBodyElement) + " toString: " + child); 535 536 // Recursion 537 if (typeof(child) === "object") // TODO function 538 this.iterate(child, path + "." + name, callback); 539 } 540 catch (exc) 541 { 542 if (FBTrace.DBG_MEMORY_PROFILER) 543 FBTrace.sysout("memoryProfiler; iteration fails on " + path + "." + name, exc); 544 } 545 } 546 547 //xxxHonza, xxxJBB: iterate also prototype as soon as we understand the consequences. 548 /* 549 var proto = Object.getPrototypeOf(obj); 550 if (proto && typeof(proto) === 'object') 551 this.sweepRecursive(deltaObjects, proto, path+'.__proto__'); 552 */ 553 }, 554 }; 555 556 // ********************************************************************************************* // 557 // Domplate Templates 558 559 with (Domplate) { 560 Firebug.MemoryProfiler.ProfileTable = domplate( 561 { 562 tag: 563 DIV({"class": "profileSizer", "tabindex": "-1" }, 564 TABLE({"class": "profileTable", cellspacing: 0, cellpadding: 0, 565 width: "100%", "role": "grid"}, 566 THEAD({"class": "profileThead", "role": "presentation"}, 567 TR({"class": "headerRow focusRow profileRow subFocusRow", 568 onclick: "$onClick", "role": "row"}, 569 TH({"class": "headerCell alphaValue a11yFocus", "role": "columnheader"}, 570 DIV({"class": "headerCellBox"}, 571 Locale.$STR("Function") 572 ) 573 ), 574 TH({"class": "headerCell a11yFocus", "role": "columnheader"}, 575 DIV({"class": "headerCellBox", title: Locale.$STR("CallsHeaderTooltip")}, 576 Locale.$STR("Calls") 577 ) 578 ), 579 FOR("column", "$object|getColumns", 580 TH({"class": "headerCell a11yFocus", "role": "columnheader", 581 "aria-sort": "descending"}, 582 DIV({"class": "headerCellBox"}, 583 Locale.$STR("$column|getColumnLabel") 584 ) 585 ) 586 ), 587 TH({"class": "headerCell alphaValue a11yFocus", "role": "columnheader"}, 588 DIV({"class": "headerCellBox"}, 589 Locale.$STR("File") 590 ) 591 ) 592 ) 593 ), 594 TBODY({"class": "profileTbody", "role": "presentation"}) 595 ) 596 ), 597 598 getColumns: function(object) 599 { 600 var cols = []; 601 for (var p in object) 602 cols.push(p) 603 return cols; 604 }, 605 606 getColumnLabel: function(column) 607 { 608 return column; 609 }, 610 611 onClick: function(event) 612 { 613 var table = Dom.getAncestorByClass(event.target, "profileTable"); 614 var header = Dom.getAncestorByClass(event.target, "headerCell"); 615 if (!header) 616 return; 617 618 var numerical = !Css.hasClass(header, "alphaValue"); 619 620 var colIndex = 0; 621 for (header = header.previousSibling; header; header = header.previousSibling) 622 ++colIndex; 623 624 this.sort(table, colIndex, numerical); 625 }, 626 627 sort: function(table, colIndex, numerical) 628 { 629 sortAscending = function() 630 { 631 Css.removeClass(header, "sortedDescending"); 632 Css.setClass(header, "sortedAscending"); 633 header.setAttribute("aria-sort", "ascending"); 634 635 header.sorted = -1; 636 637 for (var i = 0; i < values.length; ++i) 638 tbody.appendChild(values[i].row); 639 }, 640 641 sortDescending = function() 642 { 643 Css.removeClass(header, "sortedAscending"); 644 Css.setClass(header, "sortedDescending"); 645 header.setAttribute("aria-sort", "descending") 646 647 header.sorted = 1; 648 649 for (var i = values.length-1; i >= 0; --i) 650 tbody.appendChild(values[i].row); 651 } 652 653 var tbody = Dom.getChildByClass(table, "profileTbody"); 654 var thead = Dom.getChildByClass(table, "profileThead"); 655 656 var values = []; 657 for (var row = tbody.childNodes[0]; row; row = row.nextSibling) 658 { 659 var cell = row.childNodes[colIndex]; 660 var sortValue = cell.sortValue ? cell.sortValue : cell.textContent; 661 var value = numerical ? parseFloat(sortValue) : sortValue; 662 values.push({row: row, value: value}); 663 } 664 665 values.sort(function(a, b) { return a.value < b.value ? -1 : 1; }); 666 667 var headerRow = thead.firstChild; 668 var headerSorted = Dom.getChildByClass(headerRow, "headerSorted"); 669 Css.removeClass(headerSorted, "headerSorted"); 670 if (headerSorted) 671 headerSorted.removeAttribute('aria-sort'); 672 673 var header = headerRow.childNodes[colIndex]; 674 Css.setClass(header, "headerSorted"); 675 676 if (numerical) 677 { 678 if (!header.sorted || header.sorted == -1) 679 { 680 sortDescending(); 681 } 682 else 683 { 684 sortAscending(); 685 } 686 } 687 else 688 { 689 if (!header.sorted || header.sorted == -1) 690 { 691 sortAscending(); 692 } 693 else 694 { 695 sortDescending(); 696 } 697 } 698 } 699 }); 700 701 // ********************************************************************************************* // 702 703 Firebug.MemoryProfiler.ProfileCaption = domplate(Firebug.Rep, 704 { 705 tag: 706 SPAN({"class": "profileTitle", "role": "status"}, 707 SPAN({"class": "profileCaption"}, "$object"), 708 " ", 709 SPAN({"class": "profileTime"}, "") 710 ) 711 }); 712 713 // ********************************************************************************************* // 714 715 // FirebugReps.OBJECTLINK is not yet initialized at this moment. 716 var OBJECTLINK = 717 A({ 718 "class": "objectLink objectLink-$className a11yFocus", 719 _repObject: "$object" 720 }); 721 722 Firebug.MemoryProfiler.ProfileCall = domplate(Firebug.Rep, 723 { 724 tag: 725 TR({"class": "focusRow profileRow subFocusRow", "role": "row"}, 726 TD({"class": "profileCell", "role": "presentation"}, 727 OBJECTLINK("$object|getCallName") 728 ), 729 TD({"class": "a11yFocus profileCell", "role": "gridcell"}, 730 "$object.callCount" 731 ), 732 FOR("column", "$object|getColumns", 733 TD({"class": "a11yFocus profileCell", "role": "gridcell", _sortValue: "$column"}, 734 "$column|getColumnLabel" 735 ) 736 ), 737 TD({"class": "linkCell profileCell", "role": "presentation"}, 738 TAG("$object|getSourceLinkTag", {object: "$object|getSourceLink"}) 739 ) 740 ), 741 742 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 743 744 getSourceLinkTag: function(object) 745 { 746 return FirebugReps.SourceLink.tag; 747 }, 748 749 getCallName: function(call) 750 { 751 return Str.cropString(StackFrame.getFunctionName(call.script, call.context), 60); 752 }, 753 754 getColumns: function(call) 755 { 756 var cols = []; 757 for (var p in MEMORY_PATHS) 758 cols.push(call.report[p] || 0); 759 return cols; 760 }, 761 762 getColumnLabel: function(call) 763 { 764 return Str.formatSize(call); 765 }, 766 767 getSourceLink: function(call) 768 { 769 return call.sourceLink; 770 }, 771 772 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 773 774 className: "profile", 775 776 supportsObject: function(object, type) 777 { 778 return object instanceof MemoryProfileCall; 779 }, 780 781 inspectObject: function(call, context) 782 { 783 var sourceLink = this.getSourceLink(call); 784 Firebug.chrome.select(sourceLink); 785 }, 786 787 getTooltip: function(call) 788 { 789 try 790 { 791 var fn = StackFrame.getFunctionName(call.script, call.context); 792 return FirebugReps.Func.getTooltip(fn, call.context); 793 } 794 catch (exc) 795 { 796 if (FBTrace.DBG_ERRORS) 797 FBTrace.sysout("profiler.getTooltip FAILS ", exc); 798 } 799 }, 800 801 getContextMenuItems: function(call, target, context) 802 { 803 var fn = Wrapper.unwrapIValue(call.script.functionObject); 804 return FirebugReps.Func.getContextMenuItems(fn, call.script, context); 805 } 806 }); 807 808 // ********************************************************************************************* // 809 810 Firebug.MemoryProfiler.ProfileSummary = domplate(Firebug.Rep, 811 { 812 tag: 813 TR({"class": "focusRow profileSummaryRow subFocusRow", "role": "row"}, 814 TD({"class": "profileCell", "role": "presentation", colspan: 2}, 815 Locale.$STR("firebug.Entire Session") 816 ), 817 FOR("column", "$object|getColumns", 818 TD({"class": "a11yFocus profileCell", "role": "gridcell", _sortValue: "$column"}, 819 "$column|getColumnLabel" 820 ) 821 ), 822 TD({"class": "linkCell profileCell", "role": "presentation"}) 823 ), 824 825 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 826 827 className: "profile", 828 829 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 830 831 getColumns: function(call) 832 { 833 return Firebug.MemoryProfiler.ProfileCall.getColumns(call) 834 }, 835 836 getColumnLabel: function(call) 837 { 838 return Firebug.MemoryProfiler.ProfileCall.getColumnLabel(call); 839 }, 840 }); 841 842 } // END with Domplate 843 844 // ********************************************************************************************* // 845 // Private Functions 846 847 function frameId(frame, depth) 848 { 849 if (frame) 850 return frame.script.tag+"@"+frame.line; 851 else 852 return "noIdForNoframe"; 853 } 854 855 // ********************************************************************************************* // 856 // Registration 857 858 Firebug.registerModule(Firebug.MemoryProfiler); 859 Firebug.registerRep(Firebug.MemoryProfiler.ProfileCall); 860 861 return Firebug.MemoryProfiler; 862 863 // ********************************************************************************************* // 864 }); 865