tor-browser

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

StartupOSIntegration.sys.mjs (10412B)


      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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
      6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      7 
      8 const PRIVATE_BROWSING_BINARY = "private_browsing.exe";
      9 // Index of Private Browsing icon in private_browsing.exe
     10 // Must line up with IDI_PBICON_PB_PB_EXE in nsNativeAppSupportWin.h.
     11 const PRIVATE_BROWSING_EXE_ICON_INDEX = 1;
     12 const PREF_PRIVATE_BROWSING_SHORTCUT_CREATED =
     13  "browser.privacySegmentation.createdShortcut";
     14 
     15 const lazy = {};
     16 
     17 XPCOMUtils.defineLazyServiceGetters(lazy, {
     18  BrowserHandler: ["@mozilla.org/browser/clh;1", Ci.nsIBrowserHandler],
     19  profileService: [
     20    "@mozilla.org/toolkit/profile-service;1",
     21    Ci.nsIToolkitProfileService,
     22  ],
     23 });
     24 
     25 ChromeUtils.defineESModuleGetters(lazy, {
     26  FirefoxBridgeExtensionUtils:
     27    "resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs",
     28  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
     29  ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
     30  WindowsLaunchOnLogin: "resource://gre/modules/WindowsLaunchOnLogin.sys.mjs",
     31  WindowsGPOParser: "resource://gre/modules/policies/WindowsGPOParser.sys.mjs",
     32 });
     33 
     34 ChromeUtils.defineLazyGetter(lazy, "log", () => {
     35  let { ConsoleAPI } = ChromeUtils.importESModule(
     36    "resource://gre/modules/Console.sys.mjs"
     37  );
     38  let consoleOptions = {
     39    // tip: set maxLogLevel to "debug" and use lazy.log.debug() to create
     40    // detailed messages during development. See LOG_LEVELS in Console.sys.mjs
     41    // for details.
     42    maxLogLevel: "error",
     43    maxLogLevelPref: "browser.policies.loglevel",
     44    prefix: "StartupOSIntegration.sys.mjs",
     45  };
     46  return new ConsoleAPI(consoleOptions);
     47 });
     48 
     49 function WindowsRegPoliciesGetter(wrk, root, regLocation) {
     50  wrk.open(root, regLocation, wrk.ACCESS_READ);
     51  let policies;
     52  if (wrk.hasChild("Mozilla\\" + Services.appinfo.name)) {
     53    policies = lazy.WindowsGPOParser.readPolicies(wrk, policies);
     54  }
     55  wrk.close();
     56  return policies;
     57 }
     58 
     59 export let StartupOSIntegration = {
     60  isPrivateBrowsingAllowedInRegistry() {
     61    // If there is an attempt to open Private Browsing before
     62    // EnterprisePolicies are initialized the Windows registry
     63    // can be checked to determine if it is enabled
     64    if (Services.policies.status > Ci.nsIEnterprisePolicies.UNINITIALIZED) {
     65      // Yield to policies engine if initialized
     66      let privateAllowed = Services.policies.isAllowed("privatebrowsing");
     67      lazy.log.debug(
     68        `Yield to initialized policies engine: Private Browsing Allowed = ${privateAllowed}`
     69      );
     70      return privateAllowed;
     71    }
     72    if (AppConstants.platform !== "win") {
     73      // Not using Windows so no registry, return true
     74      lazy.log.debug(
     75        "AppConstants.platform is not 'win': Private Browsing allowed"
     76      );
     77      return true;
     78    }
     79    // If all other checks fail only then do we check registry
     80    let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
     81      Ci.nsIWindowsRegKey
     82    );
     83    let regLocation = "SOFTWARE\\Policies";
     84    let userPolicies, machinePolicies;
     85    // Only check HKEY_LOCAL_MACHINE if not in testing
     86    if (!Cu.isInAutomation) {
     87      machinePolicies = WindowsRegPoliciesGetter(
     88        wrk,
     89        wrk.ROOT_KEY_LOCAL_MACHINE,
     90        regLocation
     91      );
     92    }
     93    // Check machine policies before checking user policies
     94    // HKEY_LOCAL_MACHINE supersedes HKEY_CURRENT_USER so only check
     95    // HKEY_CURRENT_USER if the registry key is not present in
     96    // HKEY_LOCAL_MACHINE at all
     97    if (machinePolicies && "DisablePrivateBrowsing" in machinePolicies) {
     98      lazy.log.debug(
     99        `DisablePrivateBrowsing in HKEY_LOCAL_MACHINE is ${machinePolicies.DisablePrivateBrowsing}`
    100      );
    101      return !(machinePolicies.DisablePrivateBrowsing === 1);
    102    }
    103    userPolicies = WindowsRegPoliciesGetter(
    104      wrk,
    105      wrk.ROOT_KEY_CURRENT_USER,
    106      regLocation
    107    );
    108    if (userPolicies && "DisablePrivateBrowsing" in userPolicies) {
    109      lazy.log.debug(
    110        `DisablePrivateBrowsing in HKEY_CURRENT_USER is ${userPolicies.DisablePrivateBrowsing}`
    111      );
    112      return !(userPolicies.DisablePrivateBrowsing === 1);
    113    }
    114    // Private browsing allowed if no registry entry exists
    115    lazy.log.debug(
    116      "No DisablePrivateBrowsing registry entry: Private Browsing allowed"
    117    );
    118    return true;
    119  },
    120 
    121  checkForLaunchOnLogin() {
    122    // We only support launch on login on Windows at the moment.
    123    if (AppConstants.platform != "win") {
    124      return;
    125    }
    126    let launchOnLoginPref = "browser.startup.windowsLaunchOnLogin.enabled";
    127    if (!lazy.profileService.startWithLastProfile) {
    128      // If we don't start with last profile, the user
    129      // likely sees the profile selector on launch.
    130      if (Services.prefs.getBoolPref(launchOnLoginPref)) {
    131        Glean.launchOnLogin.lastProfileDisableStartup.record();
    132        // Disable launch on login messaging if we are disabling the
    133        // feature.
    134        Services.prefs.setBoolPref(
    135          "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
    136          true
    137        );
    138      }
    139      // To reduce confusion when running multiple Gecko profiles,
    140      // delete launch on login shortcuts and registry keys so that
    141      // users are not presented with the outdated profile selector
    142      // dialog.
    143      lazy.WindowsLaunchOnLogin.removeLaunchOnLogin();
    144    }
    145  },
    146 
    147  // Note: currently only invoked on Windows and macOS.
    148  async onStartupIdle() {
    149    // Catch and report exceptions, including async rejections:
    150    let safeCall = async fn => {
    151      try {
    152        await fn();
    153      } catch (ex) {
    154        console.error(ex);
    155      }
    156    };
    157    // Note that we explicitly do not await calls to `safeCall` as
    158    // these individual calls are independent and can run without
    159    // waiting for each other.
    160 
    161    // Currently we only support Firefox bridge on Windows and macOS.
    162    safeCall(() => this.ensureBridgeRegistered());
    163 
    164    if (AppConstants.platform == "win") {
    165      if (Services.sysinfo.getProperty("hasWinPackageId")) {
    166        safeCall(() => this.maybePinMSIXToStartMenu());
    167      }
    168      safeCall(() => this.ensurePrivateBrowsingShortcutExists());
    169    }
    170  },
    171 
    172  async ensureBridgeRegistered() {
    173    if (!Services.prefs.getBoolPref("browser.firefoxbridge.enabled", false)) {
    174      return;
    175    }
    176    let { defaultProfile, currentProfile } = lazy.profileService;
    177    if (defaultProfile && currentProfile == defaultProfile) {
    178      await lazy.FirefoxBridgeExtensionUtils.ensureRegistered();
    179    } else {
    180      lazy.log.debug(
    181        "FirefoxBridgeExtensionUtils failed to register due to non-default current profile."
    182      );
    183    }
    184  },
    185 
    186  // Silently pin Firefox to the start menu on first run when using MSIX on a
    187  // new profile.
    188  // If not first run, check if Firefox is no longer pinned to the Start Menu
    189  // when it previously was and send telemetry.
    190  async maybePinMSIXToStartMenu() {
    191    if (!Services.sysinfo.getProperty("hasWinPackageId")) {
    192      return;
    193    }
    194    if (
    195      lazy.BrowserHandler.firstRunProfile &&
    196      (await lazy.ShellService.doesAppNeedStartMenuPin())
    197    ) {
    198      await lazy.ShellService.pinToStartMenu();
    199      return;
    200    }
    201    await lazy.ShellService.recordWasPreviouslyPinnedToStartMenu();
    202  },
    203 
    204  // Ensure a Private Browsing Shortcut exists. This is needed in case
    205  // a user tries to use Windows functionality to pin our Private Browsing
    206  // mode icon to the Taskbar (eg: the "Pin to Taskbar" context menu item).
    207  // This is also created by the installer, but it's possible that a user
    208  // has removed it, or is running out of a zip build. The consequences of not
    209  // having a Shortcut for this are that regular Firefox will be pinned instead
    210  // of the Private Browsing version -- so it's quite important we do our best
    211  // to make sure one is available.
    212  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1762994 for additional
    213  // background.
    214  async ensurePrivateBrowsingShortcutExists() {
    215    if (
    216      // If the feature is disabled, don't do this.
    217      !Services.prefs.getBoolPref(
    218        "browser.privateWindowSeparation.enabled",
    219        true
    220      ) ||
    221      // We don't want a shortcut if it's been disabled, eg: by enterprise policy.
    222      !lazy.PrivateBrowsingUtils.enabled ||
    223      // Private Browsing shortcuts for packaged builds come with the package,
    224      // if they exist at all. We shouldn't try to create our own.
    225      Services.sysinfo.getProperty("hasWinPackageId") ||
    226      // If we've ever done this successfully before, don't try again. The
    227      // user may have deleted the shortcut, and we don't want to force it
    228      // on them.
    229      Services.prefs.getBoolPref(PREF_PRIVATE_BROWSING_SHORTCUT_CREATED, false)
    230    ) {
    231      return;
    232    }
    233 
    234    let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
    235      Ci.nsIWindowsShellService
    236    );
    237    let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(
    238      Ci.nsIWinTaskbar
    239    );
    240 
    241    if (
    242      !(await shellService.hasPinnableShortcut(
    243        winTaskbar.defaultPrivateGroupId,
    244        true
    245      ))
    246    ) {
    247      let appdir = Services.dirsvc.get("GreD", Ci.nsIFile);
    248      let exe = appdir.clone();
    249      exe.append(PRIVATE_BROWSING_BINARY);
    250      let strings = new Localization(
    251        ["branding/brand.ftl", "browser/browser.ftl"],
    252        true
    253      );
    254      let [desc] = await strings.formatValues([
    255        "private-browsing-shortcut-text-2",
    256      ]);
    257      await shellService.createShortcut(
    258        exe,
    259        [],
    260        desc,
    261        exe,
    262        // The code we're calling indexes from 0 instead of 1
    263        PRIVATE_BROWSING_EXE_ICON_INDEX - 1,
    264        winTaskbar.defaultPrivateGroupId,
    265        "Programs",
    266        desc + ".lnk",
    267        appdir
    268      );
    269    }
    270    // We always set this as long as no exception has been thrown. This
    271    // ensure that it is `true` both if we created one because it didn't
    272    // exist, or if it already existed (most likely because it was created
    273    // by the installer). This avoids the need to call `hasPinnableShortcut`
    274    // again, which necessarily does pointless I/O.
    275    Services.prefs.setBoolPref(PREF_PRIVATE_BROWSING_SHORTCUT_CREATED, true);
    276  },
    277 };