tor-browser

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

MeatballMenu.js (9729B)


      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 "use strict";
      5 
      6 const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsole.enabled";
      7 
      8 const {
      9  PureComponent,
     10  createFactory,
     11 } = require("resource://devtools/client/shared/vendor/react.mjs");
     12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     13 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     14 const { hr } = dom;
     15 
     16 loader.lazyGetter(this, "MenuItem", function () {
     17  return createFactory(
     18    require("resource://devtools/client/shared/components/menu/MenuItem.js")
     19  );
     20 });
     21 loader.lazyGetter(this, "MenuList", function () {
     22  return createFactory(
     23    require("resource://devtools/client/shared/components/menu/MenuList.js")
     24  );
     25 });
     26 
     27 loader.lazyRequireGetter(
     28  this,
     29  "openDocLink",
     30  "resource://devtools/client/shared/link.js",
     31  true
     32 );
     33 loader.lazyRequireGetter(
     34  this,
     35  "assert",
     36  "resource://devtools/shared/DevToolsUtils.js",
     37  true
     38 );
     39 
     40 const openDevToolsDocsLink = () => {
     41  openDocLink("https://firefox-source-docs.mozilla.org/devtools-user/");
     42 };
     43 
     44 const openCommunityLink = () => {
     45  openDocLink(
     46    "https://discourse.mozilla.org/c/devtools?utm_source=devtools&utm_medium=tabbar-menu"
     47  );
     48 };
     49 
     50 class MeatballMenu extends PureComponent {
     51  static get propTypes() {
     52    return {
     53      // The id of the currently selected tool, e.g. "inspector"
     54      currentToolId: PropTypes.string,
     55 
     56      // List of possible docking options.
     57      hostTypes: PropTypes.arrayOf(
     58        PropTypes.shape({
     59          position: PropTypes.string.isRequired,
     60          switchHost: PropTypes.func.isRequired,
     61        })
     62      ),
     63 
     64      // Current docking type. Typically one of the position values in
     65      // |hostTypes| but this is not always the case (e.g. for "browsertoolbox").
     66      currentHostType: PropTypes.string,
     67 
     68      // Is the split console currently visible?
     69      isSplitConsoleActive: PropTypes.bool,
     70 
     71      // Are we disabling the behavior where pop-ups are automatically closed
     72      // when clicking outside them?
     73      //
     74      // This is a tri-state value that may be true/false or undefined where
     75      // undefined means that the option is not relevant in this context
     76      // (i.e. we're not in a browser toolbox).
     77      disableAutohide: PropTypes.bool,
     78 
     79      // Apply a pseudo-locale to the Firefox UI. This is only available in the browser
     80      // toolbox. This value can be undefined, "accented", "bidi", "none".
     81      pseudoLocale: PropTypes.string,
     82 
     83      // Function to turn the options panel on / off.
     84      toggleOptions: PropTypes.func.isRequired,
     85 
     86      // Function to turn the split console on / off.
     87      toggleSplitConsole: PropTypes.func,
     88 
     89      // Function to turn the disable pop-up autohide behavior on / off.
     90      toggleNoAutohide: PropTypes.func,
     91 
     92      // Manage the pseudo-localization for the Firefox UI.
     93      // https://firefox-source-docs.mozilla.org/l10n/fluent/tutorial.html#manually-testing-ui-with-pseudolocalization
     94      disablePseudoLocale: PropTypes.func,
     95      enableAccentedPseudoLocale: PropTypes.func,
     96      enableBidiPseudoLocale: PropTypes.func,
     97 
     98      // Bug 1709191 - The help shortcut key is localized without Fluent, and still needs
     99      // to be migrated. This is the only remaining use of the legacy L10N object.
    100      // Everything else should prefer the Fluent API.
    101      L10N: PropTypes.object.isRequired,
    102 
    103      // Callback function that will be invoked any time the component contents
    104      // update in such a way that its bounding box might change.
    105      onResize: PropTypes.func,
    106    };
    107  }
    108 
    109  componentDidUpdate(prevProps) {
    110    if (!this.props.onResize) {
    111      return;
    112    }
    113 
    114    // We are only expecting the following kinds of dynamic changes when a popup
    115    // is showing:
    116    //
    117    // - The "Disable pop-up autohide" menu item being added after the Browser
    118    //   Toolbox is connected.
    119    // - The pseudo locale options being added after the Browser Toolbox is connected.
    120    // - The split console label changing between "Show Split Console" and "Hide
    121    //   Split Console".
    122    // - The "Show/Hide Split Console" entry being added removed or removed.
    123    //
    124    // The latter two cases are only likely to be noticed when "Disable pop-up
    125    // autohide" is active, but for completeness we handle them here.
    126    const didChange =
    127      typeof this.props.disableAutohide !== typeof prevProps.disableAutohide ||
    128      this.props.pseudoLocale !== prevProps.pseudoLocale ||
    129      this.props.currentToolId !== prevProps.currentToolId ||
    130      this.props.isSplitConsoleActive !== prevProps.isSplitConsoleActive;
    131 
    132    if (didChange) {
    133      this.props.onResize();
    134    }
    135  }
    136 
    137  render() {
    138    const items = [];
    139 
    140    // Dock options
    141    for (const hostType of this.props.hostTypes) {
    142      // This is more verbose than it needs to be but lets us easily search for
    143      // l10n entities.
    144      let l10nID;
    145      switch (hostType.position) {
    146        case "window":
    147          l10nID = "toolbox-meatball-menu-dock-separate-window-label";
    148          break;
    149 
    150        case "bottom":
    151          l10nID = "toolbox-meatball-menu-dock-bottom-label";
    152          break;
    153 
    154        case "left":
    155          l10nID = "toolbox-meatball-menu-dock-left-label";
    156          break;
    157 
    158        case "right":
    159          l10nID = "toolbox-meatball-menu-dock-right-label";
    160          break;
    161 
    162        default:
    163          assert(false, `Unexpected hostType.position: ${hostType.position}`);
    164          break;
    165      }
    166 
    167      items.push(
    168        MenuItem({
    169          id: `toolbox-meatball-menu-dock-${hostType.position}`,
    170          key: `dock-${hostType.position}`,
    171          l10nID,
    172          onClick: hostType.switchHost,
    173          checked: hostType.position === this.props.currentHostType,
    174          className: "iconic",
    175        })
    176      );
    177    }
    178 
    179    if (items.length) {
    180      items.push(hr({ key: "dock-separator" }));
    181    }
    182 
    183    // Split console
    184    if (this.props.currentToolId !== "webconsole") {
    185      const isSplitConsoleEnabled = Services.prefs.getBoolPref(
    186        SPLITCONSOLE_ENABLED_PREF,
    187        true
    188      );
    189 
    190      if (isSplitConsoleEnabled) {
    191        const l10nID = this.props.isSplitConsoleActive
    192          ? "toolbox-meatball-menu-hideconsole-label"
    193          : "toolbox-meatball-menu-splitconsole-label";
    194 
    195        items.push(
    196          MenuItem({
    197            id: "toolbox-meatball-menu-splitconsole",
    198            key: "splitconsole",
    199            l10nID,
    200            accelerator: "Esc",
    201            onClick: this.props.toggleSplitConsole,
    202            className: "iconic",
    203          })
    204        );
    205      }
    206    }
    207 
    208    // Settings
    209    items.push(
    210      MenuItem({
    211        id: "toolbox-meatball-menu-settings",
    212        key: "settings",
    213        l10nID: "toolbox-meatball-menu-settings-label",
    214        // Bug 1709191 - The help key is localized without Fluent, and still needs to
    215        // be migrated.
    216        accelerator: this.props.L10N.getStr("toolbox.help.key"),
    217        onClick: this.props.toggleOptions,
    218        className: "iconic",
    219      })
    220    );
    221 
    222    if (
    223      typeof this.props.disableAutohide !== "undefined" ||
    224      typeof this.props.pseudoLocale !== "undefined"
    225    ) {
    226      items.push(hr({ key: "docs-separator-1" }));
    227    }
    228 
    229    // Disable pop-up autohide
    230    //
    231    // If |disableAutohide| is undefined, it means this feature is not available
    232    // in this context.
    233    if (typeof this.props.disableAutohide !== "undefined") {
    234      items.push(
    235        MenuItem({
    236          id: "toolbox-meatball-menu-noautohide",
    237          key: "noautohide",
    238          l10nID: "toolbox-meatball-menu-noautohide-label",
    239          type: "checkbox",
    240          checked: this.props.disableAutohide,
    241          onClick: this.props.toggleNoAutohide,
    242          className: "iconic",
    243        })
    244      );
    245    }
    246 
    247    // Pseudo-locales.
    248    if (typeof this.props.pseudoLocale !== "undefined") {
    249      const {
    250        pseudoLocale,
    251        enableAccentedPseudoLocale,
    252        enableBidiPseudoLocale,
    253        disablePseudoLocale,
    254      } = this.props;
    255      items.push(
    256        MenuItem({
    257          id: "toolbox-meatball-menu-pseudo-locale-accented",
    258          key: "pseudo-locale-accented",
    259          l10nID: "toolbox-meatball-menu-pseudo-locale-accented",
    260          type: "checkbox",
    261          checked: pseudoLocale === "accented",
    262          onClick:
    263            pseudoLocale === "accented"
    264              ? disablePseudoLocale
    265              : enableAccentedPseudoLocale,
    266          className: "iconic",
    267        }),
    268        MenuItem({
    269          id: "toolbox-meatball-menu-pseudo-locale-bidi",
    270          key: "pseudo-locale-bidi",
    271          l10nID: "toolbox-meatball-menu-pseudo-locale-bidi",
    272          type: "checkbox",
    273          checked: pseudoLocale === "bidi",
    274          onClick:
    275            pseudoLocale === "bidi"
    276              ? disablePseudoLocale
    277              : enableBidiPseudoLocale,
    278          className: "iconic",
    279        })
    280      );
    281    }
    282 
    283    items.push(hr({ key: "docs-separator-2" }));
    284 
    285    // Getting started
    286    items.push(
    287      MenuItem({
    288        id: "toolbox-meatball-menu-documentation",
    289        key: "documentation",
    290        l10nID: "toolbox-meatball-menu-documentation-label",
    291        onClick: openDevToolsDocsLink,
    292      })
    293    );
    294 
    295    // Give feedback
    296    items.push(
    297      MenuItem({
    298        id: "toolbox-meatball-menu-community",
    299        key: "community",
    300        l10nID: "toolbox-meatball-menu-community-label",
    301        onClick: openCommunityLink,
    302      })
    303    );
    304 
    305    return MenuList({ id: "toolbox-meatball-menu" }, items);
    306  }
    307 }
    308 
    309 module.exports = MeatballMenu;