tor-browser

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

UserAgentInput.js (6726B)


      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 "use strict";
      6 
      7 const {
      8  PureComponent,
      9  createFactory,
     10 } = require("resource://devtools/client/shared/vendor/react.mjs");
     11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     13 const {
     14  connect,
     15 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     16 const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
     17 
     18 const MenuButton = createFactory(
     19  require("resource://devtools/client/shared/components/menu/MenuButton.js")
     20 );
     21 const {
     22  parseUserAgent,
     23 } = require("resource://devtools/client/responsive/utils/ua.js");
     24 
     25 const { AppConstants } = ChromeUtils.importESModule(
     26  "resource://gre/modules/AppConstants.sys.mjs"
     27 );
     28 
     29 loader.lazyGetter(this, "MenuItem", () => {
     30  const menuItemClass = require("resource://devtools/client/shared/components/menu/MenuItem.js");
     31  const menuItem = createFactory(menuItemClass);
     32  menuItem.DUMMY_ICON = menuItemClass.DUMMY_ICON;
     33  return menuItem;
     34 });
     35 
     36 loader.lazyGetter(this, "MenuList", () => {
     37  return createFactory(
     38    require("resource://devtools/client/shared/components/menu/MenuList.js")
     39  );
     40 });
     41 
     42 const {
     43  getStr,
     44 } = require("resource://devtools/client/responsive/utils/l10n.js");
     45 
     46 class UserAgentInput extends PureComponent {
     47  static get propTypes() {
     48    return {
     49      onChangeUserAgent: PropTypes.func.isRequired,
     50      userAgent: PropTypes.string.isRequired,
     51      selectedDeviceName: PropTypes.string,
     52      selectedDeviceUserAgent: PropTypes.string,
     53    };
     54  }
     55 
     56  static getDerivedStateFromProps(props, state) {
     57    if (props.userAgent !== state.prevUserAgent) {
     58      return {
     59        value: props.userAgent,
     60        prevUserAgent: props.userAgent,
     61      };
     62    }
     63    return null;
     64  }
     65 
     66  constructor(props) {
     67    super(props);
     68 
     69    this.state = {
     70      // The user agent input value.
     71      value: this.props.userAgent,
     72      // Track the last passed userAgent value in the props to
     73      // to update the local state "value" when the prop changes
     74      prevUserAgent: this.props.userAgent,
     75    };
     76 
     77    this.onChange = this.onChange.bind(this);
     78    this.onKeyUp = this.onKeyUp.bind(this);
     79    this.onBlur = this.onBlur.bind(this);
     80  }
     81 
     82  /**
     83   * Input change handler.
     84   *
     85   * @param  {Event} event
     86   */
     87  onChange({ target }) {
     88    const value = target.value;
     89 
     90    this.setState(prevState => {
     91      return {
     92        ...prevState,
     93        value,
     94      };
     95    });
     96  }
     97 
     98  /**
     99   * Input key up handler.
    100   *
    101   * @param  {Event} event
    102   */
    103  onKeyUp({ target, keyCode }) {
    104    if (keyCode == KeyCodes.DOM_VK_RETURN) {
    105      // This triggers the onBlur() handler, which calls this.props.onChangeUserAgent()
    106      target.blur();
    107    }
    108 
    109    if (keyCode == KeyCodes.DOM_VK_ESCAPE) {
    110      this.setState({ value: this.props.userAgent }, () => target.blur());
    111    }
    112  }
    113 
    114  onBlur({ target }) {
    115    this.props.onChangeUserAgent(target.value);
    116  }
    117 
    118  onChangeUserAgent(userAgent) {
    119    this.setState({
    120      value: userAgent,
    121    });
    122    this.props.onChangeUserAgent(userAgent);
    123  }
    124 
    125  renderMenuList() {
    126    const browsers = [];
    127 
    128    const { selectedDeviceName, selectedDeviceUserAgent } = this.props;
    129    if (selectedDeviceName) {
    130      const { browser } = parseUserAgent(selectedDeviceUserAgent);
    131      browsers.push({
    132        name: selectedDeviceName,
    133        userAgent: selectedDeviceUserAgent,
    134        icon: browser ? browser.name.toLowerCase() : "",
    135        separator: true,
    136      });
    137    }
    138 
    139    const androidVersion = "15";
    140    const firefoxVersion = AppConstants.MOZ_APP_VERSION.replace(/[ab]\d+/, "");
    141    // Bug 1953205 should revisit how the browser/user agent string list is implemented
    142    // and avoid hardcoding the chrome version number
    143    const chromeVersion = "134.0.0.0";
    144    // Chrome uses a fixed version to reference WebKit and Safari
    145    const frozenWebkitVersionForChromeUA = "537.36";
    146 
    147    browsers.push(
    148      {
    149        name: "Firefox Desktop",
    150        // Empty string will default the firefox original user agent
    151        userAgent: "",
    152        icon: "firefox",
    153        version: firefoxVersion,
    154      },
    155      {
    156        name: "Firefox for Android",
    157        userAgent: `Mozilla/5.0 (Android ${androidVersion}; Mobile; rv:${firefoxVersion}) Gecko/${firefoxVersion} Firefox/${firefoxVersion}`,
    158        icon: "firefox",
    159        version: firefoxVersion,
    160      },
    161      {
    162        name: "Chrome Desktop",
    163        userAgent: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/${frozenWebkitVersionForChromeUA} (KHTML, like Gecko) Chrome/${chromeVersion} Safari/${frozenWebkitVersionForChromeUA}`,
    164        icon: "chrome",
    165        version: chromeVersion,
    166      }
    167    );
    168 
    169    const menuItems = [];
    170    for (const browser of browsers) {
    171      const { icon, name, userAgent, version, separator } = browser;
    172      menuItems.push(
    173        MenuItem({
    174          key: name,
    175          className:
    176            "user-agent-selector-item" + (separator ? " separator" : ""),
    177          label: [
    178            name,
    179            // Only show the major version as chrome uses 136.0.0.0
    180            version
    181              ? dom.span(
    182                  { className: "user-agent-browser-version" },
    183                  version.split(".")[0]
    184                )
    185              : null,
    186          ],
    187          icon: icon
    188            ? `chrome://devtools/skin/images/browsers/${icon}.svg`
    189            : MenuItem.DUMMY_ICON,
    190          tooltip: name,
    191          checked: this.state.value == userAgent,
    192          onClick: () => this.onChangeUserAgent(userAgent),
    193        })
    194      );
    195    }
    196 
    197    return MenuList({}, menuItems);
    198  }
    199 
    200  render() {
    201    return dom.label(
    202      { id: "user-agent-label" },
    203      "UA:",
    204      dom.input({
    205        id: "user-agent-input",
    206        className: "text-input",
    207        onChange: this.onChange,
    208        onKeyUp: this.onKeyUp,
    209        onBlur: this.onBlur,
    210        placeholder: getStr("responsive.customUserAgent"),
    211        type: "text",
    212        value: this.state.value,
    213      }),
    214      MenuButton(
    215        {
    216          id: "user-agent-selector",
    217          menuId: "user-agent-selector-menu",
    218          toolboxDoc: window.document,
    219          className: "devtools-button devtools-dropdown-button",
    220          label: "",
    221          title: getStr("responsive.userAgentList"),
    222        },
    223        () => this.renderMenuList()
    224      )
    225    );
    226  }
    227 }
    228 
    229 const mapStateToProps = state => {
    230  return {
    231    userAgent: state.ui.userAgent,
    232  };
    233 };
    234 
    235 module.exports = connect(mapStateToProps)(UserAgentInput);