tor-browser

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

actions.js (5851B)


      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 const { loadItemProperties } = require("resource://devtools/client/shared/components/object-inspector/utils/load-properties.js");
      6 const {
      7  getPathExpression,
      8  getParentFront,
      9  getParentGripValue,
     10  getValue,
     11  nodeIsBucket,
     12  getFront,
     13 } = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
     14 const { getLoadedProperties } = require("resource://devtools/client/shared/components/object-inspector/reducer.js");
     15 
     16 /**
     17 * This action is responsible for expanding a given node, which also means that
     18 * it will call the action responsible to fetch properties.
     19 */
     20 function nodeExpand(node, actor) {
     21  return async ({ dispatch }) => {
     22    dispatch({ type: "NODE_EXPAND", data: { node } });
     23    dispatch(nodeLoadProperties(node, actor));
     24  };
     25 }
     26 
     27 function nodeCollapse(node) {
     28  return {
     29    type: "NODE_COLLAPSE",
     30    data: { node },
     31  };
     32 }
     33 
     34 /*
     35 * This action checks if we need to fetch properties, entries, prototype and
     36 * symbols for a given node. If we do, it will call the appropriate ObjectFront
     37 * functions.
     38 */
     39 function nodeLoadProperties(node, actor) {
     40  return async ({ dispatch, client, getState }) => {
     41    const state = getState();
     42    const loadedProperties = getLoadedProperties(state);
     43    if (loadedProperties.has(node.path)) {
     44      return;
     45    }
     46 
     47    try {
     48      const properties = await loadItemProperties(
     49        node,
     50        client,
     51        loadedProperties
     52      );
     53 
     54      // If the client does not have a releaseActor function, it means the actors are
     55      // handled directly by the consumer, so we don't need to track them.
     56      if (!client || !client.releaseActor) {
     57        actor = null;
     58      }
     59 
     60      dispatch(nodePropertiesLoaded(node, actor, properties));
     61    } catch (e) {
     62      console.error(e);
     63      dispatch(nodeCollapse(node));
     64    }
     65  };
     66 }
     67 
     68 function nodePropertiesLoaded(node, actor, properties) {
     69  return {
     70    type: "NODE_PROPERTIES_LOADED",
     71    data: { node, actor, properties },
     72  };
     73 }
     74 
     75 /*
     76 * This action adds a property watchpoint to an object
     77 */
     78 function addWatchpoint(item, watchpoint) {
     79  return async function({ dispatch, client }) {
     80    const { parent, name } = item;
     81    let object = getValue(parent);
     82 
     83    if (nodeIsBucket(parent)) {
     84      object = getValue(parent.parent);
     85    }
     86 
     87    if (!object) {
     88      return;
     89    }
     90 
     91    const path = parent.path;
     92    const property = name;
     93    const label = getPathExpression(item);
     94    const actor = object.actor;
     95 
     96    await client.addWatchpoint(object, property, label, watchpoint);
     97 
     98    dispatch({
     99      type: "SET_WATCHPOINT",
    100      data: { path, watchpoint, property, actor },
    101    });
    102  };
    103 }
    104 
    105 /*
    106 * This action removes a property watchpoint from an object
    107 */
    108 function removeWatchpoint(item) {
    109  return async function({ dispatch, client }) {
    110    const { parent, name } = item;
    111    let object = getValue(parent);
    112 
    113    if (nodeIsBucket(parent)) {
    114      object = getValue(parent.parent);
    115    }
    116 
    117    const property = name;
    118    const path = parent.path;
    119    const actor = object.actor;
    120 
    121    await client.removeWatchpoint(object, property);
    122 
    123    dispatch({
    124      type: "REMOVE_WATCHPOINT",
    125      data: { path, property, actor },
    126    });
    127  };
    128 }
    129 
    130 function getActorIDs(roots) {
    131  if (!roots) {
    132    return []
    133  }
    134 
    135  const actorIds = [];
    136  for (const root of roots) {
    137    const front = getFront(root);
    138    if (front?.actorID) {
    139      actorIds.push(front.actorID);
    140    }
    141  }
    142 
    143  return actorIds;
    144 }
    145 
    146 function closeObjectInspector(roots) {
    147  return ({ client }) => {
    148    releaseActors(client, roots);
    149  };
    150 }
    151 
    152 /*
    153 * This action is dispatched when the `roots` prop, provided by a consumer of
    154 * the ObjectInspector (inspector, console, …), is modified. It will clean the
    155 * internal state properties (expandedPaths, loadedProperties, …) and release
    156 * the actors consumed with the previous roots.
    157 * It takes a props argument which reflects what is passed by the upper-level
    158 * consumer.
    159 */
    160 function rootsChanged(roots, oldRoots, autoReleaseObjectActors) {
    161  return ({ dispatch, client }) => {
    162    if (autoReleaseObjectActors) {
    163      releaseActors(client, oldRoots, roots);
    164    }
    165    dispatch({
    166      type: "ROOTS_CHANGED",
    167      data: roots,
    168    });
    169  };
    170 }
    171 
    172 /**
    173 * Release any actors we don't need anymore
    174 *
    175 * @param {object} client: Object with a `releaseActor` method
    176 * @param {Array} oldRoots: The roots in which we want to cleanup now-unused actors
    177 * @param {Array} newRoots: The current roots (might have item that are also in oldRoots)
    178 */
    179 async function releaseActors(client, oldRoots, newRoots = []) {
    180  if (!client?.releaseActor ) {
    181    return;
    182  }
    183 
    184  let actorIdsToRelease = getActorIDs(oldRoots);
    185  if (newRoots.length) {
    186    const newActorIds = getActorIDs(newRoots);
    187    actorIdsToRelease = actorIdsToRelease.filter(id => !newActorIds.includes(id));
    188  }
    189 
    190  if (!actorIdsToRelease.length) {
    191    return;
    192  }
    193  await Promise.all(actorIdsToRelease.map(client.releaseActor));
    194 }
    195 
    196 function invokeGetter(node, receiverId) {
    197  return async ({ dispatch, client, _getState }) => {
    198    try {
    199      const objectFront =
    200        getParentFront(node) ||
    201        client.createObjectFront(getParentGripValue(node));
    202      const getterName = node.propertyName || node.name;
    203 
    204      const result = await objectFront.getPropertyValue(getterName, receiverId);
    205      dispatch({
    206        type: "GETTER_INVOKED",
    207        data: {
    208          node,
    209          result,
    210        },
    211      });
    212    } catch (e) {
    213      console.error(e);
    214    }
    215  };
    216 }
    217 
    218 module.exports = {
    219  closeObjectInspector,
    220  invokeGetter,
    221  nodeExpand,
    222  nodeCollapse,
    223  nodeLoadProperties,
    224  nodePropertiesLoaded,
    225  rootsChanged,
    226  addWatchpoint,
    227  removeWatchpoint,
    228 };