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