Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
"use strict";
const {
Component,
const { isSavedFrame } = require("resource://devtools/shared/DevToolsUtils.js");
const {
getSourceNames,
const { L10N } = require("resource://devtools/client/memory/utils.js");
const GRAPH_DEFAULTS = {
translate: [20, 20],
scale: 1,
};
const NO_STACK = "noStack";
const NO_FILENAME = "noFilename";
const ROOT_LIST = "JS::ubi::RootList";
function stringifyLabel(label, id) {
const sanitized = [];
for (let i = 0, length = label.length; i < length; i++) {
const piece = label[i];
if (isSavedFrame(piece)) {
const { short } = getSourceNames(piece.source);
sanitized[i] =
`${piece.functionDisplayName} @ ` +
`${short}:${piece.line}:${piece.column}`;
} else if (piece === NO_STACK) {
sanitized[i] = L10N.getStr("tree-item.nostack");
} else if (piece === NO_FILENAME) {
sanitized[i] = L10N.getStr("tree-item.nofilename");
} else if (piece === ROOT_LIST) {
// Don't use the usual labeling machinery for root lists: replace it
// with the "GC Roots" string.
sanitized.splice(0, label.length);
sanitized.push(L10N.getStr("tree-item.rootlist"));
break;
} else {
sanitized[i] = "" + piece;
}
}
return `${sanitized.join(" › ")} @ 0x${id.toString(16)}`;
}
class ShortestPaths extends Component {
static get propTypes() {
return {
graph: PropTypes.shape({
nodes: PropTypes.arrayOf(PropTypes.object),
edges: PropTypes.arrayOf(PropTypes.object),
}),
};
}
constructor(props) {
super(props);
this.state = { zoom: null };
this._renderGraph = this._renderGraph.bind(this);
}
componentDidMount() {
if (this.props.graph) {
this._renderGraph(this.refs.container, this.props.graph);
}
}
shouldComponentUpdate(nextProps) {
return this.props.graph != nextProps.graph;
}
componentDidUpdate() {
if (this.props.graph) {
this._renderGraph(this.refs.container, this.props.graph);
}
}
componentWillUnmount() {
if (this.state.zoom) {
this.state.zoom.on("zoom", null);
}
}
_renderGraph(container, { nodes, edges }) {
if (!container.firstChild) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("id", "graph-svg");
svg.setAttribute("xlink", "http://www.w3.org/1999/xlink");
svg.style.width = "100%";
svg.style.height = "100%";
const target = document.createElementNS(
"g"
);
target.setAttribute("id", "graph-target");
target.style.width = "100%";
target.style.height = "100%";
svg.appendChild(target);
container.appendChild(svg);
}
const graph = new dagreD3.Digraph();
for (let i = 0; i < nodes.length; i++) {
graph.addNode(nodes[i].id, {
id: nodes[i].id,
label: stringifyLabel(nodes[i].label, nodes[i].id),
});
}
for (let i = 0; i < edges.length; i++) {
graph.addEdge(null, edges[i].from, edges[i].to, {
label: edges[i].name,
});
}
const renderer = new dagreD3.Renderer();
renderer.drawNodes();
renderer.drawEdgePaths();
const svg = d3.select("#graph-svg");
const target = d3.select("#graph-target");
let zoom = this.state.zoom;
if (!zoom) {
zoom = d3.behavior.zoom().on("zoom", function () {
target.attr(
"transform",
`translate(${d3.event.translate}) scale(${d3.event.scale})`
);
});
svg.call(zoom);
this.setState({ zoom });
}
const { translate, scale } = GRAPH_DEFAULTS;
zoom.scale(scale);
zoom.translate(translate);
target.attr("transform", `translate(${translate}) scale(${scale})`);
const layout = dagreD3.layout();
renderer.layout(layout).run(graph, target);
}
render() {
let contents;
if (this.props.graph) {
// Let the componentDidMount or componentDidUpdate method draw the graph
// with DagreD3. We just provide the container for the graph here.
contents = dom.div({
ref: "container",
style: {
flex: 1,
height: "100%",
width: "100%",
},
});
} else {
contents = dom.div(
{
id: "shortest-paths-select-node-msg",
},
L10N.getStr("shortest-paths.select-node")
);
}
return dom.div(
{
id: "shortest-paths",
className: "vbox",
},
dom.label(
{
id: "shortest-paths-header",
className: "header",
},
L10N.getStr("shortest-paths.header")
),
contents
);
}
}
module.exports = ShortestPaths;