tor-browser

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

JsonPanel.mjs (5197B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import {
      6  createFactory,
      7  Component,
      8 } from "resource://devtools/client/shared/vendor/react.mjs";
      9 import * as dom from "resource://devtools/client/shared/vendor/react-dom-factories.mjs";
     10 import PropTypes from "resource://devtools/client/shared/vendor/react-prop-types.mjs";
     11 import { createFactories } from "resource://devtools/client/shared/react-utils.mjs";
     12 
     13 import TreeViewClass from "resource://devtools/client/shared/components/tree/TreeView.mjs";
     14 import { BucketProperty } from "resource://devtools/client/shared/components/tree/ObjectProvider.mjs";
     15 import JsonToolbarClass from "resource://devtools/client/jsonview/components/JsonToolbar.mjs";
     16 
     17 import {
     18  JSON_NUMBER,
     19  MODE,
     20 } from "resource://devtools/client/shared/components/reps/reps/constants.mjs";
     21 import { Rep } from "resource://devtools/client/shared/components/reps/reps/rep.mjs";
     22 
     23 const TreeView = createFactory(TreeViewClass);
     24 const { JsonToolbar } = createFactories(JsonToolbarClass);
     25 const { div } = dom;
     26 
     27 const MAX_STRING_LENGTH = 250;
     28 
     29 function isObject(value) {
     30  return Object(value) === value;
     31 }
     32 
     33 /**
     34 * This template represents the 'JSON' panel. The panel is
     35 * responsible for rendering an expandable tree that allows simple
     36 * inspection of JSON structure.
     37 */
     38 class JsonPanel extends Component {
     39  static get propTypes() {
     40    return {
     41      data: PropTypes.oneOfType([
     42        PropTypes.string,
     43        PropTypes.array,
     44        PropTypes.object,
     45        PropTypes.bool,
     46        PropTypes.number,
     47      ]),
     48      dataSize: PropTypes.number,
     49      expandedNodes: PropTypes.instanceOf(Set),
     50      searchFilter: PropTypes.string,
     51      actions: PropTypes.object,
     52    };
     53  }
     54 
     55  constructor(props) {
     56    super(props);
     57    this.state = {};
     58    this.onKeyPress = this.onKeyPress.bind(this);
     59    this.onFilter = this.onFilter.bind(this);
     60    this.renderValue = this.renderValue.bind(this);
     61    this.renderTree = this.renderTree.bind(this);
     62  }
     63 
     64  componentDidMount() {
     65    document.addEventListener("keypress", this.onKeyPress, true);
     66    document.getElementById("json-scrolling-panel").focus();
     67  }
     68 
     69  componentWillUnmount() {
     70    document.removeEventListener("keypress", this.onKeyPress, true);
     71  }
     72 
     73  onKeyPress() {
     74    // XXX shortcut for focusing the Filter field (see Bug 1178771).
     75  }
     76 
     77  onFilter(object) {
     78    if (!this.props.searchFilter) {
     79      return true;
     80    }
     81 
     82    const searchFilter = this.props.searchFilter.toLowerCase();
     83 
     84    // For bucket nodes, check if any of their children match
     85    if (object instanceof BucketProperty) {
     86      const { object: array, startIndex, endIndex } = object;
     87      for (let i = startIndex; i <= endIndex; i++) {
     88        const childJson = JSON.stringify(array[i]);
     89        if (childJson.toLowerCase().includes(searchFilter)) {
     90          return true;
     91        }
     92      }
     93      return false;
     94    }
     95 
     96    const json = object.name + JSON.stringify(object.value);
     97    return json.toLowerCase().includes(searchFilter);
     98  }
     99 
    100  renderValue(props) {
    101    const member = props.member;
    102 
    103    // Hide value for bucket nodes (they show ranges like [0…99])
    104    if (member.type === "bucket") {
    105      return null;
    106    }
    107 
    108    // Hide object summary when non-empty object is expanded (bug 1244912).
    109    if (isObject(member.value) && member.hasChildren && member.open) {
    110      return null;
    111    }
    112 
    113    // Render the value (summary) using Reps library.
    114    return Rep(
    115      Object.assign({}, props, {
    116        cropLimit: MAX_STRING_LENGTH,
    117        noGrip: true,
    118        isInContentPage: true,
    119      })
    120    );
    121  }
    122 
    123  renderTree() {
    124    // Append custom column for displaying values. This column
    125    // Take all available horizontal space.
    126    const columns = [
    127      {
    128        id: "value",
    129        width: "100%",
    130      },
    131    ];
    132 
    133    // Render tree component.
    134    return TreeView({
    135      object: this.props.data,
    136      mode: MODE.LONG,
    137      bucketLargeArrays: true,
    138      onFilter: this.onFilter,
    139      columns,
    140      renderValue: this.renderValue,
    141      expandedNodes: this.props.expandedNodes,
    142      maxStringLength: MAX_STRING_LENGTH,
    143    });
    144  }
    145 
    146  render() {
    147    let content;
    148    const data = this.props.data;
    149 
    150    if (!isObject(data)) {
    151      content = div(
    152        { className: "jsonPrimitiveValue" },
    153        Rep({
    154          object: data,
    155        })
    156      );
    157    } else if (data instanceof Error) {
    158      content = div({ className: "jsonParseError" }, data + "");
    159    } else if (data.type === JSON_NUMBER) {
    160      content = div(
    161        { className: "jsonPrimitiveValue" },
    162        Rep({
    163          object: data,
    164          noGrip: true,
    165        })
    166      );
    167    } else {
    168      content = this.renderTree();
    169    }
    170 
    171    return div(
    172      { className: "jsonPanelBox tab-panel-inner" },
    173      JsonToolbar({
    174        actions: this.props.actions,
    175        dataSize: this.props.dataSize,
    176      }),
    177      div(
    178        {
    179          className: "panelContent",
    180          id: "json-scrolling-panel",
    181          tabIndex: 0,
    182        },
    183        content
    184      )
    185    );
    186  }
    187 }
    188 
    189 export default { JsonPanel };