tor-browser

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

source-tree-item.js (9955B)


      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 { showMenu } from "../../context-menu/menu";
      6 
      7 import {
      8  isSourceMapIgnoreListEnabled,
      9  isSourceOnSourceMapIgnoreList,
     10  getProjectDirectoryRoot,
     11  getSourcesTreeSources,
     12  getBlackBoxRanges,
     13 } from "../../selectors/index";
     14 
     15 import {
     16  setNetworkOverride,
     17  removeNetworkOverride,
     18 } from "devtools/client/framework/actions/index";
     19 
     20 import { loadSourceText } from "../sources/loadSourceText";
     21 import { toggleBlackBox, blackBoxSources } from "../sources/blackbox";
     22 import {
     23  setProjectDirectoryRoot,
     24  clearProjectDirectoryRoot,
     25 } from "../sources-tree";
     26 
     27 import { shouldBlackbox } from "../../utils/source";
     28 import { copyToTheClipboard } from "../../utils/clipboard";
     29 import { saveAsLocalFile } from "../../utils/utils";
     30 
     31 /**
     32 * Show the context menu of SourceTreeItem.
     33 *
     34 * @param {object} event
     35 *        The context-menu DOM event.
     36 * @param {object} item
     37 *        Source Tree Item object.
     38 */
     39 export function showSourceTreeItemContextMenu(
     40  event,
     41  item,
     42  depth,
     43  setExpanded,
     44  itemName,
     45  isOverridden
     46 ) {
     47  return async ({ dispatch, getState, panel }) => {
     48    const copySourceUri2Label = L10N.getStr("copySourceUri2");
     49    const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
     50    const setDirectoryRootLabel = L10N.getStr("setDirectoryRoot.label");
     51    const setDirectoryRootKey = L10N.getStr("setDirectoryRoot.accesskey");
     52    const removeDirectoryRootLabel = L10N.getStr("removeDirectoryRoot.label");
     53 
     54    const menuOptions = [];
     55 
     56    const state = getState();
     57    const isSourceOnIgnoreList =
     58      isSourceMapIgnoreListEnabled(state) &&
     59      isSourceOnSourceMapIgnoreList(state, item.source);
     60    const projectRoot = getProjectDirectoryRoot(state);
     61 
     62    if (item.type == "source") {
     63      const { source } = item;
     64      const copySourceUri2 = {
     65        id: "node-menu-copy-source",
     66        label: copySourceUri2Label,
     67        accesskey: copySourceUri2Key,
     68        disabled: false,
     69        click: () => copyToTheClipboard(source.url),
     70      };
     71 
     72      const ignoreStr = item.isBlackBoxed ? "unignore" : "ignore";
     73      const blackBoxMenuItem = {
     74        id: "node-menu-blackbox",
     75        label: L10N.getStr(`ignoreContextItem.${ignoreStr}`),
     76        accesskey: L10N.getStr(`ignoreContextItem.${ignoreStr}.accesskey`),
     77        disabled: isSourceOnIgnoreList || !shouldBlackbox(source),
     78        click: () => dispatch(toggleBlackBox(source)),
     79      };
     80      const downloadFileItem = {
     81        id: "node-menu-download-file",
     82        label: L10N.getStr("downloadFile.label"),
     83        accesskey: L10N.getStr("downloadFile.accesskey"),
     84        disabled: false,
     85        click: () => saveLocalFile(dispatch, source),
     86      };
     87 
     88      const overrideStr = !isOverridden ? "override" : "removeOverride";
     89      const overridesItem = {
     90        id: "node-menu-overrides",
     91        label: L10N.getStr(`overridesContextItem.${overrideStr}`),
     92        accesskey: L10N.getStr(`overridesContextItem.${overrideStr}.accesskey`),
     93        // Network overrides are disabled for original files.
     94        disabled: source.isOriginal,
     95        // Network overrides are disabled for remote debugging (bug 1881441).
     96        visible: panel.toolbox.commands.descriptorFront.isLocalTab,
     97        click: () =>
     98          handleLocalOverride(dispatch, panel.toolbox, source, isOverridden),
     99      };
    100 
    101      menuOptions.push(
    102        copySourceUri2,
    103        blackBoxMenuItem,
    104        downloadFileItem,
    105        overridesItem
    106      );
    107    }
    108 
    109    // All other types other than source are folder-like
    110    if (item.type != "source") {
    111      addCollapseExpandAllOptions(menuOptions, item, setExpanded);
    112 
    113      if (projectRoot == item.uniquePath) {
    114        menuOptions.push({
    115          id: "node-remove-directory-root",
    116          label: removeDirectoryRootLabel,
    117          disabled: false,
    118          click: () => dispatch(clearProjectDirectoryRoot()),
    119        });
    120      } else {
    121        menuOptions.push({
    122          id: "node-set-directory-root",
    123          label: setDirectoryRootLabel,
    124          accesskey: setDirectoryRootKey,
    125          disabled: false,
    126          click: () =>
    127            dispatch(
    128              setProjectDirectoryRoot(
    129                item.uniquePath,
    130                itemName,
    131                getItemProjectDirectoryRootName(item)
    132              )
    133            ),
    134        });
    135      }
    136 
    137      addBlackboxAllOption(dispatch, state, menuOptions, item, depth);
    138    }
    139 
    140    showMenu(event, menuOptions);
    141  };
    142 }
    143 
    144 /**
    145 * Compute the string which will be displayed as tooltip on the project directory root header
    146 */
    147 function getItemProjectDirectoryRootName(item) {
    148  if (item.thread) {
    149    return item.thread.name;
    150  }
    151 
    152  // Go up the source tree to get to the group item
    153  let groupItem = item;
    154  while (!groupItem.groupName) {
    155    groupItem = groupItem.parent;
    156  }
    157 
    158  // Group's origin is the base URL
    159  const origin = groupItem.origin;
    160  const path = item != groupItem ? item.path : "";
    161  // The group item's parent is always a thread item
    162  const threadName = groupItem.parent.thread.name;
    163 
    164  return `${origin}${path} on ${threadName}`;
    165 }
    166 
    167 async function saveLocalFile(dispatch, source) {
    168  if (!source) {
    169    return null;
    170  }
    171 
    172  const data = await dispatch(loadSourceText(source));
    173  if (!data) {
    174    return null;
    175  }
    176  return saveAsLocalFile(data.value, source.displayURL.filename);
    177 }
    178 
    179 async function handleLocalOverride(dispatch, toolbox, source, isOverridden) {
    180  if (!source || !source.url) {
    181    return;
    182  }
    183 
    184  const toolboxStore = toolbox.store;
    185  if (!isOverridden) {
    186    const data = await dispatch(loadSourceText(source));
    187    if (data?.value && data.value.type == "text") {
    188      toolboxStore.dispatch(
    189        setNetworkOverride(
    190          toolbox.commands,
    191          source.url,
    192          data.value.value,
    193          window
    194        )
    195      );
    196    }
    197  } else {
    198    toolboxStore.dispatch(removeNetworkOverride(toolbox.commands, source.url));
    199  }
    200 }
    201 
    202 function addBlackboxAllOption(dispatch, state, menuOptions, item, depth) {
    203  const {
    204    sourcesInside,
    205    sourcesOutside,
    206    allInsideBlackBoxed,
    207    allOutsideBlackBoxed,
    208  } = getBlackBoxSourcesGroups(state, item);
    209  const projectRoot = getProjectDirectoryRoot(state);
    210 
    211  let blackBoxInsideMenuItemLabel;
    212  let blackBoxOutsideMenuItemLabel;
    213  if (depth === 0 || (depth === 1 && projectRoot === "")) {
    214    blackBoxInsideMenuItemLabel = allInsideBlackBoxed
    215      ? L10N.getStr("unignoreAllInGroup.label")
    216      : L10N.getStr("ignoreAllInGroup.label");
    217    if (sourcesOutside.length) {
    218      blackBoxOutsideMenuItemLabel = allOutsideBlackBoxed
    219        ? L10N.getStr("unignoreAllOutsideGroup.label")
    220        : L10N.getStr("ignoreAllOutsideGroup.label");
    221    }
    222  } else {
    223    blackBoxInsideMenuItemLabel = allInsideBlackBoxed
    224      ? L10N.getStr("unignoreAllInDir.label")
    225      : L10N.getStr("ignoreAllInDir.label");
    226    if (sourcesOutside.length) {
    227      blackBoxOutsideMenuItemLabel = allOutsideBlackBoxed
    228        ? L10N.getStr("unignoreAllOutsideDir.label")
    229        : L10N.getStr("ignoreAllOutsideDir.label");
    230    }
    231  }
    232 
    233  const blackBoxInsideMenuItem = {
    234    id: allInsideBlackBoxed
    235      ? "node-unblackbox-all-inside"
    236      : "node-blackbox-all-inside",
    237    label: blackBoxInsideMenuItemLabel,
    238    disabled: false,
    239    click: () => dispatch(blackBoxSources(sourcesInside, !allInsideBlackBoxed)),
    240  };
    241 
    242  if (sourcesOutside.length) {
    243    menuOptions.push({
    244      id: "node-blackbox-all",
    245      label: L10N.getStr("ignoreAll.label"),
    246      submenu: [
    247        blackBoxInsideMenuItem,
    248        {
    249          id: allOutsideBlackBoxed
    250            ? "node-unblackbox-all-outside"
    251            : "node-blackbox-all-outside",
    252          label: blackBoxOutsideMenuItemLabel,
    253          disabled: false,
    254          click: () =>
    255            dispatch(blackBoxSources(sourcesOutside, !allOutsideBlackBoxed)),
    256        },
    257      ],
    258    });
    259  } else {
    260    menuOptions.push(blackBoxInsideMenuItem);
    261  }
    262 }
    263 
    264 function addCollapseExpandAllOptions(menuOptions, item, setExpanded) {
    265  menuOptions.push({
    266    id: "node-menu-collapse-all",
    267    label: L10N.getStr("collapseAll.label"),
    268    disabled: false,
    269    click: () => setExpanded(item, false, true),
    270  });
    271 
    272  menuOptions.push({
    273    id: "node-menu-expand-all",
    274    label: L10N.getStr("expandAll.label"),
    275    disabled: false,
    276    click: () => setExpanded(item, true, true),
    277  });
    278 }
    279 
    280 /**
    281 * Computes 4 lists:
    282 *  - `sourcesInside`: the list of all Source Items that are
    283 *    children of the current item (can be thread/group/directory).
    284 *    This include any nested level of children.
    285 *  - `sourcesOutside`: all other Source Items.
    286 *    i.e. all sources that are in any other folder of any group/thread.
    287 *  - `allInsideBlackBoxed`, all sources of `sourcesInside` which are currently
    288 *    blackboxed.
    289 *  - `allOutsideBlackBoxed`, all sources of `sourcesOutside` which are currently
    290 *    blackboxed.
    291 */
    292 function getBlackBoxSourcesGroups(state, item) {
    293  const allSources = [];
    294  function collectAllSources(list, _item) {
    295    if (_item.children) {
    296      _item.children.forEach(i => collectAllSources(list, i));
    297    }
    298    if (_item.type == "source") {
    299      list.push(_item.source);
    300    }
    301  }
    302 
    303  const rootItems = getSourcesTreeSources(state);
    304  const blackBoxRanges = getBlackBoxRanges(state);
    305 
    306  for (const rootItem of rootItems) {
    307    collectAllSources(allSources, rootItem);
    308  }
    309 
    310  const sourcesInside = [];
    311  collectAllSources(sourcesInside, item);
    312 
    313  const sourcesOutside = allSources.filter(
    314    source => !sourcesInside.includes(source)
    315  );
    316  const allInsideBlackBoxed = sourcesInside.every(
    317    source => blackBoxRanges[source.url]
    318  );
    319  const allOutsideBlackBoxed = sourcesOutside.every(
    320    source => blackBoxRanges[source.url]
    321  );
    322 
    323  return {
    324    sourcesInside,
    325    sourcesOutside,
    326    allInsideBlackBoxed,
    327    allOutsideBlackBoxed,
    328  };
    329 }