277 lines
10 KiB
JavaScript
277 lines
10 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
|
|
|
const PRIVATE_BROWSING_BINARY = "private_browsing.exe";
|
|
// Index of Private Browsing icon in private_browsing.exe
|
|
// Must line up with IDI_PBICON_PB_PB_EXE in nsNativeAppSupportWin.h.
|
|
const PRIVATE_BROWSING_EXE_ICON_INDEX = 1;
|
|
const PREF_PRIVATE_BROWSING_SHORTCUT_CREATED =
|
|
"browser.privacySegmentation.createdShortcut";
|
|
|
|
const lazy = {};
|
|
|
|
XPCOMUtils.defineLazyServiceGetters(lazy, {
|
|
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
|
|
profileService: [
|
|
"@mozilla.org/toolkit/profile-service;1",
|
|
"nsIToolkitProfileService",
|
|
],
|
|
});
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
FirefoxBridgeExtensionUtils:
|
|
"resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs",
|
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
|
ShellService: "resource:///modules/ShellService.sys.mjs",
|
|
WindowsLaunchOnLogin: "resource://gre/modules/WindowsLaunchOnLogin.sys.mjs",
|
|
WindowsGPOParser: "resource://gre/modules/policies/WindowsGPOParser.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "log", () => {
|
|
let { ConsoleAPI } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Console.sys.mjs"
|
|
);
|
|
let consoleOptions = {
|
|
// tip: set maxLogLevel to "debug" and use lazy.log.debug() to create
|
|
// detailed messages during development. See LOG_LEVELS in Console.sys.mjs
|
|
// for details.
|
|
maxLogLevel: "error",
|
|
maxLogLevelPref: "browser.policies.loglevel",
|
|
prefix: "StartupOSIntegration.sys.mjs",
|
|
};
|
|
return new ConsoleAPI(consoleOptions);
|
|
});
|
|
|
|
function WindowsRegPoliciesGetter(wrk, root, regLocation) {
|
|
wrk.open(root, regLocation, wrk.ACCESS_READ);
|
|
let policies;
|
|
if (wrk.hasChild("Mozilla\\" + Services.appinfo.name)) {
|
|
policies = lazy.WindowsGPOParser.readPolicies(wrk, policies);
|
|
}
|
|
wrk.close();
|
|
return policies;
|
|
}
|
|
|
|
export let StartupOSIntegration = {
|
|
isPrivateBrowsingAllowedInRegistry() {
|
|
// If there is an attempt to open Private Browsing before
|
|
// EnterprisePolicies are initialized the Windows registry
|
|
// can be checked to determine if it is enabled
|
|
if (Services.policies.status > Ci.nsIEnterprisePolicies.UNINITIALIZED) {
|
|
// Yield to policies engine if initialized
|
|
let privateAllowed = Services.policies.isAllowed("privatebrowsing");
|
|
lazy.log.debug(
|
|
`Yield to initialized policies engine: Private Browsing Allowed = ${privateAllowed}`
|
|
);
|
|
return privateAllowed;
|
|
}
|
|
if (AppConstants.platform !== "win") {
|
|
// Not using Windows so no registry, return true
|
|
lazy.log.debug(
|
|
"AppConstants.platform is not 'win': Private Browsing allowed"
|
|
);
|
|
return true;
|
|
}
|
|
// If all other checks fail only then do we check registry
|
|
let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
|
|
Ci.nsIWindowsRegKey
|
|
);
|
|
let regLocation = "SOFTWARE\\Policies";
|
|
let userPolicies, machinePolicies;
|
|
// Only check HKEY_LOCAL_MACHINE if not in testing
|
|
if (!Cu.isInAutomation) {
|
|
machinePolicies = WindowsRegPoliciesGetter(
|
|
wrk,
|
|
wrk.ROOT_KEY_LOCAL_MACHINE,
|
|
regLocation
|
|
);
|
|
}
|
|
// Check machine policies before checking user policies
|
|
// HKEY_LOCAL_MACHINE supersedes HKEY_CURRENT_USER so only check
|
|
// HKEY_CURRENT_USER if the registry key is not present in
|
|
// HKEY_LOCAL_MACHINE at all
|
|
if (machinePolicies && "DisablePrivateBrowsing" in machinePolicies) {
|
|
lazy.log.debug(
|
|
`DisablePrivateBrowsing in HKEY_LOCAL_MACHINE is ${machinePolicies.DisablePrivateBrowsing}`
|
|
);
|
|
return !(machinePolicies.DisablePrivateBrowsing === 1);
|
|
}
|
|
userPolicies = WindowsRegPoliciesGetter(
|
|
wrk,
|
|
wrk.ROOT_KEY_CURRENT_USER,
|
|
regLocation
|
|
);
|
|
if (userPolicies && "DisablePrivateBrowsing" in userPolicies) {
|
|
lazy.log.debug(
|
|
`DisablePrivateBrowsing in HKEY_CURRENT_USER is ${userPolicies.DisablePrivateBrowsing}`
|
|
);
|
|
return !(userPolicies.DisablePrivateBrowsing === 1);
|
|
}
|
|
// Private browsing allowed if no registry entry exists
|
|
lazy.log.debug(
|
|
"No DisablePrivateBrowsing registry entry: Private Browsing allowed"
|
|
);
|
|
return true;
|
|
},
|
|
|
|
checkForLaunchOnLogin() {
|
|
// We only support launch on login on Windows at the moment.
|
|
if (AppConstants.platform != "win") {
|
|
return;
|
|
}
|
|
let launchOnLoginPref = "browser.startup.windowsLaunchOnLogin.enabled";
|
|
if (!lazy.profileService.startWithLastProfile) {
|
|
// If we don't start with last profile, the user
|
|
// likely sees the profile selector on launch.
|
|
if (Services.prefs.getBoolPref(launchOnLoginPref)) {
|
|
Glean.launchOnLogin.lastProfileDisableStartup.record();
|
|
// Disable launch on login messaging if we are disabling the
|
|
// feature.
|
|
Services.prefs.setBoolPref(
|
|
"browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
|
|
true
|
|
);
|
|
}
|
|
// To reduce confusion when running multiple Gecko profiles,
|
|
// delete launch on login shortcuts and registry keys so that
|
|
// users are not presented with the outdated profile selector
|
|
// dialog.
|
|
lazy.WindowsLaunchOnLogin.removeLaunchOnLogin();
|
|
}
|
|
},
|
|
|
|
// Note: currently only invoked on Windows and macOS.
|
|
async onStartupIdle() {
|
|
// Catch and report exceptions, including async rejections:
|
|
let safeCall = async fn => {
|
|
try {
|
|
await fn();
|
|
} catch (ex) {
|
|
console.error(ex);
|
|
}
|
|
};
|
|
// Note that we explicitly do not await calls to `safeCall` as
|
|
// these individual calls are independent and can run without
|
|
// waiting for each other.
|
|
|
|
// Currently we only support Firefox bridge on Windows and macOS.
|
|
safeCall(() => this.ensureBridgeRegistered());
|
|
|
|
if (AppConstants.platform == "win") {
|
|
if (Services.sysinfo.getProperty("hasWinPackageId")) {
|
|
safeCall(() => this.maybePinMSIXToStartMenu());
|
|
}
|
|
safeCall(() => this.ensurePrivateBrowsingShortcutExists());
|
|
}
|
|
},
|
|
|
|
async ensureBridgeRegistered() {
|
|
if (!Services.prefs.getBoolPref("browser.firefoxbridge.enabled", false)) {
|
|
return;
|
|
}
|
|
let { defaultProfile, currentProfile } = lazy.profileService;
|
|
if (defaultProfile && currentProfile == defaultProfile) {
|
|
await lazy.FirefoxBridgeExtensionUtils.ensureRegistered();
|
|
} else {
|
|
lazy.log.debug(
|
|
"FirefoxBridgeExtensionUtils failed to register due to non-default current profile."
|
|
);
|
|
}
|
|
},
|
|
|
|
// Silently pin Firefox to the start menu on first run when using MSIX on a
|
|
// new profile.
|
|
// If not first run, check if Firefox is no longer pinned to the Start Menu
|
|
// when it previously was and send telemetry.
|
|
async maybePinMSIXToStartMenu() {
|
|
if (!Services.sysinfo.getProperty("hasWinPackageId")) {
|
|
return;
|
|
}
|
|
if (
|
|
lazy.BrowserHandler.firstRunProfile &&
|
|
(await lazy.ShellService.doesAppNeedStartMenuPin())
|
|
) {
|
|
await lazy.ShellService.pinToStartMenu();
|
|
return;
|
|
}
|
|
await lazy.ShellService.recordWasPreviouslyPinnedToStartMenu();
|
|
},
|
|
|
|
// Ensure a Private Browsing Shortcut exists. This is needed in case
|
|
// a user tries to use Windows functionality to pin our Private Browsing
|
|
// mode icon to the Taskbar (eg: the "Pin to Taskbar" context menu item).
|
|
// This is also created by the installer, but it's possible that a user
|
|
// has removed it, or is running out of a zip build. The consequences of not
|
|
// having a Shortcut for this are that regular Firefox will be pinned instead
|
|
// of the Private Browsing version -- so it's quite important we do our best
|
|
// to make sure one is available.
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1762994 for additional
|
|
// background.
|
|
async ensurePrivateBrowsingShortcutExists() {
|
|
if (
|
|
// If the feature is disabled, don't do this.
|
|
!Services.prefs.getBoolPref(
|
|
"browser.privateWindowSeparation.enabled",
|
|
true
|
|
) ||
|
|
// We don't want a shortcut if it's been disabled, eg: by enterprise policy.
|
|
!lazy.PrivateBrowsingUtils.enabled ||
|
|
// Private Browsing shortcuts for packaged builds come with the package,
|
|
// if they exist at all. We shouldn't try to create our own.
|
|
Services.sysinfo.getProperty("hasWinPackageId") ||
|
|
// If we've ever done this successfully before, don't try again. The
|
|
// user may have deleted the shortcut, and we don't want to force it
|
|
// on them.
|
|
Services.prefs.getBoolPref(PREF_PRIVATE_BROWSING_SHORTCUT_CREATED, false)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
|
Ci.nsIWindowsShellService
|
|
);
|
|
let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(
|
|
Ci.nsIWinTaskbar
|
|
);
|
|
|
|
if (
|
|
!(await shellService.hasPinnableShortcut(
|
|
winTaskbar.defaultPrivateGroupId,
|
|
true
|
|
))
|
|
) {
|
|
let appdir = Services.dirsvc.get("GreD", Ci.nsIFile);
|
|
let exe = appdir.clone();
|
|
exe.append(PRIVATE_BROWSING_BINARY);
|
|
let strings = new Localization(
|
|
["branding/brand.ftl", "browser/browser.ftl"],
|
|
true
|
|
);
|
|
let [desc] = await strings.formatValues([
|
|
"private-browsing-shortcut-text-2",
|
|
]);
|
|
await shellService.createShortcut(
|
|
exe,
|
|
[],
|
|
desc,
|
|
exe,
|
|
// The code we're calling indexes from 0 instead of 1
|
|
PRIVATE_BROWSING_EXE_ICON_INDEX - 1,
|
|
winTaskbar.defaultPrivateGroupId,
|
|
"Programs",
|
|
desc + ".lnk",
|
|
appdir
|
|
);
|
|
}
|
|
// We always set this as long as no exception has been thrown. This
|
|
// ensure that it is `true` both if we created one because it didn't
|
|
// exist, or if it already existed (most likely because it was created
|
|
// by the installer). This avoids the need to call `hasPinnableShortcut`
|
|
// again, which necessarily does pointless I/O.
|
|
Services.prefs.setBoolPref(PREF_PRIVATE_BROWSING_SHORTCUT_CREATED, true);
|
|
},
|
|
};
|