tor-browser

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

ua_helpers.js (6450B)


      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 /* globals browser, exportFunction */
      8 
      9 var UAHelpers = {
     10  _deviceAppropriateChromeUAs: {},
     11  getRunningFirefoxVersion() {
     12    return (navigator.userAgent.match(/Firefox\/([0-9.]+)/) || ["", "58.0"])[1];
     13  },
     14  getFxQuantumSegment() {
     15    return `FxQuantum/${UAHelpers.getRunningFirefoxVersion()} `;
     16  },
     17  getDeviceAppropriateChromeUA(config = {}) {
     18    let { androidVersion, version = "143.0.0.0", phone, tablet, OS } = config;
     19    const key = JSON.stringify(config);
     20    if (config.noCache || !UAHelpers._deviceAppropriateChromeUAs[key]) {
     21      const userAgent =
     22        config.ua ||
     23        (typeof navigator !== "undefined" ? navigator.userAgent : "");
     24      const fxQuantum = config.noFxQuantum
     25        ? ""
     26        : UAHelpers.getFxQuantumSegment();
     27      const noOSGiven = !OS || OS === "nonLinux";
     28      if (OS === "android" || (noOSGiven && userAgent.includes("Android"))) {
     29        const AndroidVersion = androidVersion
     30          ? `Android ${androidVersion}`
     31          : userAgent.match(/Android [0-9.]+/) || "Android 6.0";
     32        if (phone === undefined && tablet === undefined) {
     33          phone = userAgent.includes("Mobile");
     34          tablet = userAgent.includes("Tablet");
     35        }
     36        if (phone === true) {
     37          phone = "Nexus 5 Build/MRA58N";
     38        }
     39        if (tablet === true || (!tablet && !phone)) {
     40          tablet = "Nexus 7 Build/JSS15Q";
     41        }
     42        if (phone) {
     43          UAHelpers._deviceAppropriateChromeUAs[key] =
     44            `Mozilla/5.0 (Linux; ${AndroidVersion}; ${phone}) ${fxQuantum}AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Mobile Safari/537.36`;
     45        } else {
     46          UAHelpers._deviceAppropriateChromeUAs[key] =
     47            `Mozilla/5.0 (Linux; ${AndroidVersion}; ${tablet}) ${fxQuantum}AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
     48        }
     49      } else {
     50        const WIN_SEGMENT = "Windows NT 11.0; Win64; x64";
     51        let osSegment;
     52        if (OS === "macOS" || (noOSGiven && userAgent.includes("Macintosh"))) {
     53          osSegment = "Macintosh; Intel Mac OS X 10_15_7";
     54        } else if (
     55          OS === "linux" ||
     56          (noOSGiven && userAgent.includes("Linux"))
     57        ) {
     58          if (OS !== "nonLinux") {
     59            osSegment = "X11; Ubuntu; Linux x86_64";
     60          } else {
     61            osSegment = WIN_SEGMENT;
     62          }
     63        } else {
     64          osSegment = WIN_SEGMENT;
     65        }
     66 
     67        UAHelpers._deviceAppropriateChromeUAs[key] =
     68          `Mozilla/5.0 (${osSegment}) ${fxQuantum}AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
     69      }
     70    }
     71    return UAHelpers._deviceAppropriateChromeUAs[key];
     72  },
     73  addGecko(
     74    ua = navigator.userAgent,
     75    version = UAHelpers.getRunningFirefoxVersion()
     76  ) {
     77    return `${ua} Gecko/${version}`;
     78  },
     79  addChrome(ua = navigator.userAgent, version = "143.0.0.0") {
     80    const isMobile =
     81      navigator.userAgent.includes("Mobile") ||
     82      navigator.userAgent.includes("Tablet");
     83    return `${ua} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} ${isMobile ? "Mobile " : ""}Safari/537.36`;
     84  },
     85  safari(config = {}) {
     86    const version = config.version || "26.1";
     87    const webkitVersion = config.webkitVersion || "605.1.15";
     88    const osVersion = config.osVersion?.replace(".", "_") || "10_15_7";
     89    const arch = config.arch || "Intel";
     90    let firefox = "";
     91    if (config.withFirefox) {
     92      firefox = `Firefox/${UAHelpers.getRunningFirefoxVersion()} `;
     93    } else if (config.withFxQuantum) {
     94      firefox = UAHelpers.getFxQuantumSegment();
     95    }
     96    return `Mozilla/5.0 (Macintosh; ${arch} Mac OS X ${osVersion}) ${firefox}AppleWebKit/${webkitVersion} (KHTML, like Gecko) Version/${version} Safari/${webkitVersion}`;
     97  },
     98  androidHotspot2Device(originalUA) {
     99    return originalUA.replace(/\(.+?\)/, "(Linux; Android 10; K)");
    100  },
    101  changeFirefoxToFireFox(ua = navigator.userAgent) {
    102    return ua.replace("Firefox", "FireFox");
    103  },
    104  windows(ua = navigator.userAgent) {
    105    const rv = navigator.userAgent.match("rv:[0-9]+.[0-9]+")[0];
    106    return ua.replace(/\(.+?\)/, `(Windows NT 10.0; Win64; x64; ${rv})`);
    107  },
    108  desktopUA(ua = navigator.userAgent) {
    109    return ua.replace(/ (Mobile|Tablet);/, "");
    110  },
    111  addSamsungForSamsungDevices(ua = navigator.userAgent) {
    112    if (!browser.systemManufacturer) {
    113      return ua;
    114    }
    115 
    116    const manufacturer = browser.systemManufacturer.getManufacturer();
    117    if (manufacturer && manufacturer.toLowerCase() === "samsung") {
    118      return ua.replace("Mobile;", "Mobile; Samsung;");
    119    }
    120 
    121    return ua;
    122  },
    123  getPrefix(originalUA) {
    124    return originalUA.substr(0, originalUA.indexOf(")") + 1);
    125  },
    126  overrideWithDeviceAppropriateChromeUA(config) {
    127    const chromeUA = UAHelpers.getDeviceAppropriateChromeUA(config);
    128    const nav = Object.getPrototypeOf(navigator.wrappedJSObject);
    129    const ua = Object.getOwnPropertyDescriptor(nav, "userAgent");
    130    ua.get = exportFunction(() => chromeUA, window);
    131    Object.defineProperty(nav, "userAgent", ua);
    132  },
    133  capVersionTo99(originalUA) {
    134    const ver = originalUA.match(/Firefox\/(\d+\.\d+)/);
    135    if (!ver || parseFloat(ver[1]) < 100) {
    136      return originalUA;
    137    }
    138    return originalUA
    139      .replace(`Firefox/${ver[1]}`, "Firefox/99.0")
    140      .replace(`rv:${ver[1]}`, "rv:99.0");
    141  },
    142  capRvTo109(originalUA) {
    143    const ver = originalUA.match(/rv:(\d+\.\d+)/);
    144    if (!ver || parseFloat(ver[1]) <= 109) {
    145      return originalUA;
    146    }
    147    return originalUA.replace(`rv:${ver[1]}`, "rv:109.0");
    148  },
    149  capVersionToNumber(originalUA, cap = 120) {
    150    const ver = originalUA.match(/Firefox\/(\d+\.\d+)/);
    151    if (!ver || parseFloat(ver[1]) <= cap) {
    152      return originalUA;
    153    }
    154    const capped = `Firefox/${cap}.0`;
    155    return originalUA.replace(`Firefox/${ver[1]}`, capped);
    156  },
    157  getMacOSXUA(originalUA, arch = "Intel", version = "10.15") {
    158    return originalUA.replace(
    159      /\(.+?\)/,
    160      `(Macintosh; ${arch} Mac OS X ${version})`
    161    );
    162  },
    163  getWindowsUA(originalUA) {
    164    const rv = originalUA.match("rv:[0-9]+.[0-9]+")[0];
    165    const ver = originalUA.match("Firefox/[0-9]+.[0-9]+")[0];
    166    return `Mozilla/5.0 (Windows NT 10.0; Win64; x64; ${rv}) Gecko/20100101 ${ver}`;
    167  },
    168 };