tor-browser

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

function.mjs (6027B)


      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 /* eslint no-shadow: ["error", { "allow": ["name"] }] */
      6 
      7 import PropTypes from "resource://devtools/client/shared/vendor/react-prop-types.mjs";
      8 import {
      9  button,
     10  span,
     11 } from "resource://devtools/client/shared/vendor/react-dom-factories.mjs";
     12 
     13 import { getGripType, cropString, wrapRender } from "./rep-utils.mjs";
     14 import { MODE } from "./constants.mjs";
     15 
     16 const IGNORED_SOURCE_URLS = ["debugger eval code"];
     17 
     18 /**
     19 * This component represents a template for Function objects.
     20 */
     21 
     22 FunctionRep.propTypes = {
     23  object: PropTypes.object.isRequired,
     24  onViewSourceInDebugger: PropTypes.func,
     25  shouldRenderTooltip: PropTypes.bool,
     26 };
     27 
     28 function FunctionRep(props) {
     29  const {
     30    object: grip,
     31    onViewSourceInDebugger,
     32    recordTelemetryEvent,
     33    shouldRenderTooltip,
     34  } = props;
     35 
     36  let jumpToDefinitionButton;
     37 
     38  // Test to see if we should display the link back to the original function definition
     39  if (
     40    onViewSourceInDebugger &&
     41    grip.location &&
     42    grip.location.url &&
     43    !IGNORED_SOURCE_URLS.includes(grip.location.url)
     44  ) {
     45    jumpToDefinitionButton = button({
     46      className: "jump-definition",
     47      draggable: false,
     48      title: "Jump to definition",
     49      onClick: async e => {
     50        // Stop the event propagation so we don't trigger ObjectInspector
     51        // expand/collapse.
     52        e.stopPropagation();
     53        if (recordTelemetryEvent) {
     54          recordTelemetryEvent("jump_to_definition");
     55        }
     56 
     57        onViewSourceInDebugger(grip.location);
     58      },
     59    });
     60  }
     61 
     62  const elProps = {
     63    "data-link-actor-id": grip.actor,
     64    className: "objectBox objectBox-function",
     65    // Set dir="ltr" to prevent parentheses from
     66    // appearing in the wrong direction
     67    dir: "ltr",
     68  };
     69 
     70  const parameterNames = (grip.parameterNames || []).filter(Boolean);
     71  const fnTitle = getFunctionTitle(grip, props);
     72  const fnName = getFunctionName(grip, props);
     73 
     74  if (grip.isClassConstructor) {
     75    const classTitle = getClassTitle(grip, props);
     76    const classBodyTooltip = getClassBody(parameterNames, true, props);
     77    const classTooltip = `${classTitle ? classTitle.props.children : ""}${
     78      fnName ? fnName : ""
     79    }${classBodyTooltip.join("")}`;
     80 
     81    elProps.title = shouldRenderTooltip ? classTooltip : null;
     82 
     83    return span(
     84      elProps,
     85      classTitle,
     86      fnName,
     87      ...getClassBody(parameterNames, false, props),
     88      jumpToDefinitionButton
     89    );
     90  }
     91 
     92  const fnTooltip = `${fnTitle ? fnTitle.props.children : ""}${
     93    fnName ? fnName : ""
     94  }(${parameterNames.join(", ")})`;
     95 
     96  elProps.title = shouldRenderTooltip ? fnTooltip : null;
     97 
     98  const returnSpan = span(
     99    elProps,
    100    fnTitle,
    101    fnName,
    102    "(",
    103    ...getParams(parameterNames),
    104    ")",
    105    jumpToDefinitionButton
    106  );
    107 
    108  return returnSpan;
    109 }
    110 
    111 function getClassTitle() {
    112  return span(
    113    {
    114      className: "objectTitle",
    115    },
    116    "class "
    117  );
    118 }
    119 
    120 function getFunctionTitle(grip, props) {
    121  const { mode } = props;
    122 
    123  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
    124    return null;
    125  }
    126 
    127  let title = mode === MODE.TINY ? "" : "function ";
    128 
    129  if (grip.isGenerator) {
    130    title = mode === MODE.TINY ? "* " : "function* ";
    131  }
    132 
    133  if (grip.isAsync) {
    134    title = `${"async" + " "}${title}`;
    135  }
    136 
    137  return span(
    138    {
    139      className: "objectTitle",
    140    },
    141    title
    142  );
    143 }
    144 
    145 /**
    146 * Returns a ReactElement representing the function name.
    147 *
    148 * @param {object} grip : Function grip
    149 * @param {object} props: Function rep props
    150 */
    151 function getFunctionName(grip, props = {}) {
    152  let { functionName } = props;
    153  let name;
    154 
    155  if (functionName) {
    156    const end = functionName.length - 1;
    157    functionName =
    158      functionName.startsWith('"') && functionName.endsWith('"')
    159        ? functionName.substring(1, end)
    160        : functionName;
    161  }
    162 
    163  if (
    164    grip.displayName != undefined &&
    165    functionName != undefined &&
    166    grip.displayName != functionName
    167  ) {
    168    name = `${functionName}:${grip.displayName}`;
    169  } else {
    170    name = cleanFunctionName(
    171      grip.userDisplayName ||
    172        grip.displayName ||
    173        grip.name ||
    174        props.functionName ||
    175        ""
    176    );
    177  }
    178 
    179  return cropString(name, 100);
    180 }
    181 
    182 const objectProperty = /([\w\d\$]+)$/;
    183 const arrayProperty = /\[(.*?)\]$/;
    184 const functionProperty = /([\w\d]+)[\/\.<]*?$/;
    185 const annonymousProperty = /([\w\d]+)\(\^\)$/;
    186 
    187 /**
    188 * Decodes an anonymous naming scheme that
    189 * spider monkey implements based on "Naming Anonymous JavaScript Functions"
    190 * http://johnjbarton.github.io/nonymous/index.html
    191 *
    192 * @param {string} name : Function name to clean up
    193 * @returns String
    194 */
    195 function cleanFunctionName(name) {
    196  for (const reg of [
    197    objectProperty,
    198    arrayProperty,
    199    functionProperty,
    200    annonymousProperty,
    201  ]) {
    202    const match = reg.exec(name);
    203    if (match) {
    204      return match[1];
    205    }
    206  }
    207 
    208  return name;
    209 }
    210 
    211 function getClassBody(constructorParams, textOnly = false, props) {
    212  const { mode } = props;
    213 
    214  if (mode === MODE.TINY) {
    215    return [];
    216  }
    217 
    218  return [" {", ...getClassConstructor(textOnly, constructorParams), "}"];
    219 }
    220 
    221 function getClassConstructor(textOnly = false, parameterNames) {
    222  if (parameterNames.length === 0) {
    223    return [];
    224  }
    225 
    226  if (textOnly) {
    227    return [` constructor(${parameterNames.join(", ")}) `];
    228  }
    229  return [" constructor(", ...getParams(parameterNames), ") "];
    230 }
    231 
    232 function getParams(parameterNames) {
    233  return parameterNames.flatMap((param, index, arr) => {
    234    return [
    235      span({ className: "param" }, param),
    236      index === arr.length - 1 ? "" : span({ className: "delimiter" }, ", "),
    237    ];
    238  });
    239 }
    240 
    241 // Registration
    242 function supportsObject(grip, noGrip = false) {
    243  return getGripType(grip, noGrip) === "Function";
    244 }
    245 
    246 const rep = wrapRender(FunctionRep);
    247 
    248 // Exports from this module
    249 export { rep, supportsObject, cleanFunctionName, getFunctionName };