Bug 960776
This commit is contained in:
@@ -65,7 +65,7 @@ const App = createClass({
|
||||
|
||||
HeapView({
|
||||
snapshot: selectedSnapshot,
|
||||
onSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker))
|
||||
onSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
|
||||
}),
|
||||
])
|
||||
])
|
||||
|
||||
@@ -2,11 +2,52 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
|
||||
const Tree = createFactory(require("./tree"));
|
||||
const TreeItem = createFactory(require("./tree-item"));
|
||||
const { getSnapshotStatusText } = require("../utils");
|
||||
const { snapshotState: states } = require("../constants");
|
||||
const { snapshot: snapshotModel } = require("../models");
|
||||
const TAKE_SNAPSHOT_TEXT = "Take snapshot";
|
||||
const TREE_ROW_HEIGHT = 10;
|
||||
|
||||
/**
|
||||
* Creates a hash map mapping node IDs to its parent node.
|
||||
*
|
||||
* @param {CensusTreeNode} node
|
||||
* @param {Object<number, CensusTreeNode>} aggregator
|
||||
*
|
||||
* @return {Object<number, CensusTreeNode>}
|
||||
*/
|
||||
function createParentMap (node, aggregator=Object.create(null)) {
|
||||
for (let child of (node.children || [])) {
|
||||
aggregator[child.id] = node;
|
||||
createParentMap(child, aggregator);
|
||||
}
|
||||
|
||||
return aggregator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates properties to be passed into the Tree component.
|
||||
*
|
||||
* @param {CensusTreeNode} census
|
||||
* @return {Object}
|
||||
*/
|
||||
function createTreeProperties (census) {
|
||||
let map = createParentMap(census);
|
||||
|
||||
return {
|
||||
// getParent only used for focusing parents when child selected;
|
||||
// handle this later?
|
||||
getParent: node => map(node.id),
|
||||
getChildren: node => node.children || [],
|
||||
renderItem: (item, depth, focused, arrow) => new TreeItem({ item, depth, focused, arrow }),
|
||||
getRoots: () => census.children,
|
||||
getKey: node => node.id,
|
||||
itemHeight: TREE_ROW_HEIGHT,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Main view for the memory tool -- contains several panels for different states;
|
||||
@@ -43,7 +84,9 @@ const Heap = module.exports = createClass({
|
||||
getSnapshotStatusText(snapshot));
|
||||
break;
|
||||
case states.SAVED_CENSUS:
|
||||
pane = dom.div({ className: "heap-view-panel", "data-state": "loaded" }, JSON.stringify(census || {}));
|
||||
pane = dom.div({ className: "heap-view-panel", "data-state": "loaded" },
|
||||
Tree(createTreeProperties(snapshot.census))
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,8 @@ DevToolsModules(
|
||||
'list.js',
|
||||
'snapshot-list-item.js',
|
||||
'toolbar.js',
|
||||
'tree-item.js',
|
||||
'tree.js',
|
||||
)
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
|
||||
|
||||
12
devtools/client/memory/components/test/mochitest/chrome.ini
Normal file
12
devtools/client/memory/components/test/mochitest/chrome.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[test_tree_01.html]
|
||||
[test_tree_02.html]
|
||||
[test_tree_03.html]
|
||||
[test_tree_04.html]
|
||||
[test_tree_05.html]
|
||||
[test_tree_06.html]
|
||||
[test_tree_07.html]
|
||||
[test_tree_08.html]
|
||||
205
devtools/client/memory/components/test/mochitest/head.js
Normal file
205
devtools/client/memory/components/test/mochitest/head.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://testing-common/Assert.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
// Disable logging for all the tests. Both the debugger server and frontend will
|
||||
// be affected by this pref.
|
||||
var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", false);
|
||||
|
||||
// Enable the memory tool for all tests.
|
||||
var gMemoryToolEnabled = Services.prefs.getBoolPref("devtools.memory.enabled");
|
||||
Services.prefs.setBoolPref("devtools.memory.enabled", true);
|
||||
|
||||
var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
|
||||
var { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
var { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
var { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
|
||||
var { TargetFactory } = require("devtools/client/framework/target");
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
|
||||
DevToolsUtils.testing = true;
|
||||
var { require: bRequire } = BrowserLoader("resource://devtools/client/memory/", this);
|
||||
|
||||
var EXAMPLE_URL = "http://example.com/browser/browser/devtools/memory/test/";
|
||||
|
||||
// Encoding of the following tree/forest:
|
||||
//
|
||||
// A
|
||||
// |-- B
|
||||
// | |-- E
|
||||
// | | |-- K
|
||||
// | | `-- L
|
||||
// | |-- F
|
||||
// | `-- G
|
||||
// |-- C
|
||||
// | |-- H
|
||||
// | `-- I
|
||||
// `-- D
|
||||
// `-- J
|
||||
// M
|
||||
// `-- N
|
||||
// `-- O
|
||||
var TEST_TREE = {
|
||||
children: {
|
||||
A: ["B", "C", "D"],
|
||||
B: ["E", "F", "G"],
|
||||
C: ["H", "I"],
|
||||
D: ["J"],
|
||||
E: ["K", "L"],
|
||||
F: [],
|
||||
G: [],
|
||||
H: [],
|
||||
I: [],
|
||||
J: [],
|
||||
K: [],
|
||||
L: [],
|
||||
M: ["N"],
|
||||
N: ["O"],
|
||||
O: []
|
||||
},
|
||||
parent: {
|
||||
A: null,
|
||||
B: "A",
|
||||
C: "A",
|
||||
D: "A",
|
||||
E: "B",
|
||||
F: "B",
|
||||
G: "B",
|
||||
H: "C",
|
||||
I: "C",
|
||||
J: "D",
|
||||
K: "E",
|
||||
L: "E",
|
||||
M: null,
|
||||
N: "M",
|
||||
O: "N"
|
||||
}
|
||||
};
|
||||
|
||||
var TEST_TREE_INTERFACE = {
|
||||
getParent: x => TEST_TREE.parent[x],
|
||||
getChildren: x => TEST_TREE.children[x],
|
||||
renderItem: (x, depth, focused, arrow) => "-".repeat(depth) + x + ":" + focused + "\n",
|
||||
getRoots: () => ["A", "M"],
|
||||
getKey: x => "key-" + x,
|
||||
itemHeight: 1
|
||||
};
|
||||
|
||||
// All tests are asynchronous.
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
info("finish() was called, cleaning up...");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
|
||||
Services.prefs.setBoolPref("devtools.memory.enabled", gMemoryToolEnabled);
|
||||
});
|
||||
|
||||
function addTab(url) {
|
||||
info("Adding tab: " + url);
|
||||
|
||||
var deferred = promise.defer();
|
||||
var tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
var linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
linkedBrowser.addEventListener("load", function onLoad() {
|
||||
linkedBrowser.removeEventListener("load", onLoad, true);
|
||||
info("Tab added and finished loading: " + url);
|
||||
deferred.resolve(tab);
|
||||
}, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeTab(tab) {
|
||||
info("Removing tab.");
|
||||
|
||||
var deferred = promise.defer();
|
||||
var tabContainer = gBrowser.tabContainer;
|
||||
|
||||
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
|
||||
tabContainer.removeEventListener("TabClose", onClose, false);
|
||||
info("Tab removed and finished closing.");
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var withTab = Task.async(function* (url, generator) {
|
||||
var tab = yield addTab(url);
|
||||
try {
|
||||
yield* generator(tab);
|
||||
} finally {
|
||||
yield removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
var openMemoryTool = Task.async(function* (tab) {
|
||||
info("Initializing a memory panel.");
|
||||
|
||||
var target = TargetFactory.forTab(tab);
|
||||
var debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
var toolbox = yield gDevTools.showToolbox(target, "memory");
|
||||
var panel = toolbox.getCurrentPanel();
|
||||
return [target, debuggee, panel];
|
||||
});
|
||||
|
||||
var closeMemoryTool = Task.async(function* (panel) {
|
||||
info("Closing a memory panel");
|
||||
yield panel._toolbox.destroy();
|
||||
});
|
||||
|
||||
var withMemoryTool = Task.async(function* (tab, generator) {
|
||||
var [target, debuggee, panel] = yield openMemoryTool(tab);
|
||||
try {
|
||||
yield* generator(target, debuggee, panel);
|
||||
} finally {
|
||||
yield closeMemoryTool(panel);
|
||||
}
|
||||
});
|
||||
|
||||
var withTabAndMemoryTool = Task.async(function* (url, generator) {
|
||||
yield withTab(url, function* (tab) {
|
||||
yield withMemoryTool(tab, function* (target, debuggee, panel) {
|
||||
yield* generator(tab, target, debuggee, panel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function reload(target) {
|
||||
info("Reloading tab.");
|
||||
var deferred = promise.defer();
|
||||
target.once("navigate", deferred.resolve);
|
||||
target.activeTab.reload();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function setState(component, newState) {
|
||||
var deferred = promise.defer();
|
||||
component.setState(newState, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function setProps(component, newState) {
|
||||
var deferred = promise.defer();
|
||||
component.setProps(newState, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function isRenderedTree(actual, expectedDescription, msg) {
|
||||
is(actual, expectedDescription.map(x => x + "\n").join(""), msg);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test trees get displayed with the items in correct order and at the correct
|
||||
depth.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
|
||||
ok(React, "Should get React");
|
||||
ok(Tree, "Should get Tree");
|
||||
|
||||
const t = Tree(TEST_TREE_INTERFACE);
|
||||
ok(t, "Should be able to create Tree instances");
|
||||
|
||||
const tree = React.render(t, window.document.body);
|
||||
ok(tree, "Should be able to mount Tree instances");
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "Should get the items rendered and indented as expected");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that collapsed subtrees aren't rendered.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set("MNO".split(""))
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "Collapsed subtrees shouldn't be rendered");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test Tree's autoExpandDepth.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
let React = bRequire("devtools/client/shared/vendor/react");
|
||||
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setProps(tree, {
|
||||
autoExpandDepth: 1
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"-C:false",
|
||||
"-D:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
], "Tree should be auto expanded one level");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that we only render visible tree items.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
height: 3,
|
||||
scroll: 1
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
], "Tree should show the second, third, and fourth items + buffer of 1 item at the end");
|
||||
|
||||
yield setState(tree, {
|
||||
height: 2,
|
||||
scroll: 3
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
], "Tree should show the third and fourth item + buffer of 1 item at each end");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test focusing with the Tree component.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "G",
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:true",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "G should be focused");
|
||||
|
||||
// Click the first tree node
|
||||
Simulate.click(document.querySelector(".tree-node"));
|
||||
|
||||
// Let the next render happen.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:true",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "A should be focused");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,297 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test keyboard navigation with the Tree component.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
|
||||
// UP ----------------------------------------------------------------------
|
||||
|
||||
info("Up to the previous sibling.");
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "L"
|
||||
});
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:true",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the UP, K should be focused.");
|
||||
|
||||
info("Up to the parent.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:true",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the UP, E should be focused.");
|
||||
|
||||
info("Try and navigate up, past the first item.");
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "A"
|
||||
});
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:true",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the UP, A should be focused and we shouldn't have overflowed past it.");
|
||||
|
||||
// DOWN --------------------------------------------------------------------
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "K"
|
||||
});
|
||||
|
||||
info("Down to next sibling.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:true",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the DOWN, L should be focused.");
|
||||
|
||||
info("Down to parent's next sibling.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:true",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the DOWN, F should be focused.");
|
||||
|
||||
info("Try and go down past the last item.");
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "O"
|
||||
});
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:true",
|
||||
], "After the DOWN, O should still be focused and we shouldn't have overflowed past it.");
|
||||
|
||||
// LEFT --------------------------------------------------------------------
|
||||
|
||||
info("Left to go to parent.");
|
||||
|
||||
yield setState(tree, {
|
||||
focused: "L"
|
||||
})
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:true",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the LEFT, E should be focused.");
|
||||
|
||||
info("Left to collapse children.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:true",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the LEFT, E's children should be collapsed.");
|
||||
|
||||
// RIGHT -------------------------------------------------------------------
|
||||
|
||||
info("Right to expand children.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:true",
|
||||
"---K:false",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the RIGHT, E's children should be expanded again.");
|
||||
|
||||
info("Right to go to next item.");
|
||||
|
||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
|
||||
// Let the component re-render.
|
||||
yield setState(tree, {});
|
||||
|
||||
isRenderedTree(document.body.textContent, [
|
||||
"A:false",
|
||||
"-B:false",
|
||||
"--E:false",
|
||||
"---K:true",
|
||||
"---L:false",
|
||||
"--F:false",
|
||||
"--G:false",
|
||||
"-C:false",
|
||||
"--H:false",
|
||||
"--I:false",
|
||||
"-D:false",
|
||||
"--J:false",
|
||||
"M:false",
|
||||
"-N:false",
|
||||
"--O:false",
|
||||
], "After the RIGHT, K should be focused.");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that arrows get the open attribute when their item's children are expanded.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<link rel="stylesheet" href="chrome://browser/skin/devtools/light-theme.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
yield setProps(tree, {
|
||||
renderItem: (item, depth, focused, arrow) => {
|
||||
return React.DOM.div(
|
||||
{
|
||||
id: item,
|
||||
style: { marginLeft: depth * 16 + "px" }
|
||||
},
|
||||
arrow,
|
||||
item
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
|
||||
let arrows = document.querySelectorAll(".arrow");
|
||||
for (let a of arrows) {
|
||||
ok(a.classList.contains("open"), "Every arrow should be open.");
|
||||
}
|
||||
|
||||
yield setState(tree, {
|
||||
expanded: new Set()
|
||||
});
|
||||
|
||||
arrows = document.querySelectorAll(".arrow");
|
||||
for (let a of arrows) {
|
||||
ok(!a.classList.contains("open"), "Every arrow should be closed.");
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that when an item in the Tree component is clicked, it steals focus from
|
||||
other inputs.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tree component test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<link rel="stylesheet" href="chrome://browser/skin/devtools/light-theme.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = Task.async(function* () {
|
||||
try {
|
||||
const React = bRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = React.addons.TestUtils;
|
||||
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
|
||||
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
|
||||
|
||||
const input = document.createElement("input");
|
||||
document.body.appendChild(input);
|
||||
|
||||
input.focus();
|
||||
is(document.activeElement, input, "The text input should be focused.");
|
||||
|
||||
Simulate.click(document.querySelector(".tree-node"));
|
||||
// Let the tree re-render, because focus is dealt with on componentDidUpdate.
|
||||
yield setState(tree, {});
|
||||
|
||||
isnot(document.activeElement, input,
|
||||
"The input should have had it's focus stolen by clicking on a tree item.");
|
||||
} catch(e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
27
devtools/client/memory/components/tree-item.js
Normal file
27
devtools/client/memory/components/tree-item.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const INDENT = 10;
|
||||
|
||||
/**
|
||||
* An arrow that displays whether its node is expanded (▼) or collapsed
|
||||
* (▶). When its node has no children, it is hidden.
|
||||
*/
|
||||
const TreeItem = module.exports = createClass({
|
||||
displayName: "tree-item",
|
||||
|
||||
render() {
|
||||
let { item, depth, arrow, focused } = this.props;
|
||||
|
||||
return dom.div({ className: "heap-tree-item", style: { marginLeft: depth * INDENT }},
|
||||
arrow,
|
||||
dom.span({ className: "heap-tree-item-name" }, item.name),
|
||||
dom.span({ className: "heap-tree-item-bytes" }, item.bytes),
|
||||
dom.span({ className: "heap-tree-item-count" }, item.count),
|
||||
dom.span({ className: "heap-tree-item-total-bytes" }, item.totalBytes),
|
||||
dom.span({ className: "heap-tree-item-total-count" }, item.totalCount)
|
||||
);
|
||||
}
|
||||
});
|
||||
455
devtools/client/memory/components/tree.js
Normal file
455
devtools/client/memory/components/tree.js
Normal file
@@ -0,0 +1,455 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { ViewHelpers } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
||||
|
||||
const AUTO_EXPAND_DEPTH = 3; // depth
|
||||
|
||||
/**
|
||||
* An arrow that displays whether its node is expanded (▼) or collapsed
|
||||
* (▶). When its node has no children, it is hidden.
|
||||
*/
|
||||
const ArrowExpander = createFactory(createClass({
|
||||
displayName: "ArrowExpander",
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return this.props.item !== nextProps.item
|
||||
|| this.props.visible != nextProps.visible
|
||||
|| this.props.expanded !== nextProps.expanded;
|
||||
},
|
||||
|
||||
render() {
|
||||
const attrs = {
|
||||
className: "arrow theme-twisty",
|
||||
onClick: this.props.expanded
|
||||
? () => this.props.onCollapse(this.props.item)
|
||||
: e => this.props.onExpand(this.props.item, e.altKey)
|
||||
};
|
||||
|
||||
if (this.props.expanded) {
|
||||
attrs.className += " open";
|
||||
}
|
||||
|
||||
if (!this.props.visible) {
|
||||
attrs.style = {
|
||||
visibility: "hidden"
|
||||
};
|
||||
}
|
||||
|
||||
return dom.div(attrs);
|
||||
}
|
||||
}));
|
||||
|
||||
const TreeNode = createFactory(createClass({
|
||||
componentDidUpdate() {
|
||||
if (this.props.focused) {
|
||||
this.refs.button.getDOMNode().focus();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const arrow = ArrowExpander({
|
||||
item: this.props.item,
|
||||
expanded: this.props.expanded,
|
||||
visible: this.props.hasChildren,
|
||||
onExpand: this.props.onExpand,
|
||||
onCollapse: this.props.onCollapse
|
||||
});
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "tree-node div",
|
||||
onFocus: this.props.onFocus,
|
||||
onClick: this.props.onFocus,
|
||||
onBlur: this.props.onBlur,
|
||||
style: {
|
||||
padding: 0,
|
||||
margin: 0
|
||||
}
|
||||
},
|
||||
|
||||
this.props.renderItem(this.props.item,
|
||||
this.props.depth,
|
||||
this.props.focused,
|
||||
arrow),
|
||||
|
||||
// XXX: OSX won't focus/blur regular elements even if you set tabindex
|
||||
// unless there is an input/button child.
|
||||
dom.button(this._buttonAttrs)
|
||||
);
|
||||
},
|
||||
|
||||
_buttonAttrs: {
|
||||
ref: "button",
|
||||
style: {
|
||||
opacity: 0,
|
||||
width: "0 !important",
|
||||
height: "0 !important",
|
||||
padding: "0 !important",
|
||||
outline: "none",
|
||||
MozAppearance: "none",
|
||||
// XXX: Despite resetting all of the above properties (and margin), the
|
||||
// button still ends up with ~79px width, so we set a large negative
|
||||
// margin to completely hide it.
|
||||
MozMarginStart: "-1000px !important",
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
/**
|
||||
* A generic tree component. See propTypes for the public API.
|
||||
*
|
||||
* @see `devtools/client/memory/components/test/mochitest/head.js` for usage
|
||||
* @see `devtools/client/memory/components/heap.js` for usage
|
||||
*/
|
||||
const Tree = module.exports = createClass({
|
||||
displayName: "Tree",
|
||||
|
||||
propTypes: {
|
||||
// Required props
|
||||
|
||||
// A function to get an item's parent, or null if it is a root.
|
||||
getParent: PropTypes.func.isRequired,
|
||||
// A function to get an item's children.
|
||||
getChildren: PropTypes.func.isRequired,
|
||||
// A function which takes an item and ArrowExpander and returns a
|
||||
// component.
|
||||
renderItem: PropTypes.func.isRequired,
|
||||
// A function which returns the roots of the tree (forest).
|
||||
getRoots: PropTypes.func.isRequired,
|
||||
// A function to get a unique key for the given item.
|
||||
getKey: PropTypes.func.isRequired,
|
||||
// The height of an item in the tree including margin and padding, in
|
||||
// pixels.
|
||||
itemHeight: PropTypes.number.isRequired,
|
||||
|
||||
// Optional props
|
||||
|
||||
// A predicate function to filter out unwanted items from the tree.
|
||||
filter: PropTypes.func,
|
||||
// The depth to which we should automatically expand new items.
|
||||
autoExpandDepth: PropTypes.number
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
filter: item => true,
|
||||
expanded: new Set(),
|
||||
seen: new Set(),
|
||||
focused: undefined,
|
||||
autoExpandDepth: AUTO_EXPAND_DEPTH
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
scroll: 0,
|
||||
height: window.innerHeight,
|
||||
expanded: new Set(),
|
||||
seen: new Set(),
|
||||
focused: undefined
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("resize", this._updateHeight);
|
||||
this._updateHeight();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("resize", this._updateHeight);
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// Automatically expand the first autoExpandDepth levels for new items.
|
||||
for (let { item } of this._dfsFromRoots(this.props.autoExpandDepth)) {
|
||||
if (!this.state.seen.has(item)) {
|
||||
this.state.expanded.add(item);
|
||||
this.state.seen.add(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const traversal = this._dfsFromRoots();
|
||||
|
||||
// Remove 1 from `begin` and add 2 to `end` so that the top and bottom of
|
||||
// the page are filled with the previous and next items respectively,
|
||||
// rather than whitespace if the item is not in full view.
|
||||
const begin = Math.max(((this.state.scroll / this.props.itemHeight) | 0) - 1, 0);
|
||||
const end = begin + 2 + ((this.state.height / this.props.itemHeight) | 0);
|
||||
const toRender = traversal.slice(begin, end);
|
||||
|
||||
const nodes = [
|
||||
dom.div({
|
||||
key: "top-spacer",
|
||||
style: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: begin * this.props.itemHeight + "px"
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
for (let i = 0; i < toRender.length; i++) {
|
||||
let { item, depth } = toRender[i];
|
||||
nodes.push(TreeNode({
|
||||
key: this.props.getKey(item),
|
||||
item: item,
|
||||
depth: depth,
|
||||
renderItem: this.props.renderItem,
|
||||
focused: this.state.focused === item,
|
||||
expanded: this.state.expanded.has(item),
|
||||
hasChildren: !!this.props.getChildren(item).length,
|
||||
onExpand: this._onExpand,
|
||||
onCollapse: this._onCollapse,
|
||||
onFocus: () => this._onFocus(item)
|
||||
}));
|
||||
}
|
||||
|
||||
nodes.push(dom.div({
|
||||
key: "bottom-spacer",
|
||||
style: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
height: (traversal.length - 1 - end) * this.props.itemHeight + "px"
|
||||
}
|
||||
}));
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "tree",
|
||||
ref: "tree",
|
||||
onKeyDown: this._onKeyDown,
|
||||
onScroll: this._onScroll,
|
||||
style: {
|
||||
padding: 0,
|
||||
margin: 0
|
||||
}
|
||||
},
|
||||
nodes
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the state's height based on clientHeight.
|
||||
*/
|
||||
_updateHeight() {
|
||||
this.setState({
|
||||
height: this.refs.tree.getDOMNode().clientHeight
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform a pre-order depth-first search from item.
|
||||
*/
|
||||
_dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
|
||||
if (!this.props.filter(item)) {
|
||||
return traversal;
|
||||
}
|
||||
|
||||
traversal.push({ item, depth: _depth });
|
||||
|
||||
if (!this.state.expanded.has(item)) {
|
||||
return traversal;
|
||||
}
|
||||
|
||||
const nextDepth = _depth + 1;
|
||||
|
||||
if (nextDepth > maxDepth) {
|
||||
return traversal;
|
||||
}
|
||||
|
||||
for (let child of this.props.getChildren(item)) {
|
||||
this._dfs(child, maxDepth, traversal, nextDepth);
|
||||
}
|
||||
|
||||
return traversal;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform a pre-order depth-first search over the whole forest.
|
||||
*/
|
||||
_dfsFromRoots(maxDepth = Infinity) {
|
||||
const traversal = [];
|
||||
for (let root of this.props.getRoots()) {
|
||||
this._dfs(root, maxDepth, traversal);
|
||||
}
|
||||
return traversal;
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands current row.
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Boolean} expandAllChildren
|
||||
*/
|
||||
_onExpand(item, expandAllChildren) {
|
||||
this.state.expanded.add(item);
|
||||
|
||||
if (expandAllChildren) {
|
||||
for (let { item: child } of this._dfs(item)) {
|
||||
this.state.expanded.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
expanded: this.state.expanded
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Collapses current row.
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
_onCollapse(item) {
|
||||
this.state.expanded.delete(item);
|
||||
this.setState({
|
||||
expanded: this.state.expanded
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the passed in item to be the focused item.
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
_onFocus(item) {
|
||||
this.setState({
|
||||
focused: item
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the state to have no focused item.
|
||||
*/
|
||||
_onBlur() {
|
||||
this.setState({
|
||||
focused: undefined
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired on a scroll within the tree's container, updates
|
||||
* the stored position of the view port to handle virtual view rendering.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
_onScroll(e) {
|
||||
this.setState({
|
||||
scroll: Math.max(this.refs.tree.getDOMNode().scrollTop, 0),
|
||||
height: this.refs.tree.getDOMNode().clientHeight
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles key down events in the tree's container.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
_onKeyDown(e) {
|
||||
if (this.state.focused == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent scrolling when pressing navigation keys. Guard against mocked
|
||||
// events received when testing.
|
||||
if (e.nativeEvent && e.nativeEvent.preventDefault) {
|
||||
ViewHelpers.preventScrolling(e.nativeEvent);
|
||||
}
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
this._focusPrevNode();
|
||||
return false;
|
||||
|
||||
case "ArrowDown":
|
||||
this._focusNextNode();
|
||||
return false;
|
||||
|
||||
case "ArrowLeft":
|
||||
if (this.state.expanded.has(this.state.focused)
|
||||
&& this.props.getChildren(this.state.focused).length) {
|
||||
this._onCollapse(this.state.focused);
|
||||
} else {
|
||||
this._focusParentNode();
|
||||
}
|
||||
return false;
|
||||
|
||||
case "ArrowRight":
|
||||
if (!this.state.expanded.has(this.state.focused)) {
|
||||
this._onExpand(this.state.focused);
|
||||
} else {
|
||||
this._focusNextNode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the previous node relative to the currently focused item, to focused.
|
||||
*/
|
||||
_focusPrevNode() {
|
||||
// Start a depth first search and keep going until we reach the currently
|
||||
// focused node. Focus the previous node in the DFS, if it exists. If it
|
||||
// doesn't exist, we're at the first node already.
|
||||
|
||||
let prev;
|
||||
for (let { item } of this._dfsFromRoots()) {
|
||||
if (item === this.state.focused) {
|
||||
break;
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
|
||||
if (prev === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
focused: prev
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the down arrow key which will focus either the next child
|
||||
* or sibling row.
|
||||
*/
|
||||
_focusNextNode() {
|
||||
// Start a depth first search and keep going until we reach the currently
|
||||
// focused node. Focus the next node in the DFS, if it exists. If it
|
||||
// doesn't exist, we're at the last node already.
|
||||
|
||||
const traversal = this._dfsFromRoots();
|
||||
|
||||
let i = 0;
|
||||
for (let { item } of traversal) {
|
||||
if (item === this.state.focused) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i + 1 < traversal.length) {
|
||||
this.setState({
|
||||
focused: traversal[i + 1].item
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the left arrow key, going back up to the current rows'
|
||||
* parent row.
|
||||
*/
|
||||
_focusParentNode() {
|
||||
const parent = this.props.getParent(this.state.focused);
|
||||
if (parent) {
|
||||
this.setState({
|
||||
focused: parent
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -39,13 +39,13 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
|
||||
let shouldHavePath = [states.SAVED, states.READ, states.SAVING_CENSUS, states.SAVED_CENSUS];
|
||||
let shouldHaveCensus = [states.SAVED_CENSUS];
|
||||
|
||||
if (!stateNames.contains(current)) {
|
||||
if (!stateNames.includes(current)) {
|
||||
throw new Error(`Snapshot state must be one of ${stateNames}.`);
|
||||
}
|
||||
if (shouldHavePath.contains(current) && !path) {
|
||||
if (shouldHavePath.includes(current) && !path) {
|
||||
throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
|
||||
}
|
||||
if (shouldHaveCensus.contains(current) && (!props.census || !props.breakdown)) {
|
||||
if (shouldHaveCensus.includes(current) && (!props.census || !props.breakdown)) {
|
||||
throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@ function BrowserLoader(baseURI, window) {
|
||||
paths: Object.assign({}, loaderOptions.paths),
|
||||
invisibleToDebugger: loaderOptions.invisibleToDebugger,
|
||||
require: (id, require) => {
|
||||
const uri = require.resolve(id);
|
||||
let uri = require.resolve(id);
|
||||
|
||||
if (!uri.startsWith(baseURI) &&
|
||||
!uri.startsWith(VENDOR_CONTENT_URL)) {
|
||||
|
||||
@@ -279,7 +279,7 @@ div.CodeMirror span.eval-text {
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
.theme-twisty[open] {
|
||||
.theme-twisty[open], .theme-twisty.open {
|
||||
background-position: -42px -14px;
|
||||
}
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ div.CodeMirror span.eval-text {
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
.theme-twisty[open] {
|
||||
.theme-twisty[open], .theme-twisty.open {
|
||||
background-position: -14px -14px;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
const { Visitor, walk } = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
|
||||
|
||||
// Monotonically increasing integer for CensusTreeNode `id`s.
|
||||
let INC = 0;
|
||||
|
||||
/**
|
||||
* Return true if the given object is a SavedFrame stack object, false otherwise.
|
||||
*
|
||||
@@ -138,7 +141,8 @@ CensusTreeNodeCache.lookupNode = function (cache, node) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Add `child` to `parent`'s set of children.
|
||||
* Add `child` to `parent`'s set of children and store the parent ID
|
||||
* on the child.
|
||||
*
|
||||
* @param {CensusTreeNode} parent
|
||||
* @param {CensusTreeNode} child
|
||||
@@ -147,6 +151,7 @@ function addChild(parent, child) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
child.parent = parent.id;
|
||||
parent.children.push(child);
|
||||
}
|
||||
|
||||
@@ -206,9 +211,6 @@ function makeCensusTreeNodeSubTree(breakdown, report, edge, cache, outParams) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through each frame in the stack and get or create a CensusTreeNode for
|
||||
// the frame.
|
||||
|
||||
const frames = getArrayOfFrames(edge);
|
||||
let currentCache = cache;
|
||||
let prevNode;
|
||||
@@ -389,6 +391,8 @@ function CensusTreeNode (name) {
|
||||
this.count = 0;
|
||||
this.totalCount = 0;
|
||||
this.children = undefined;
|
||||
this.id = ++INC;
|
||||
this.parent = undefined;
|
||||
}
|
||||
|
||||
CensusTreeNode.prototype = null;
|
||||
@@ -523,6 +527,8 @@ function invert(tree) {
|
||||
* name: <?String>
|
||||
* count: <?Number>
|
||||
* bytes: <?Number>
|
||||
* id: <?Number>
|
||||
* parent: <?Number>
|
||||
* }
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
|
||||
@@ -38,7 +38,9 @@ const EXPECTED = {
|
||||
totalBytes: 500,
|
||||
count: 50,
|
||||
totalCount: 50,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 3,
|
||||
parent: 1
|
||||
},
|
||||
{
|
||||
name: "JSObject",
|
||||
@@ -46,7 +48,9 @@ const EXPECTED = {
|
||||
totalBytes: 100,
|
||||
count: 10,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 2,
|
||||
parent: 1
|
||||
},
|
||||
{
|
||||
name: "JSString",
|
||||
@@ -54,9 +58,13 @@ const EXPECTED = {
|
||||
totalBytes: 0,
|
||||
count: 0,
|
||||
totalCount: 0,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 4,
|
||||
parent: 1
|
||||
},
|
||||
],
|
||||
id: 1,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
|
||||
@@ -48,7 +48,9 @@ const EXPECTED = {
|
||||
totalBytes: 40,
|
||||
count: 4,
|
||||
totalCount: 4,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 9,
|
||||
parent: 7,
|
||||
},
|
||||
{
|
||||
name: "js::Shape",
|
||||
@@ -56,9 +58,13 @@ const EXPECTED = {
|
||||
totalBytes: 30,
|
||||
count: 3,
|
||||
totalCount: 3,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 8,
|
||||
parent: 7,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 7,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "objects",
|
||||
@@ -73,7 +79,9 @@ const EXPECTED = {
|
||||
totalBytes: 20,
|
||||
count: 2,
|
||||
totalCount: 2,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 4,
|
||||
parent: 2,
|
||||
},
|
||||
{
|
||||
name: "Function",
|
||||
@@ -81,9 +89,13 @@ const EXPECTED = {
|
||||
totalBytes: 10,
|
||||
count: 1,
|
||||
totalCount: 1,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 3,
|
||||
parent: 2,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 2,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "strings",
|
||||
@@ -91,7 +103,9 @@ const EXPECTED = {
|
||||
totalCount: 1,
|
||||
bytes: 10,
|
||||
totalBytes: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 6,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "scripts",
|
||||
@@ -99,9 +113,13 @@ const EXPECTED = {
|
||||
totalCount: 1,
|
||||
bytes: 1,
|
||||
totalBytes: 1,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 5,
|
||||
parent: 1,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 1,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
|
||||
@@ -35,7 +35,9 @@ const EXPECTED = {
|
||||
totalBytes: 100,
|
||||
count: 1,
|
||||
totalCount: 1,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 3,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "other",
|
||||
@@ -50,7 +52,9 @@ const EXPECTED = {
|
||||
totalBytes: 40,
|
||||
count: 4,
|
||||
totalCount: 4,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 6,
|
||||
parent: 4,
|
||||
},
|
||||
{
|
||||
name: "JIT::CODE::NOW!!!",
|
||||
@@ -58,9 +62,13 @@ const EXPECTED = {
|
||||
totalBytes: 20,
|
||||
count: 2,
|
||||
totalCount: 2,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 5,
|
||||
parent: 4,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 4,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "Function",
|
||||
@@ -68,9 +76,13 @@ const EXPECTED = {
|
||||
totalBytes: 10,
|
||||
count: 10,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 2,
|
||||
parent: 1,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 1,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
|
||||
@@ -67,7 +67,9 @@ function run_test() {
|
||||
totalBytes: 30,
|
||||
count: 3,
|
||||
totalCount: 3,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 7,
|
||||
parent: 5,
|
||||
},
|
||||
{
|
||||
name: stack2,
|
||||
@@ -75,9 +77,13 @@ function run_test() {
|
||||
totalBytes: 20,
|
||||
count: 2,
|
||||
totalCount: 2,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 6,
|
||||
parent: 5,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 5,
|
||||
parent: 2,
|
||||
},
|
||||
{
|
||||
name: stack4,
|
||||
@@ -85,7 +91,9 @@ function run_test() {
|
||||
totalBytes: 40,
|
||||
count: 4,
|
||||
totalCount: 4,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 8,
|
||||
parent: 2,
|
||||
},
|
||||
{
|
||||
name: stack1.parent,
|
||||
@@ -100,11 +108,17 @@ function run_test() {
|
||||
totalBytes: 10,
|
||||
count: 1,
|
||||
totalCount: 1,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 4,
|
||||
parent: 3,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 3,
|
||||
parent: 2,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 2,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "noStack",
|
||||
@@ -112,7 +126,9 @@ function run_test() {
|
||||
totalBytes: 60,
|
||||
count: 6,
|
||||
totalCount: 6,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 10,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: stack5,
|
||||
@@ -120,9 +136,13 @@ function run_test() {
|
||||
totalBytes: 50,
|
||||
count: 5,
|
||||
totalCount: 5,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 9,
|
||||
parent: 1,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 1,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
|
||||
|
||||
@@ -71,7 +71,9 @@ function run_test() {
|
||||
totalBytes: 40,
|
||||
count: 4,
|
||||
totalCount: 4,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 8,
|
||||
parent: 4,
|
||||
},
|
||||
{
|
||||
name: "Baz",
|
||||
@@ -79,7 +81,9 @@ function run_test() {
|
||||
totalBytes: 30,
|
||||
count: 3,
|
||||
totalCount: 3,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 7,
|
||||
parent: 4,
|
||||
},
|
||||
{
|
||||
name: "Bar",
|
||||
@@ -87,7 +91,9 @@ function run_test() {
|
||||
totalBytes: 20,
|
||||
count: 2,
|
||||
totalCount: 2,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 6,
|
||||
parent: 4,
|
||||
},
|
||||
{
|
||||
name: "Foo",
|
||||
@@ -95,13 +101,21 @@ function run_test() {
|
||||
totalBytes: 10,
|
||||
count: 1,
|
||||
totalCount: 1,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 5,
|
||||
parent: 4,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 4,
|
||||
parent: 3,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 3,
|
||||
parent: 2,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 2,
|
||||
parent: 1,
|
||||
},
|
||||
{
|
||||
name: "noStack",
|
||||
@@ -109,9 +123,13 @@ function run_test() {
|
||||
totalBytes: 50,
|
||||
count: 5,
|
||||
totalCount: 5,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 9,
|
||||
parent: 1,
|
||||
},
|
||||
]
|
||||
],
|
||||
id: 1,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
|
||||
|
||||
@@ -65,9 +65,13 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 16,
|
||||
parent: 15,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 15,
|
||||
parent: 14,
|
||||
},
|
||||
{
|
||||
name: abc_Stack,
|
||||
@@ -82,7 +86,9 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 18,
|
||||
parent: 17,
|
||||
},
|
||||
{
|
||||
name: abc_Stack.parent,
|
||||
@@ -97,7 +103,9 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 22,
|
||||
parent: 19,
|
||||
},
|
||||
{
|
||||
name: abc_Stack.parent.parent,
|
||||
@@ -112,9 +120,13 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 21,
|
||||
parent: 20,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 20,
|
||||
parent: 19,
|
||||
},
|
||||
{
|
||||
name: dbc_Stack.parent.parent,
|
||||
@@ -129,11 +141,17 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 24,
|
||||
parent: 23,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 23,
|
||||
parent: 19,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 19,
|
||||
parent: 17,
|
||||
},
|
||||
{
|
||||
name: ec_Stack.parent,
|
||||
@@ -148,13 +166,21 @@ function run_test() {
|
||||
totalBytes: 100,
|
||||
count: 0,
|
||||
totalCount: 10,
|
||||
children: undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
children: undefined,
|
||||
id: 26,
|
||||
parent: 25,
|
||||
},
|
||||
],
|
||||
id: 25,
|
||||
parent: 17,
|
||||
},
|
||||
],
|
||||
id: 17,
|
||||
parent: 14,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 14,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { invert: true });
|
||||
|
||||
@@ -70,11 +70,17 @@ function run_test() {
|
||||
totalBytes: 220,
|
||||
count: 0,
|
||||
totalCount: 22,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 14,
|
||||
parent: 13,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 13,
|
||||
parent: 12,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 12,
|
||||
parent: 11,
|
||||
},
|
||||
{
|
||||
name: "JSAtom",
|
||||
@@ -96,11 +102,17 @@ function run_test() {
|
||||
totalBytes: 220,
|
||||
count: 0,
|
||||
totalCount: 22,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 17,
|
||||
parent: 16,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 16,
|
||||
parent: 15,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 15,
|
||||
parent: 11,
|
||||
},
|
||||
{
|
||||
name: "Array",
|
||||
@@ -122,11 +134,17 @@ function run_test() {
|
||||
totalBytes: 220,
|
||||
count: 0,
|
||||
totalCount: 22,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 20,
|
||||
parent: 19,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 19,
|
||||
parent: 18,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 18,
|
||||
parent: 11,
|
||||
},
|
||||
{
|
||||
name: "js::jit::JitScript",
|
||||
@@ -148,11 +166,17 @@ function run_test() {
|
||||
totalBytes: 220,
|
||||
count: 0,
|
||||
totalCount: 22,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 26,
|
||||
parent: 25,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 25,
|
||||
parent: 24,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 24,
|
||||
parent: 11,
|
||||
},
|
||||
{
|
||||
name: "other",
|
||||
@@ -174,13 +198,21 @@ function run_test() {
|
||||
totalBytes: 220,
|
||||
count: 0,
|
||||
totalCount: 22,
|
||||
children: undefined
|
||||
children: undefined,
|
||||
id: 23,
|
||||
parent: 22,
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 22,
|
||||
parent: 21,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
id: 21,
|
||||
parent: 11,
|
||||
},
|
||||
],
|
||||
id: 11,
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { invert: true });
|
||||
|
||||
Reference in New Issue
Block a user