tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ShortestPaths.js (5272B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  Component,
      9 } = require("resource://devtools/client/shared/vendor/react.mjs");
     10 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     12 const { isSavedFrame } = require("resource://devtools/shared/DevToolsUtils.js");
     13 const {
     14  getSourceNames,
     15 } = require("resource://devtools/client/shared/source-utils.js");
     16 const { L10N } = require("resource://devtools/client/memory/utils.js");
     17 
     18 const GRAPH_DEFAULTS = {
     19  translate: [20, 20],
     20  scale: 1,
     21 };
     22 
     23 const NO_STACK = "noStack";
     24 const NO_FILENAME = "noFilename";
     25 const ROOT_LIST = "JS::ubi::RootList";
     26 
     27 function stringifyLabel(label, id) {
     28  const sanitized = [];
     29 
     30  for (let i = 0, length = label.length; i < length; i++) {
     31    const piece = label[i];
     32 
     33    if (isSavedFrame(piece)) {
     34      const { short } = getSourceNames(piece.source);
     35      sanitized[i] =
     36        `${piece.functionDisplayName} @ ` +
     37        `${short}:${piece.line}:${piece.column}`;
     38    } else if (piece === NO_STACK) {
     39      sanitized[i] = L10N.getStr("tree-item.nostack");
     40    } else if (piece === NO_FILENAME) {
     41      sanitized[i] = L10N.getStr("tree-item.nofilename");
     42    } else if (piece === ROOT_LIST) {
     43      // Don't use the usual labeling machinery for root lists: replace it
     44      // with the "GC Roots" string.
     45      sanitized.splice(0, label.length);
     46      sanitized.push(L10N.getStr("tree-item.rootlist"));
     47      break;
     48    } else {
     49      sanitized[i] = "" + piece;
     50    }
     51  }
     52 
     53  return `${sanitized.join(" › ")} @ 0x${id.toString(16)}`;
     54 }
     55 
     56 class ShortestPaths extends Component {
     57  static get propTypes() {
     58    return {
     59      graph: PropTypes.shape({
     60        nodes: PropTypes.arrayOf(PropTypes.object),
     61        edges: PropTypes.arrayOf(PropTypes.object),
     62      }),
     63    };
     64  }
     65 
     66  constructor(props) {
     67    super(props);
     68    this.state = { zoom: null };
     69    this._renderGraph = this._renderGraph.bind(this);
     70  }
     71 
     72  componentDidMount() {
     73    if (this.props.graph) {
     74      this._renderGraph(this.refs.container, this.props.graph);
     75    }
     76  }
     77 
     78  shouldComponentUpdate(nextProps) {
     79    return this.props.graph != nextProps.graph;
     80  }
     81 
     82  componentDidUpdate() {
     83    if (this.props.graph) {
     84      this._renderGraph(this.refs.container, this.props.graph);
     85    }
     86  }
     87 
     88  componentWillUnmount() {
     89    if (this.state.zoom) {
     90      this.state.zoom.on("zoom", null);
     91    }
     92  }
     93 
     94  _renderGraph(container, { nodes, edges }) {
     95    if (!container.firstChild) {
     96      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
     97      svg.setAttribute("id", "graph-svg");
     98      svg.setAttribute("xlink", "http://www.w3.org/1999/xlink");
     99      svg.style.width = "100%";
    100      svg.style.height = "100%";
    101 
    102      const target = document.createElementNS(
    103        "http://www.w3.org/2000/svg",
    104        "g"
    105      );
    106      target.setAttribute("id", "graph-target");
    107      target.style.width = "100%";
    108      target.style.height = "100%";
    109 
    110      svg.appendChild(target);
    111      container.appendChild(svg);
    112    }
    113 
    114    const graph = new dagreD3.Digraph();
    115 
    116    for (let i = 0; i < nodes.length; i++) {
    117      graph.addNode(nodes[i].id, {
    118        id: nodes[i].id,
    119        label: stringifyLabel(nodes[i].label, nodes[i].id),
    120      });
    121    }
    122 
    123    for (let i = 0; i < edges.length; i++) {
    124      graph.addEdge(null, edges[i].from, edges[i].to, {
    125        label: edges[i].name,
    126      });
    127    }
    128 
    129    const renderer = new dagreD3.Renderer();
    130    renderer.drawNodes();
    131    renderer.drawEdgePaths();
    132 
    133    const svg = d3.select("#graph-svg");
    134    const target = d3.select("#graph-target");
    135 
    136    let zoom = this.state.zoom;
    137    if (!zoom) {
    138      zoom = d3.behavior.zoom().on("zoom", function () {
    139        target.attr(
    140          "transform",
    141          `translate(${d3.event.translate}) scale(${d3.event.scale})`
    142        );
    143      });
    144      svg.call(zoom);
    145      this.setState({ zoom });
    146    }
    147 
    148    const { translate, scale } = GRAPH_DEFAULTS;
    149    zoom.scale(scale);
    150    zoom.translate(translate);
    151    target.attr("transform", `translate(${translate}) scale(${scale})`);
    152 
    153    const layout = dagreD3.layout();
    154    renderer.layout(layout).run(graph, target);
    155  }
    156 
    157  render() {
    158    let contents;
    159    if (this.props.graph) {
    160      // Let the componentDidMount or componentDidUpdate method draw the graph
    161      // with DagreD3. We just provide the container for the graph here.
    162      contents = dom.div({
    163        ref: "container",
    164        style: {
    165          flex: 1,
    166          height: "100%",
    167          width: "100%",
    168        },
    169      });
    170    } else {
    171      contents = dom.div(
    172        {
    173          id: "shortest-paths-select-node-msg",
    174        },
    175        L10N.getStr("shortest-paths.select-node")
    176      );
    177    }
    178 
    179    return dom.div(
    180      {
    181        id: "shortest-paths",
    182        className: "vbox",
    183      },
    184      dom.label(
    185        {
    186          id: "shortest-paths-header",
    187          className: "header",
    188        },
    189        L10N.getStr("shortest-paths.header")
    190      ),
    191      contents
    192    );
    193  }
    194 }
    195 
    196 module.exports = ShortestPaths;