--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -37,53 +37,37 @@
* ***** END LICENSE BLOCK ***** */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
-// Must use .href here instead of .search because "about:memory" is a
-// non-standard URL.
-var gVerbose = (location.href.split(/[\?,]/).indexOf("verbose") !== -1);
+const gVerbose = location.href === "about:memory?verbose";
var gAddedObserver = false;
-const KIND_NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
-const KIND_HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
-const KIND_OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
-const UNITS_BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
-const UNITS_COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
+const KIND_NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
+const KIND_HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+const KIND_OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+const UNITS_BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+const UNITS_COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
const UNITS_COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
-const UNITS_PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
+const UNITS_PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
const kUnknown = -1; // used for _amount if a memory reporter failed
-// Paths, names and descriptions all need to be sanitized before being
-// displayed, because the user has some control over them via names of
-// compartments, windows, etc. Also, forward slashes in URLs in paths are
-// represented with backslashes to avoid being mistaken for path separators, so
-// we need to undo that as well.
-
-function escapeAll(aStr)
-{
- return aStr.replace(/\&/g, '&').replace(/'/g, ''').
- replace(/\</g, '<').replace(/>/g, '>').
- replace(/\"/g, '"');
-}
-
-function flipBackslashes(aStr)
-{
- return aStr.replace(/\\/g, '/');
-}
-
+// Forward slashes in URLs in paths are represented with backslashes to avoid
+// being mistaken for path separators. Paths/names/descriptions where this
+// hasn't been undone are prefixed with "unsafe"; the rest are prefixed with
+// "safe".
function makeSafe(aUnsafeStr)
{
- return escapeAll(flipBackslashes(aUnsafeStr));
+ return aUnsafeStr.replace(/\\/g, '/');
}
const kTreeUnsafeDescriptions = {
'explicit' :
"This tree covers explicit memory allocations by the application, " +
"both at the operating system level (via calls to functions such as " +
"VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " +
"(via functions such as malloc, calloc, realloc, memalign, operator " +
@@ -163,21 +147,16 @@ function onUnload()
}
}
function ChildMemoryListener(aSubject, aTopic, aData)
{
update();
}
-function $(n)
-{
- return document.getElementById(n);
-}
-
function doGlobalGC()
{
Cu.forceGC();
var os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
os.notifyObservers(null, "child-gc-request", null);
update();
}
@@ -310,90 +289,115 @@ function getReportersByProcess(aMgr)
catch(e) {
debug("An error occurred when collecting a multi-reporter's results: " + e);
}
}
return reportersByProcess;
}
+function appendTextNode(aP, aText)
+{
+ var e = document.createTextNode(aText);
+ aP.appendChild(e);
+ return e;
+}
+
+function appendElement(aP, aTagName, aClassName)
+{
+ var e = document.createElement(aTagName);
+ e.className = aClassName;
+ aP.appendChild(e);
+ return e;
+}
+
+function appendElementWithText(aP, aTagName, aClassName, aText)
+{
+ var e = appendElement(aP, aTagName, aClassName);
+ appendTextNode(e, aText);
+ return e;
+}
+
/**
* Top-level function that does the work of generating the page.
*/
function update()
{
// First, clear the page contents. Necessary because update() might be
// called more than once due to ChildMemoryListener.
- var content = $("content");
- content.parentNode.replaceChild(content.cloneNode(false), content);
- content = $("content");
-
- if (gVerbose)
- content.parentNode.classList.add('verbose');
- else
- content.parentNode.classList.add('non-verbose');
+ var oldContent = document.getElementById("content");
+ var content = oldContent.cloneNode(false);
+ oldContent.parentNode.replaceChild(content, oldContent);
+ content.classList.add(gVerbose ? 'verbose' : 'non-verbose');
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
getService(Ci.nsIMemoryReporterManager);
- var text = "";
-
// Generate output for one process at a time. Always start with the
// Main process.
var reportersByProcess = getReportersByProcess(mgr);
var hasMozMallocUsableSize = mgr.hasMozMallocUsableSize;
- text += genProcessText("Main", reportersByProcess["Main"],
- hasMozMallocUsableSize);
+ appendProcessElements(content, "Main", reportersByProcess["Main"],
+ hasMozMallocUsableSize);
for (var process in reportersByProcess) {
if (process !== "Main") {
- text += genProcessText(process, reportersByProcess[process],
- hasMozMallocUsableSize);
+ appendProcessElements(content, process, reportersByProcess[process],
+ hasMozMallocUsableSize);
}
}
+ appendElement(content, "hr");
+
// Memory-related actions.
const UpDesc = "Re-measure.";
const GCDesc = "Do a global garbage collection.";
const CCDesc = "Do a cycle collection.";
const MPDesc = "Send three \"heap-minimize\" notifications in a " +
"row. Each notification triggers a global garbage " +
"collection followed by a cycle collection, and causes the " +
"process to reduce memory usage in other ways, e.g. by " +
"flushing various caches.";
+ function appendButton(aTitle, aOnClick, aText, aId)
+ {
+ var b = appendElementWithText(content, "button", "", aText);
+ b.title = aTitle;
+ b.onclick = aOnClick
+ if (aId) {
+ b.id = aId;
+ }
+ }
+
// The "Update" button has an id so it can be clicked in a test.
- text += "<div>" +
- "<button title='" + UpDesc + "' onclick='update()' id='updateButton'>Update</button>" +
- "<button title='" + GCDesc + "' onclick='doGlobalGC()'>GC</button>" +
- "<button title='" + CCDesc + "' onclick='doCC()'>CC</button>" +
- "<button title='" + MPDesc + "' onclick='sendHeapMinNotifications()'>" + "Minimize memory usage</button>" +
- "</div>";
-
- // Generate verbosity option link at the bottom.
- text += "<div>";
- text += gVerbose
- ? "<span class='option'><a href='about:memory'>Less verbose</a></span>"
- : "<span class='option'><a href='about:memory?verbose'>More verbose</a></span>";
- text += "</div>";
+ appendButton(UpDesc, update, "Update", "updateButton");
+ appendButton(GCDesc, doGlobalGC, "GC");
+ appendButton(CCDesc, doCC, "CC");
+ appendButton(MPDesc, sendHeapMinNotifications, "Minimize memory usage");
- text += "<div>" +
- "<span class='option'><a href='about:support'>Troubleshooting information</a></span>" +
- "</div>";
+ var div1 = appendElement(content, "div", "");
+ var a;
+ if (gVerbose) {
+ var a = appendElementWithText(div1, "a", "option", "Less verbose");
+ a.href = "about:memory";
+ } else {
+ var a = appendElementWithText(div1, "a", "option", "More verbose");
+ a.href = "about:memory?verbose";
+ }
- text += "<div>" +
- "<span class='legend'>Click on a non-leaf node in a tree to expand ('++') " +
- "or collapse ('--') its children.</span>" +
- "</div>";
- text += "<div>" +
- "<span class='legend'>Hover the pointer over the name of a memory " +
- "reporter to see a description of what it measures.</span>";
+ var div2 = appendElement(content, "div", "");
+ a = appendElementWithText(div2, "a", "option", "Troubleshooting information");
+ a.href = "about:support";
- var div = document.createElement("div");
- div.innerHTML = text;
- content.appendChild(div);
+ var legendText1 = "Click on a non-leaf node in a tree to expand ('++') " +
+ "or collapse ('--') its children.";
+ var legendText2 = "Hover the pointer over the name of a memory reporter " +
+ "to see a description of what it measures.";
+
+ appendElementWithText(content, "div", "legend", legendText1);
+ appendElementWithText(content, "div", "legend", legendText2);
}
// There are two kinds of TreeNode.
// - Leaf TreeNodes correspond to Reporters and have more properties.
// - Non-leaf TreeNodes are just scaffolding nodes for the tree; their values
// are derived from their children.
function TreeNode(aUnsafeName)
{
@@ -573,17 +577,16 @@ function ignoreTree(aReporters, aTreeNam
* The tree.
* @param aReporters
* Table of Reporters for this process, indexed by _unsafePath.
* @return A boolean indicating if "heap-allocated" is known for the process.
*/
function fixUpExplicitTree(aT, aReporters)
{
// Determine how many bytes are reported by heap reporters.
- var s = "";
function getKnownHeapUsedBytes(aT)
{
var n = 0;
if (aT._kids.length === 0) {
// Leaf node.
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
n = aT._kind === KIND_HEAP ? aT._amount : 0;
} else {
@@ -696,122 +699,123 @@ function sortTreeAndInsertAggregateNodes
sortTreeAndInsertAggregateNodes(aTotalBytes, aT._kids[i]);
}
// Global variable indicating if we've seen any invalid values for this
// process; it holds the unsafePaths of any such reporters. It is reset for
// each new process.
var gUnsafePathsWithInvalidValuesForThisProcess = [];
-function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize)
+function appendWarningElements(aP, aHasKnownHeapAllocated,
+ aHasMozMallocUsableSize)
{
- var warningText = "";
-
if (!aHasKnownHeapAllocated && !aHasMozMallocUsableSize) {
- warningText =
- "<p class='accuracyWarning'>WARNING: the 'heap-allocated' memory " +
- "reporter and the moz_malloc_usable_size() function do not work for " +
- "this platform and/or configuration. This means that " +
- "'heap-unclassified' is zero and the 'explicit' tree shows " +
- "much less memory than it should.</p>\n\n";
+ appendElementWithText(aP, "p", "",
+ "WARNING: the 'heap-allocated' memory reporter and the " +
+ "moz_malloc_usable_size() function do not work for this platform " +
+ "and/or configuration. This means that 'heap-unclassified' is zero " +
+ "and the 'explicit' tree shows much less memory than it should.");
+ appendTextNode(aP, "\n\n");
} else if (!aHasKnownHeapAllocated) {
- warningText =
- "<p class='accuracyWarning'>WARNING: the 'heap-allocated' memory " +
- "reporter does not work for this platform and/or configuration. " +
- "This means that 'heap-unclassified' is zero and the 'explicit' tree " +
- "shows less memory than it should.</p>\n\n";
+ appendElementWithText(aP, "p", "",
+ "WARNING: the 'heap-allocated' memory reporter does not work for this " +
+ "platform and/or configuration. This means that 'heap-unclassified' " +
+ "is zero and the 'explicit' tree shows less memory than it should.");
+ appendTextNode(aP, "\n\n");
} else if (!aHasMozMallocUsableSize) {
- warningText =
- "<p class='accuracyWarning'>WARNING: the moz_malloc_usable_size() " +
- "function does not work for this platform and/or configuration. " +
- "This means that much of the heap-allocated memory is not measured " +
- "by individual memory reporters and so will fall under " +
- "'heap-unclassified'.</p>\n\n";
+ appendElementWithText(aP, "p", "",
+ "WARNING: the moz_malloc_usable_size() function does not work for " +
+ "this platform and/or configuration. This means that much of the " +
+ "heap-allocated memory is not measured by individual memory reporters " +
+ "and so will fall under 'heap-unclassified'.");
+ appendTextNode(aP, "\n\n");
}
if (gUnsafePathsWithInvalidValuesForThisProcess.length > 0) {
- warningText +=
- "<div class='accuracyWarning'>" +
- "<p>WARNING: the following values are negative or unreasonably " +
- "large.</p>\n" +
- "<ul>";
+ var div = appendElement(aP, "div", "");
+ appendElementWithText(div, "p", "",
+ "WARNING: the following values are negative or unreasonably large.");
+ appendTextNode(div, "\n");
+
+ var ul = appendElement(div, "ul", "");
for (var i = 0;
i < gUnsafePathsWithInvalidValuesForThisProcess.length;
i++)
{
- warningText +=
- " <li>" +
- makeSafe(gUnsafePathsWithInvalidValuesForThisProcess[i]) +
- "</li>\n";
+ appendTextNode(ul, " ");
+ appendElementWithText(ul, "li", "",
+ makeSafe(gUnsafePathsWithInvalidValuesForThisProcess[i]));
+ appendTextNode(ul, "\n");
}
- warningText +=
- "</ul>" +
- "<p>This indicates a defect in one or more memory reporters. The " +
+
+ appendElementWithText(div, "p", "",
+ "This indicates a defect in one or more memory reporters. The " +
"invalid values are highlighted, but you may need to expand one " +
- "or more sub-trees to see them.</p>\n\n" +
- "</div>";
+ "or more sub-trees to see them.");
+ appendTextNode(div, "\n\n");
gUnsafePathsWithInvalidValuesForThisProcess = []; // reset for the next process
}
-
- return warningText;
}
/**
- * Generates the text for a single process.
+ * Appends the elements for a single process.
*
+ * @param aP
+ * The parent DOM node.
* @param aProcess
* The name of the process.
* @param aReporters
* Table of Reporters for this process, indexed by _unsafePath.
* @param aHasMozMallocUsableSize
* Boolean indicating if moz_malloc_usable_size works.
* @return The generated text.
*/
-function genProcessText(aProcess, aReporters, aHasMozMallocUsableSize)
+function appendProcessElements(aP, aProcess, aReporters,
+ aHasMozMallocUsableSize)
{
+ appendElementWithText(aP, "h1", "", aProcess + " Process");
+ appendTextNode(aP, "\n\n"); // gives nice spacing when we cut and paste
+
+ // We'll fill this in later.
+ var warningsDiv = appendElement(aP, "div", "accuracyWarning");
+
var explicitTree = buildTree(aReporters, 'explicit');
var hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReporters);
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
- var explicitText = genTreeText(explicitTree, aProcess);
+ appendTreeElements(aP, explicitTree, aProcess);
// We only show these breakdown trees in verbose mode.
- var mapTreeText = "";
kMapTreePaths.forEach(function(t) {
if (gVerbose) {
var tree = buildTree(aReporters, t);
// |tree| will be null if we don't have any reporters for the given
// unsafePath.
if (tree) {
sortTreeAndInsertAggregateNodes(tree._amount, tree);
tree._hideKids = true; // map trees are always initially collapsed
- mapTreeText += genTreeText(tree, aProcess);
+ appendTreeElements(aP, tree, aProcess);
}
} else {
ignoreTree(aReporters, t);
}
});
- // We have to call genOtherText after we process all the trees, because it
- // looks at all the reporters which aren't part of a tree.
- var otherText = genOtherText(aReporters, aProcess);
+ // We have to call appendOtherElements after we process all the trees,
+ // because it looks at all the reporters which aren't part of a tree.
+ var otherText = appendOtherElements(aP, aReporters, aProcess);
- // Generate any warnings about inaccuracies due to platform limitations.
- // This must come after generating all the text. The newlines give nice
- // spacing if we cut+paste into a text buffer.
- var warningText = "";
- var warningText =
- genWarningText(hasKnownHeapAllocated, aHasMozMallocUsableSize);
-
- // The newlines give nice spacing if we cut+paste into a text buffer.
- return "<h1>" + aProcess + " Process</h1>\n\n" +
- warningText + explicitText + mapTreeText + otherText +
- "<hr></hr>";
+ // Add any warnings about inaccuracies due to platform limitations.
+ // These must be computed after generating all the text. The newlines give
+ // nice spacing if we cut+paste into a text buffer.
+ var warningElements =
+ appendWarningElements(warningsDiv, hasKnownHeapAllocated,
+ aHasMozMallocUsableSize);
}
/**
* Determines if a number has a negative sign when converted to a string.
* Works even for -0.
*
* @param aN
* The number.
@@ -955,72 +959,74 @@ function getUnsafeDescription(aReporters
// There's a subset of the Unicode "light" box-drawing chars that are widely
// implemented in terminals, and this code sticks to that subset to maximize
// the chance that cutting and pasting about:memory output to a terminal will
// work correctly:
const kHorizontal = "\u2500",
kVertical = "\u2502",
kUpAndRight = "\u2514",
- kVerticalAndRight = "\u251c";
+ kVerticalAndRight = "\u251c",
+ kDoubleHorizontalSep = " \u2500\u2500 ";
-function genMrValueText(aValue, aIsInvalid)
+function appendMrValueSpan(aP, aValue, aIsInvalid)
{
- return aIsInvalid ?
- "<span class='mrValue invalid'>" + aValue + "</span>" :
- "<span class='mrValue'>" + aValue + "</span>";
+ appendElementWithText(aP, "span", "mrValue" + (aIsInvalid ? " invalid" : ""),
+ aValue);
}
function kindToString(aKind)
{
switch (aKind) {
case KIND_NONHEAP: return "(Non-heap) ";
case KIND_HEAP: return "(Heap) ";
case KIND_OTHER:
case undefined: return "";
default: assert(false, "bad kind in kindToString");
}
}
-function genMrNameText(aKind, aShowSubtrees, aHasKids, aUnsafeDesc,
- aUnsafeName, aIsUnknown, aIsInvalid, aNMerged)
+function appendMrNameSpan(aP, aKind, aShowSubtrees, aHasKids, aUnsafeDesc,
+ aUnsafeName, aIsUnknown, aIsInvalid, aNMerged)
{
var text = "";
if (aHasKids) {
if (aShowSubtrees) {
- text += "<span class='mrSep hidden'> ++ </span>";
- text += "<span class='mrSep'> -- </span>";
+ appendElementWithText(aP, "span", "mrSep hidden", " ++ ");
+ appendElementWithText(aP, "span", "mrSep", " -- ");
} else {
- text += "<span class='mrSep'> ++ </span>";
- text += "<span class='mrSep hidden'> -- </span>";
+ appendElementWithText(aP, "span", "mrSep", " ++ ");
+ appendElementWithText(aP, "span", "mrSep hidden", " -- ");
}
} else {
- text += "<span class='mrSep'> " + kHorizontal + kHorizontal + " </span>";
+ appendElementWithText(aP, "span", "mrSep", kDoubleHorizontalSep);
}
- text += "<span class='mrName' title='" +
- kindToString(aKind) + makeSafe(aUnsafeDesc) + "'>" +
- makeSafe(aUnsafeName) + "</span>";
+
+ var nameSpan = appendElementWithText(aP, "span", "mrName",
+ makeSafe(aUnsafeName));
+ nameSpan.title = kindToString(aKind) + makeSafe(aUnsafeDesc);
+
if (aIsUnknown) {
- const problemDesc =
+ var noteSpan = appendElementWithText(aP, "span", "mrNote", " [*]");
+ noteSpan.title =
"Warning: this memory reporter was unable to compute a useful value. ";
- text += "<span class='mrNote' title=\"" + problemDesc + "\"> [*]</span>";
}
if (aIsInvalid) {
- const invalidDesc =
+ var noteSpan = appendElementWithText(aP, "span", "mrNote", " [?!]");
+ noteSpan.title =
"Warning: this value is invalid and indicates a bug in one or more " +
"memory reporters. ";
- text += "<span class='mrNote' title=\"" + invalidDesc + "\"> [?!]</span>";
}
if (aNMerged) {
- const dupDesc = "This value is the sum of " + aNMerged +
- " memory reporters that all have the same path.";
- text += "<span class='mrNote' title=\"" + dupDesc + "\"> [" +
- aNMerged + "]</span>";
+ var noteSpan = appendElementWithText(aP, "span", "mrNote",
+ " [" + aNMerged + "]");
+ noteSpan.title =
+ "This value is the sum of " + aNMerged +
+ " memory reporters that all have the same path.";
}
- return text + '\n';
}
// This is used to record the (safe) IDs of which sub-trees have been toggled,
// so the collapsed/expanded state can be replicated when the page is
// regenerated. It can end up holding IDs of nodes that no longer exist, e.g.
// for compartments that have been closed. This doesn't seem like a big deal,
// because the number is limited by the number of entries the user has changed
// from their original state.
@@ -1062,68 +1068,73 @@ function toggle(aEvent)
if (gTogglesBySafeTreeId[safeTreeId]) {
delete gTogglesBySafeTreeId[safeTreeId];
} else {
gTogglesBySafeTreeId[safeTreeId] = true;
}
}
/**
- * Generates the text for the tree, including its heading.
+ * Appends the elements for the tree, including its heading.
*
+ * @param aPOuter
+ * The parent DOM node.
* @param aT
* The tree.
* @param aProcess
* The process the tree corresponds to.
* @return The generated text.
*/
-function genTreeText(aT, aProcess)
+function appendTreeElements(aPOuter, aT, aProcess)
{
var treeBytes = aT._amount;
var rootStringLength = aT.toString().length;
var isExplicitTree = aT._unsafeName == 'explicit';
/**
- * Generates the text for a particular tree, without a heading.
+ * Appends the elements for a particular tree, without a heading.
*
+ * @param aP
+ * The parent DOM node.
* @param aUnsafePrePath
* The partial unsafePath leading up to this node.
* @param aT
* The tree.
* @param aIndentGuide
* Records what indentation is required for this tree. It has one
* entry per level of indentation. For each entry, ._isLastKid
* records whether the node in question is the last child, and
* ._depth records how many chars of indentation are required.
* @param aParentStringLength
* The length of the formatted byte count of the top node in the tree.
* @return The generated text.
*/
- function genTreeText2(aUnsafePrePath, aT, aIndentGuide, aParentStringLength)
+ function appendTreeElements2(aP, aUnsafePrePath, aT, aIndentGuide,
+ aParentStringLength)
{
function repeatStr(aC, aN)
{
var s = "";
for (var i = 0; i < aN; i++) {
s += aC;
}
return s;
}
// Determine if we should show the sub-tree below this entry; this
// involves reinstating any previous toggling of the sub-tree.
var unsafePath = aUnsafePrePath + aT._unsafeName;
- var safeTreeId = escapeAll(aProcess + ":" + unsafePath);
+ var safeTreeId = makeSafe(aProcess + ":" + unsafePath);
var showSubtrees = !aT._hideKids;
if (gTogglesBySafeTreeId[safeTreeId]) {
showSubtrees = !showSubtrees;
}
// Generate the indent.
- var indent = "<span class='treeLine'>";
+ var indent = "";
if (aIndentGuide.length > 0) {
for (var i = 0; i < aIndentGuide.length - 1; i++) {
indent += aIndentGuide[i]._isLastKid ? " " : kVertical;
indent += repeatStr(" ", aIndentGuide[i]._depth - 1);
}
indent += aIndentGuide[i]._isLastKid ? kUpAndRight : kVerticalAndRight;
indent += repeatStr(kHorizontal, aIndentGuide[i]._depth - 1);
}
@@ -1132,76 +1143,83 @@ function genTreeText(aT, aProcess)
var tString = aT.toString();
var extraIndentLength = Math.max(aParentStringLength - tString.length, 0);
if (extraIndentLength > 0) {
for (var i = 0; i < extraIndentLength; i++) {
indent += kHorizontal;
}
aIndentGuide[aIndentGuide.length - 1]._depth += extraIndentLength;
}
- indent += "</span>";
// Generate the percentage; detect and record invalid values at the same
// time.
var percText = "";
var tIsInvalid = false;
if (aT._amount === treeBytes) {
percText = "100.0";
} else {
var perc = (100 * aT._amount / treeBytes);
if (!(0 <= perc && perc <= 100)) {
tIsInvalid = true;
gUnsafePathsWithInvalidValuesForThisProcess.push(unsafePath);
}
percText = (100 * aT._amount / treeBytes).toFixed(2);
percText = pad(percText, 5, '0');
}
- percText = tIsInvalid ?
- "<span class='mrPerc invalid'> (" + percText + "%)</span>" :
- "<span class='mrPerc'> (" + percText + "%)</span>";
-
- // We don't want to show '(nonheap)' on a tree like 'map/vsize', since the
- // whole tree is non-heap.
- var kind = isExplicitTree ? aT._kind : undefined;
+ percText = " (" + percText + "%)";
// For non-leaf nodes, the entire sub-tree is put within a span so it can
// be collapsed if the node is clicked on.
var hasKids = aT._kids.length > 0;
if (!hasKids) {
assert(!aT._hideKids, "leaf node with _hideKids set")
}
- var text = indent;
+
+ appendElementWithText(aP, "span", "treeLine", indent);
+
+ var d;
if (hasKids) {
- text += "<span onclick='toggle(event)' class='hasKids' id='" +
- safeTreeId + "'>";
+ d = appendElement(aP, "span", "hasKids");
+ d.id = safeTreeId;
+ d.onclick = toggle;
+ } else {
+ d = aP;
}
- text += genMrValueText(tString, tIsInvalid) + percText;
- text += genMrNameText(kind, showSubtrees, hasKids, aT._unsafeDescription,
- aT._unsafeName, aT._isUnknown, tIsInvalid,
- aT._nMerged);
+
+ appendMrValueSpan(d, tString, tIsInvalid);
+ appendElementWithText(d, "span", "mrPerc", percText);
+
+ // We don't want to show '(nonheap)' on a tree like 'map/vsize', since the
+ // whole tree is non-heap.
+ var kind = isExplicitTree ? aT._kind : undefined;
+ appendMrNameSpan(d, kind, showSubtrees, hasKids, aT._unsafeDescription,
+ aT._unsafeName, aT._isUnknown, tIsInvalid, aT._nMerged);
+ appendTextNode(d, "\n");
+
if (hasKids) {
- var hiddenText = showSubtrees ? "" : " hidden";
// The 'kids' class is just used for sanity checking in toggle().
- text += "</span><span class='kids" + hiddenText + "'>";
+ d = appendElement(aP, "span", showSubtrees ? "kids" : "kids hidden");
+ } else {
+ d = aP;
}
for (var i = 0; i < aT._kids.length; i++) {
// 3 is the standard depth, the callee adjusts it if necessary.
aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
- text += genTreeText2(unsafePath + "/", aT._kids[i], aIndentGuide,
- tString.length);
+ appendTreeElements2(d, unsafePath + "/", aT._kids[i], aIndentGuide,
+ tString.length);
aIndentGuide.pop();
}
- text += hasKids ? "</span>" : "";
- return text;
}
- var text = genTreeText2(/* prePath = */"", aT, [], rootStringLength);
-
- return genSectionMarkup(aT._unsafeName, text);
+ appendSectionHeader(aPOuter, kTreeNames[aT._unsafeName]);
+
+ var pre = appendElement(aPOuter, "pre", "tree");
+ appendTreeElements2(pre, /* prePath = */"", aT, [], rootStringLength);
+ appendTextNode(aPOuter, "\n"); // gives nice spacing when we cut and paste
}
function OtherReporter(aUnsafePath, aUnits, aAmount, aUnsafeDesc, aNMerged)
{
// Nb: _kind is not needed, it's always KIND_OTHER.
this._unsafePath = aUnsafePath;
this._units = aUnits;
if (aAmount === kUnknown) {
@@ -1242,26 +1260,32 @@ OtherReporter.prototype = {
OtherReporter.compare = function(a, b) {
return a._unsafePath < b._unsafePath ? -1 :
a._unsafePath > b._unsafePath ? 1 :
0;
};
/**
- * Generates the text for the "Other Measurements" section.
+ * Appends the elements for the "Other Measurements" section.
*
+ * @param aP
+ * The parent DOM node.
* @param aReportersByProcess
* Table of Reporters for this process, indexed by _unsafePath.
* @param aProcess
* The process these reporters correspond to.
* @return The generated text.
*/
-function genOtherText(aReportersByProcess, aProcess)
+function appendOtherElements(aP, aReportersByProcess, aProcess)
{
+ appendSectionHeader(aP, kTreeNames['other']);
+
+ var pre = appendElement(aP, "pre", "tree");
+
// Generate an array of Reporter-like elements, stripping out all the
// Reporters that have already been handled. Also find the width of the
// widest element, so we can format things nicely.
var maxStringLength = 0;
var otherReporters = [];
for (var unsafePath in aReportersByProcess) {
var r = aReportersByProcess[unsafePath];
if (!r._done) {
@@ -1281,37 +1305,36 @@ function genOtherText(aReportersByProces
// Generate text for the not-yet-printed values.
var text = "";
for (var i = 0; i < otherReporters.length; i++) {
var o = otherReporters[i];
var oIsInvalid = o.isInvalid();
if (oIsInvalid) {
gUnsafePathsWithInvalidValuesForThisProcess.push(o._unsafePath);
}
- text += genMrValueText(pad(o._asString, maxStringLength, ' '), oIsInvalid);
- text += genMrNameText(KIND_OTHER, /* showSubtrees = */true,
- /* hasKids = */false, o._unsafeDescription,
- o._unsafePath, o._isUnknown, oIsInvalid);
+ appendMrValueSpan(pre, pad(o._asString, maxStringLength, ' '), oIsInvalid);
+ appendMrNameSpan(pre, KIND_OTHER, /* showSubtrees = */true,
+ /* hasKids = */false, o._unsafeDescription,
+ o._unsafePath, o._isUnknown, oIsInvalid);
+ appendTextNode(pre, "\n");
}
- return genSectionMarkup('other', text);
+ appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste
}
-function genSectionMarkup(aName, aText)
+function appendSectionHeader(aP, aText)
{
- return "<h2 class='sectionHeader'>" + kTreeNames[aName] + "</h2>\n" +
- "<pre class='tree'>" + aText + "</pre>\n";
+ appendElementWithText(aP, "h2", "sectionHeader", aText);
+ appendTextNode(aP, "\n");
}
function assert(aCond, aMsg)
{
if (!aCond) {
throw("assertion failed: " + aMsg);
}
}
function debug(x)
{
- var content = $("content");
- var div = document.createElement("div");
- div.innerHTML = JSON.stringify(x);
- content.appendChild(div);
+ var content = document.getElementById("content");
+ appendElementWithText(content, "div", "legend", JSON.stringify(x));
}