Source code

Revision control

Other Tools

/* 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/. */
"use strict";
const { XPCOMUtils } = ChromeUtils.import(
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AppConstants } = ChromeUtils.import(
);
XPCOMUtils.defineLazyServiceGetters(this, {
gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
gExternalProtocolService: [
"@mozilla.org/uriloader/external-protocol-service;1",
"nsIExternalProtocolService",
],
gHandlerService: [
"@mozilla.org/uriloader/handler-service;1",
"nsIHandlerService",
],
gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"],
gXulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
});
XPCOMUtils.defineLazyModuleGetters(this, {
});
XPCOMUtils.defineLazyGlobalGetters(this, ["File", "FileReader"]);
const PREF_LOGLEVEL = "browser.policies.loglevel";
const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
const ABOUT_CONTRACT = "@mozilla.org/network/protocol/about;1?what=";
let env = Cc["@mozilla.org/process/environment;1"].getService(
Ci.nsIEnvironment
);
const isXpcshell = env.exists("XPCSHELL_TEST_PROFILE_DIR");
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
return new ConsoleAPI({
prefix: "Policies.jsm",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
var EXPORTED_SYMBOLS = ["Policies"];
/*
* ============================
* = POLICIES IMPLEMENTATIONS =
* ============================
*
* The Policies object below is where the implementation for each policy
* happens. An object for each policy should be defined, containing
* callback functions that will be called by the engine.
*
* See the _callbacks object in EnterprisePolicies.js for the list of
* possible callbacks and an explanation of each.
*
* Each callback will be called with two parameters:
* - manager
* This is the EnterprisePoliciesManager singleton object from
* EnterprisePolicies.js
*
* - param
* The parameter defined for this policy in policies-schema.json.
* It will be different for each policy. It could be a boolean,
* a string, an array or a complex object. All parameters have
* been validated according to the schema, and no unknown
* properties will be present on them.
*
* The callbacks will be bound to their parent policy object.
*/
var Policies = {
// Used for cleaning up policies.
// Use the same timing that you used for setting up the policy.
_cleanup: {
onBeforeAddons(manager) {
if (Cu.isInAutomation || isXpcshell) {
console.log("_cleanup from onBeforeAddons");
clearBlockedAboutPages();
}
},
onProfileAfterChange(manager) {
if (Cu.isInAutomation || isXpcshell) {
console.log("_cleanup from onProfileAfterChange");
}
},
onBeforeUIStartup(manager) {
if (Cu.isInAutomation || isXpcshell) {
console.log("_cleanup from onBeforeUIStartup");
}
},
onAllWindowsRestored(manager) {
if (Cu.isInAutomation || isXpcshell) {
console.log("_cleanup from onAllWindowsRestored");
}
},
},
"3rdparty": {
onBeforeAddons(manager, param) {
manager.setExtensionPolicies(param.Extensions);
},
},
AppAutoUpdate: {
onBeforeUIStartup(manager, param) {
// Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is
// true, we disallow turning off auto updating, and visa versa.
if (param) {
manager.disallowFeature("app-auto-updates-off");
} else {
manager.disallowFeature("app-auto-updates-on");
}
},
},
AppUpdateURL: {
// No implementation needed here. UpdateService.jsm will check for this
// policy directly when determining the update URL.
},
Authentication: {
onBeforeAddons(manager, param) {
let locked = true;
if ("Locked" in param) {
locked = param.Locked;
}
if ("SPNEGO" in param) {
setDefaultPref(
"network.negotiate-auth.trusted-uris",
param.SPNEGO.join(", "),
locked
);
}
if ("Delegated" in param) {
setDefaultPref(
"network.negotiate-auth.delegation-uris",
param.Delegated.join(", "),
locked
);
}
if ("NTLM" in param) {
setDefaultPref(
"network.automatic-ntlm-auth.trusted-uris",
param.NTLM.join(", "),
locked
);
}
if ("AllowNonFQDN" in param) {
if ("NTLM" in param.AllowNonFQDN) {
setDefaultPref(
"network.automatic-ntlm-auth.allow-non-fqdn",
param.AllowNonFQDN.NTLM,
locked
);
}
if ("SPNEGO" in param.AllowNonFQDN) {
setDefaultPref(
"network.negotiate-auth.allow-non-fqdn",
param.AllowNonFQDN.SPNEGO,
locked
);
}
}
if ("AllowProxies" in param) {
if ("NTLM" in param.AllowProxies) {
setDefaultPref(
"network.automatic-ntlm-auth.allow-proxies",
param.AllowProxies.NTLM,
locked
);
}
if ("SPNEGO" in param.AllowProxies) {
setDefaultPref(
"network.negotiate-auth.allow-proxies",
param.AllowProxies.SPNEGO,
locked
);
}
}
if ("PrivateBrowsing" in param) {
setDefaultPref(
"network.auth.private-browsing-sso",
param.PrivateBrowsing,
locked
);
}
},
},
BlockAboutAddons: {
onBeforeUIStartup(manager, param) {
if (param) {
blockAboutPage(manager, "about:addons", true);
}
},
},
BlockAboutConfig: {
onBeforeUIStartup(manager, param) {
if (param) {
blockAboutPage(manager, "about:config");
setAndLockPref("devtools.chrome.enabled", false);
}
},
},
BlockAboutProfiles: {
onBeforeUIStartup(manager, param) {
if (param) {
blockAboutPage(manager, "about:profiles");
}
},
},
BlockAboutSupport: {
onBeforeUIStartup(manager, param) {
if (param) {
blockAboutPage(manager, "about:support");
}
},
},
Bookmarks: {
onAllWindowsRestored(manager, param) {
BookmarksPolicies.processBookmarks(param);
},
},
CaptivePortal: {
onBeforeAddons(manager, param) {
setAndLockPref("network.captive-portal-service.enabled", param);
},
},
Certificates: {
onBeforeAddons(manager, param) {
if ("ImportEnterpriseRoots" in param) {
setAndLockPref(
"security.enterprise_roots.enabled",
param.ImportEnterpriseRoots
);
}
if ("Install" in param) {
(async () => {
let dirs = [];
let platform = AppConstants.platform;
if (platform == "win") {
dirs = [
// Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
// Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
];
} else if (platform == "macosx" || platform == "linux") {
dirs = [
// These two keys are named wrong. They return the Mozilla directory.
Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile),
Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile),
];
}
dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
for (let certfilename of param.Install) {
let certfile;
try {
certfile = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
);
certfile.initWithPath(certfilename);
} catch (e) {
for (let dir of dirs) {
certfile = dir.clone();
certfile.append(
platform == "linux" ? "certificates" : "Certificates"
);
certfile.append(certfilename);
if (certfile.exists()) {
break;
}
}
}
let file;
try {
file = await File.createFromNsIFile(certfile);
} catch (e) {
log.error(`Unable to find certificate - ${certfilename}`);
continue;
}
let reader = new FileReader();
reader.onloadend = function() {
if (reader.readyState != reader.DONE) {
log.error(`Unable to read certificate - ${certfile.path}`);
return;
}
let certFile = reader.result;
let certFileArray = [];
for (let i = 0; i < certFile.length; i++) {
certFileArray.push(certFile.charCodeAt(i));
}
let cert;
try {
cert = gCertDB.constructX509(certFileArray);
} catch (e) {
log.debug(
`constructX509 failed with error '${e}' - trying constructX509FromBase64.`
);
try {
// It might be PEM instead of DER.
cert = gCertDB.constructX509FromBase64(pemToBase64(certFile));
} catch (ex) {
log.error(`Unable to add certificate - ${certfile.path}`, ex);
}
}
if (cert) {
if (
gCertDB.isCertTrusted(
cert,
Ci.nsIX509Cert.CA_CERT,
Ci.nsIX509CertDB.TRUSTED_SSL
)
) {
// Certificate is already installed.
return;
}
try {
gCertDB.addCert(certFile, "CT,CT,");
} catch (e) {
// It might be PEM instead of DER.
gCertDB.addCertFromBase64(pemToBase64(certFile), "CT,CT,");
}
}
};
reader.readAsBinaryString(file);
}
})();
}
},
},
Cookies: {
onBeforeUIStartup(manager, param) {
addAllowDenyPermissions("cookie", param.Allow, param.Block);
if (param.AllowSession) {
for (let origin of param.AllowSession) {
try {
Services.perms.addFromPrincipal(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
origin
),
"cookie",
Ci.nsICookiePermission.ACCESS_SESSION,
Ci.nsIPermissionManager.EXPIRE_POLICY
);
} catch (ex) {
log.error(
`Unable to add cookie session permission - ${origin.href}`
);
}
}
}
if (param.Block) {
const hosts = param.Block.map(url => url.hostname)
.sort()
.join("\n");
runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
for (let blocked of param.Block) {
Services.cookies.removeCookiesWithOriginAttributes(
"{}",
blocked.hostname
);
}
});
}
if (
param.Default !== undefined ||
param.AcceptThirdParty !== undefined ||
param.RejectTracker !== undefined ||
param.Locked
) {
const ACCEPT_COOKIES = 0;
const REJECT_THIRD_PARTY_COOKIES = 1;
const REJECT_ALL_COOKIES = 2;
const REJECT_UNVISITED_THIRD_PARTY = 3;
const REJECT_TRACKER = 4;
let newCookieBehavior = ACCEPT_COOKIES;
if (param.Default !== undefined && !param.Default) {
newCookieBehavior = REJECT_ALL_COOKIES;
} else if (param.AcceptThirdParty) {
if (param.AcceptThirdParty == "never") {
newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
} else if (param.AcceptThirdParty == "from-visited") {
newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
}
} else if (param.RejectTracker !== undefined && param.RejectTracker) {
newCookieBehavior = REJECT_TRACKER;
}
setDefaultPref(
"network.cookie.cookieBehavior",
newCookieBehavior,
param.Locked
);
}
const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;
if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
if (param.ExpireAtSessionEnd) {
newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
}
setDefaultPref(
"network.cookie.lifetimePolicy",
newLifetimePolicy,
param.Locked
);
}
},
},
DefaultDownloadDirectory: {
onBeforeAddons(manager, param) {
setDefaultPref("browser.download.dir", replacePathVariables(param));
// If a custom download directory is being used, just lock folder list to 2.
setAndLockPref("browser.download.folderList", 2);
},
},
DisableAppUpdate: {
onBeforeAddons(manager, param) {
if (param) {
manager.disallowFeature("appUpdate");
}
},
},
DisableBuiltinPDFViewer: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("pdfjs.disabled", true);
}
},
},
DisabledCiphers: {
onBeforeAddons(manager, param) {
if ("TLS_DHE_RSA_WITH_AES_128_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.dhe_rsa_aes_128_sha",
!param.TLS_DHE_RSA_WITH_AES_128_CBC_SHA
);
}
if ("TLS_DHE_RSA_WITH_AES_256_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.dhe_rsa_aes_256_sha",
!param.TLS_DHE_RSA_WITH_AES_256_CBC_SHA
);
}
if ("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.ecdhe_rsa_aes_128_sha",
!param.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
);
}
if ("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.ecdhe_rsa_aes_256_sha",
!param.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
);
}
if ("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" in param) {
setAndLockPref(
"security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
!param.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
);
}
if ("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" in param) {
setAndLockPref(
"security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
!param.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
);
}
if ("TLS_RSA_WITH_AES_128_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.rsa_aes_128_sha",
!param.TLS_RSA_WITH_AES_128_CBC_SHA
);
}
if ("TLS_RSA_WITH_AES_256_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.rsa_aes_256_sha",
!param.TLS_RSA_WITH_AES_256_CBC_SHA
);
}
if ("TLS_RSA_WITH_3DES_EDE_CBC_SHA" in param) {
setAndLockPref(
"security.ssl3.rsa_des_ede3_sha",
!param.TLS_RSA_WITH_3DES_EDE_CBC_SHA
);
}
if ("TLS_RSA_WITH_AES_128_GCM_SHA256" in param) {
setAndLockPref(
"security.ssl3.rsa_aes_128_gcm_sha256",
!param.TLS_RSA_WITH_AES_128_GCM_SHA256
);
}
if ("TLS_RSA_WITH_AES_256_GCM_SHA384" in param) {
setAndLockPref(
"security.ssl3.rsa_aes_256_gcm_sha384",
!param.TLS_RSA_WITH_AES_256_GCM_SHA384
);
}
},
},
DisableDefaultBrowserAgent: {
// The implementation of this policy is in the default browser agent itself
// (/toolkit/mozapps/defaultagent); we need an entry for it here so that it
// shows up in about:policies as a real policy and not as an error.
},
DisableDeveloperTools: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("devtools.policy.disabled", true);
setAndLockPref("devtools.chrome.enabled", false);
manager.disallowFeature("devtools");
blockAboutPage(manager, "about:devtools");
blockAboutPage(manager, "about:debugging");
blockAboutPage(manager, "about:devtools-toolbox");
blockAboutPage(manager, "about:profiling");
}
},
},
DisableFeedbackCommands: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("feedbackCommands");
}
},
},
DisableFirefoxAccounts: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("identity.fxaccounts.enabled", false);
setAndLockPref("browser.aboutwelcome.enabled", false);
}
},
},
DisableFirefoxScreenshots: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("extensions.screenshots.disabled", true);
}
},
},
DisableFirefoxStudies: {
onBeforeAddons(manager, param) {
if (param) {
manager.disallowFeature("Shield");
setAndLockPref(
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
false
);
setAndLockPref(
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
false
);
}
},
},
DisableForgetButton: {
onProfileAfterChange(manager, param) {
if (param) {
setAndLockPref("privacy.panicButton.enabled", false);
}
},
},
DisableFormHistory: {
onBeforeUIStartup(manager, param) {
if (param) {
setAndLockPref("browser.formfill.enable", false);
}
},
},
DisableMasterPasswordCreation: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("createMasterPassword");
}
},
},
DisablePasswordReveal: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("passwordReveal");
}
},
},
DisablePocket: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("extensions.pocket.enabled", false);
}
},
},
DisablePrivateBrowsing: {
onBeforeAddons(manager, param) {
if (param) {
manager.disallowFeature("privatebrowsing");
blockAboutPage(manager, "about:privatebrowsing", true);
setAndLockPref("browser.privatebrowsing.autostart", false);
}
},
},
DisableProfileImport: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("profileImport");
setAndLockPref(
"browser.newtabpage.activity-stream.migrationExpired",
true
);
}
},
},
DisableProfileRefresh: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("profileRefresh");
setAndLockPref("browser.disableResetPrompt", true);
}
},
},
DisableSafeMode: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("safeMode");
}
},
},
DisableSecurityBypass: {
onBeforeUIStartup(manager, param) {
if ("InvalidCertificate" in param) {
setAndLockPref(
"security.certerror.hideAddException",
param.InvalidCertificate
);
}
if ("SafeBrowsing" in param) {
setAndLockPref(
"browser.safebrowsing.allowOverride",
!param.SafeBrowsing
);
}
},
},
DisableSetDesktopBackground: {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("setDesktopBackground");
}
},
},
DisableSystemAddonUpdate: {
onBeforeAddons(manager, param) {
if (param) {
manager.disallowFeature("SysAddonUpdate");
}
},
},
DisableTelemetry: {
onBeforeAddons(manager, param) {
if (param) {
setAndLockPref("datareporting.healthreport.uploadEnabled", false);
setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
setAndLockPref("toolkit.telemetry.archive.enabled", false);
blockAboutPage(manager, "about:telemetry");
}
},
},
DisplayBookmarksToolbar: {
onBeforeUIStartup(manager, param) {
let value = (!param).toString();
// This policy is meant to change the default behavior, not to force it.
// If this policy was alreay applied and the user chose to re-hide the
// bookmarks toolbar, do not show it again.
runOncePerModification("displayBookmarksToolbar", value, () => {
// Set the preference to keep the bookmarks bar open and also
// declaratively open the bookmarks toolbar. Otherwise, default
// to showing it on the New Tab Page.
let visibilityPref = "browser.toolbars.bookmarks.visibility";
let bookmarksFeaturePref = "browser.toolbars.bookmarks.2h2020";
let visibility = param ? "always" : "never";
if (Services.prefs.getBoolPref(bookmarksFeaturePref, false)) {
visibility = param ? "always" : "newtab";
}
Services.prefs.setCharPref(visibilityPref, visibility);
gXulStore.setValue(
BROWSER_DOCUMENT_URL,
"PersonalToolbar",
"collapsed",
value
);
});
},
},
DisplayMenuBar: {
onBeforeUIStartup(manager, param) {
let value;
if (
typeof param === "boolean" ||
param == "default-on" ||
param == "default-off"
) {
switch (param) {
case "default-on":
value = "false";
break;
case "default-off":
value = "true";
break;
default:
value = (!param).toString();
break;
}
// This policy is meant to change the default behavior, not to force it.
// If this policy was already applied and the user chose to re-hide the
// menu bar, do not show it again.
runOncePerModification("displayMenuBar", value, () => {
gXulStore.setValue(
BROWSER_DOCUMENT_URL,
"toolbar-menubar",
"autohide",
value
);
});
} else {
switch (param) {
case "always":
value = "false";
break;
case "never":
// Make sure Alt key doesn't show the menubar
setAndLockPref("ui.key.menuAccessKeyFocuses", false);
value = "true";
break;
}
gXulStore.setValue(
BROWSER_DOCUMENT_URL,
"toolbar-menubar",
"autohide",
value
);
manager.disallowFeature("hideShowMenuBar");
}
},
},
DNSOverHTTPS: {
onBeforeAddons(manager, param) {
let locked = false;
if ("Locked" in param) {
locked = param.Locked;
}
if ("Enabled" in param) {
let mode = param.Enabled ? 2 : 5;
setDefaultPref("network.trr.mode", mode, locked);
}
if ("ProviderURL" in param) {
setDefaultPref("network.trr.uri", param.ProviderURL.href, locked);
}
if ("ExcludedDomains" in param) {
setDefaultPref(
"network.trr.excluded-domains",
param.ExcludedDomains.join(","),
locked
);
}
},
},
DontCheckDefaultBrowser: {
onBeforeUIStartup(manager, param) {
setAndLockPref("browser.shell.checkDefaultBrowser", !param);
},
},
DownloadDirectory: {
onBeforeAddons(manager, param) {
setAndLockPref("browser.download.dir", replacePathVariables(param));
// If a custom download directory is being used, just lock folder list to 2.
setAndLockPref("browser.download.folderList", 2);
// Per Chrome spec, user can't choose to download every time
// if this is set.
setAndLockPref("browser.download.useDownloadDir", true);
},
},
EnableTrackingProtection: {
onBeforeUIStartup(manager, param) {
if (param.Value) {
setDefaultPref(
"privacy.trackingprotection.enabled",
true,
param.Locked
);
setDefaultPref(
"privacy.trackingprotection.pbmode.enabled",
true,
param.Locked
);
} else {
setAndLockPref("privacy.trackingprotection.enabled", false);
setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
}
if ("Cryptomining" in param) {
setDefaultPref(
"privacy.trackingprotection.cryptomining.enabled",
param.Cryptomining,
param.Locked
);
}
if ("Fingerprinting" in param) {
setDefaultPref(
"privacy.trackingprotection.fingerprinting.enabled",
param.Fingerprinting,
param.Locked
);
}
if ("Exceptions" in param) {
addAllowDenyPermissions("trackingprotection", param.Exceptions);
}
},
},
EncryptedMediaExtensions: {
onBeforeAddons(manager, param) {
let locked = false;
if ("Locked" in param) {
locked = param.Locked;
}
if ("Enabled" in param) {
setDefaultPref("media.eme.enabled", param.Enabled, locked);
}
},
},
Extensions: {
onBeforeUIStartup(manager, param) {
let uninstallingPromise = Promise.resolve();
if ("Uninstall" in param) {
uninstallingPromise = runOncePerModification(
"extensionsUninstall",
JSON.stringify(param.Uninstall),
async () => {
// If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
// changed, which will allow add-ons to be updated.
Services.prefs.clearUserPref(
"browser.policies.runOncePerModification.extensionsInstall"
);
let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
for (let addon of addons) {
if (addon) {
try {
await addon.uninstall();
} catch (e) {
// This can fail for add-ons that can't be uninstalled.
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
}
}
}
}
);
}
if ("Install" in param) {
runOncePerModification(
"extensionsInstall",
JSON.stringify(param.Install),
async () => {
await uninstallingPromise;
for (let location of param.Install) {
let uri;
try {
// We need to try as a file first because
// Windows paths are valid URIs.
// This is done for legacy support (old API)
let xpiFile = new FileUtils.File(location);
uri = Services.io.newFileURI(xpiFile);
} catch (e) {
uri = Services.io.newURI(location);
}
installAddonFromURL(uri.spec);
}
}
);
}
if ("Locked" in param) {
for (let ID of param.Locked) {
manager.disallowFeature(`uninstall-extension:${ID}`);
manager.disallowFeature(`disable-extension:${ID}`);
}
}
},
},
ExtensionSettings: {
onBeforeAddons(manager, param) {
try {
manager.setExtensionSettings(param);
} catch (e) {
log.error("Invalid ExtensionSettings");
}
},
async onBeforeUIStartup(manager, param) {
let extensionSettings = param;
let blockAllExtensions = false;
if ("*" in extensionSettings) {
if (
"installation_mode" in extensionSettings["*"] &&
extensionSettings["*"].installation_mode == "blocked"
) {
blockAllExtensions = true;
// Turn off discovery pane in about:addons
setAndLockPref("extensions.getAddons.showPane", false);
// Turn off recommendations
setAndLockPref(
"extensions.htmlaboutaddons.recommendations.enable",
false
);
// Block about:debugging
blockAboutPage(manager, "about:debugging");
}
if ("restricted_domains" in extensionSettings["*"]) {
let restrictedDomains = Services.prefs
.getCharPref("extensions.webextensions.restrictedDomains")
.split(",");
setAndLockPref(
"extensions.webextensions.restrictedDomains",
restrictedDomains
.concat(extensionSettings["*"].restricted_domains)
.join(",")
);
}
}
let addons = await AddonManager.getAllAddons();
let allowedExtensions = [];
for (let extensionID in extensionSettings) {
if (extensionID == "*") {
// Ignore global settings
continue;
}
if ("installation_mode" in extensionSettings[extensionID]) {
if (
extensionSettings[extensionID].installation_mode ==
"force_installed" ||
extensionSettings[extensionID].installation_mode ==
"normal_installed"
) {
if (!extensionSettings[extensionID].install_url) {
throw new Error(`Missing install_url for ${extensionID}`);
}
installAddonFromURL(
extensionSettings[extensionID].install_url,
extensionID,
addons.find(addon => addon.id == extensionID)
);
manager.disallowFeature(`uninstall-extension:${extensionID}`);
if (
extensionSettings[extensionID].installation_mode ==
"force_installed"
) {
manager.disallowFeature(`disable-extension:${extensionID}`);
}
allowedExtensions.push(extensionID);
} else if (
extensionSettings[extensionID].installation_mode == "allowed"
) {
allowedExtensions.push(extensionID);
} else if (
extensionSettings[extensionID].installation_mode == "blocked"
) {
if (addons.find(addon => addon.id == extensionID)) {
// Can't use the addon from getActiveAddons since it doesn't have uninstall.
let addon = await AddonManager.getAddonByID(extensionID);
try {
await addon.uninstall();
} catch (e) {
// This can fail for add-ons that can't be uninstalled.
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
}
}
}
}
}
if (blockAllExtensions) {
for (let addon of addons) {
if (
addon.isSystem ||
addon.isBuiltin ||
!(addon.scope & AddonManager.SCOPE_PROFILE)
) {
continue;
}
if (!allowedExtensions.includes(addon.id)) {
try {
// Can't use the addon from getActiveAddons since it doesn't have uninstall.
let addonToUninstall = await AddonManager.getAddonByID(addon.id);
await addonToUninstall.uninstall();
} catch (e) {
// This can fail for add-ons that can't be uninstalled.
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
}
}
}
}
},
},
ExtensionUpdate: {
onBeforeAddons(manager, param) {
if (!param) {
setAndLockPref("extensions.update.enabled", param);
}
},
},
FirefoxHome: {
onBeforeAddons(manager, param) {
let locked = param.Locked || false;
if ("Search" in param) {
setDefaultPref(
"browser.newtabpage.activity-stream.showSearch",
param.Search,
locked
);
}
if ("TopSites" in param) {
setDefaultPref(
"browser.newtabpage.activity-stream.feeds.topsites",
param.TopSites,
locked
);
}
if ("Highlights" in param) {
setDefaultPref(
"browser.newtabpage.activity-stream.feeds.section.highlights",
param.Highlights,
locked
);
}
if ("Pocket" in param) {
setDefaultPref(
"browser.newtabpage.activity-stream.feeds.system.topstories",
param.Pocket,
locked
);
}
if ("Snippets" in param) {
setDefaultPref(
"browser.newtabpage.activity-stream.feeds.snippets",
param.Snippets,
locked
);
}
},
},
FlashPlugin: {
onBeforeUIStartup(manager, param) {
addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
const FLASH_NEVER_ACTIVATE = 0;
const FLASH_ASK_TO_ACTIVATE = 1;
let flashPrefVal;
if (param.Default === undefined || param.Default) {
flashPrefVal = FLASH_ASK_TO_ACTIVATE;
} else {
flashPrefVal = FLASH_NEVER_ACTIVATE;
}
if (param.Locked) {
setAndLockPref("plugin.state.flash", flashPrefVal);
} else if (param.Default !== undefined) {
setDefaultPref("plugin.state.flash", flashPrefVal);
}
},
},
Handlers: {
onBeforeAddons(manager, param) {
if ("mimeTypes" in param) {
for (let mimeType in param.mimeTypes) {
let mimeInfo = param.mimeTypes[mimeType];
let realMIMEInfo = gMIMEService.getFromTypeAndExtension(mimeType, "");
processMIMEInfo(mimeInfo, realMIMEInfo);
}
}
if ("extensions" in param) {
for (let extension in param.extensions) {
let mimeInfo = param.extensions[extension];
try {
let realMIMEInfo = gMIMEService.getFromTypeAndExtension(
"",
extension
);
processMIMEInfo(mimeInfo, realMIMEInfo);
} catch (e) {
log.error(`Invalid file extension (${extension})`);
}
}
}
if ("schemes" in param) {
for (let scheme in param.schemes) {
let handlerInfo = param.schemes[scheme];
let realHandlerInfo = gExternalProtocolService.getProtocolHandlerInfo(
scheme
);
processMIMEInfo(handlerInfo, realHandlerInfo);
}
}
},
},
HardwareAcceleration: {
onBeforeAddons(manager, param) {
if (!param) {
setAndLockPref("layers.acceleration.disabled", true);
}
},
},
Homepage: {
onBeforeUIStartup(manager, param) {
if ("StartPage" in param && param.StartPage == "none") {
// For blank startpage, we use about:blank rather
// than messing with browser.startup.page
param.URL = new URL("about:blank");
}
// |homepages| will be a string containing a pipe-separated ('|') list of
// URLs because that is what the "Home page" section of about:preferences
// (and therefore what the pref |browser.startup.homepage|) accepts.
if ("URL" in param) {
let homepages = param.URL.href;
if (param.Additional && param.Additional.length) {
homepages += "|" + param.Additional.map(url => url.href).join("|");
}
setDefaultPref("browser.startup.homepage", homepages, param.Locked);
if (param.Locked) {
setAndLockPref(
"pref.browser.homepage.disable_button.current_page",
true
);
setAndLockPref(
"pref.browser.homepage.disable_button.bookmark_page",
true
);
setAndLockPref(
"pref.browser.homepage.disable_button.restore_default",
true
);
} else {
// Clear out old run once modification that is no longer used.
clearRunOnceModification("setHomepage");
}
}
if (param.StartPage) {
let prefValue;
switch (param.StartPage) {
case "homepage":
case "homepage-locked":
case "none":
prefValue = 1;
break;
case "previous-session":
prefValue = 3;
break;
}
setDefaultPref(
"browser.startup.page",
prefValue,
param.StartPage == "homepage-locked"
);
}
},
},
InstallAddonsPermission: {
onBeforeUIStartup(manager, param) {
if ("Allow" in param) {
addAllowDenyPermissions("install", param.Allow, null);
}
if ("Default" in param) {
setAndLockPref("xpinstall.enabled", param.Default);
if (!param.Default) {
blockAboutPage(manager, "about:debugging");
setAndLockPref(
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
false
);
setAndLockPref(
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
false
);
manager.disallowFeature("xpinstall");
}
}
},
},
LegacyProfiles: {
// Handled in nsToolkitProfileService.cpp (Windows only)
},
LegacySameSiteCookieBehaviorEnabled: {
onBeforeAddons(manager, param) {
setDefaultPref("network.cookie.sameSite.laxByDefault", !param);
},
},
LegacySameSiteCookieBehaviorEnabledForDomainList: {
onBeforeAddons(manager, param) {
setDefaultPref(
"network.cookie.sameSite.laxByDefault.disabledHosts",
param.join(",")
);
},
},
LocalFileLinks: {
onBeforeAddons(manager, param) {
// If there are existing capabilities, lock them with the policy pref.
let policyNames = Services.prefs
.getCharPref("capability.policy.policynames", "")
.split(" ");
policyNames.push("localfilelinks_policy");
setAndLockPref("capability.policy.policynames", policyNames.join(" "));
setAndLockPref(
"capability.policy.localfilelinks_policy.checkloaduri.enabled",
"allAccess"
);
setAndLockPref(
"capability.policy.localfilelinks_policy.sites",
param.join(" ")
);
},
},
ManagedBookmarks: {},
NetworkPrediction: {
onBeforeAddons(manager, param) {
setAndLockPref("network.dns.disablePrefetch", !param);
setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
},
},
NewTabPage: {
onBeforeAddons(manager, param) {
setAndLockPref("browser.newtabpage.enabled", param);
},
},
NoDefaultBookmarks: {
onProfileAfterChange(manager, param) {
if (param) {
manager.disallowFeature("defaultBookmarks");
}
},
},
OfferToSaveLogins: {
onBeforeUIStartup(manager, param) {
setAndLockPref("signon.rememberSignons", param);
},
},
OfferToSaveLoginsDefault: {
onBeforeUIStartup(manager, param) {
let policies = Services.policies.getActivePolicies();
if ("OfferToSaveLogins" in policies) {
log.error(
`OfferToSaveLoginsDefault ignored because OfferToSaveLogins is present.`
);
} else {
setDefaultPref("signon.rememberSignons", param);
}
},
},
OverrideFirstRunPage: {
onProfileAfterChange(manager, param) {
let url = param ? param : "";
setAndLockPref("startup.homepage_welcome_url", url);
setAndLockPref("browser.aboutwelcome.enabled", false);
},
},
OverridePostUpdatePage: {
onProfileAfterChange(manager, param) {
let url = param ? param.href : "";
setAndLockPref("startup.homepage_override_url", url);
// The pref startup.homepage_override_url is only used
// as a fallback when the update.xml file hasn't provided
// a specific post-update URL.
manager.disallowFeature("postUpdateCustomPage");
},
},
PasswordManagerEnabled: {
onBeforeUIStartup(manager, param) {
if (!param) {
blockAboutPage(manager, "about:logins", true);
setAndLockPref("pref.privacy.disable_button.view_passwords", true);
}
setAndLockPref("signon.rememberSignons", param);
},
},
PDFjs: {
onBeforeAddons(manager, param) {
if ("Enabled" in param) {
setAndLockPref("pdfjs.disabled", !param.Enabled);
}
if ("EnablePermissions" in param) {
setAndLockPref("pdfjs.enablePermissions", !param.Enabled);
}
},
},
Permissions: {
onBeforeUIStartup(manager, param) {
if (param.Camera) {
addAllowDenyPermissions(
"camera",
param.Camera.Allow,
param.Camera.Block
);
setDefaultPermission("camera", param.Camera);
}
if (param.Microphone) {
addAllowDenyPermissions(
"microphone",
param.Microphone.Allow,
param.Microphone.Block
);
setDefaultPermission("microphone", param.Microphone);
}
if (param.Autoplay) {
addAllowDenyPermissions(
"autoplay-media",
param.Autoplay.Allow,
param.Autoplay.Block
);
if ("Default" in param.Autoplay) {
let prefValue;
switch (param.Autoplay.Default) {
case "allow-audio-video":
prefValue = 0;
break;
case "block-audio":
prefValue = 1;
break;
case "block-audio-video":
prefValue = 5;
break;
}
setDefaultPref(
"media.autoplay.default",
prefValue,
param.Autoplay.Locked
);
}
}
if (param.Location) {
addAllowDenyPermissions(
"geo",
param.Location.Allow,
param.Location.Block
);
setDefaultPermission("geo", param.Location);
}
if (param.Notifications) {
addAllowDenyPermissions(
"desktop-notification",
param.Notifications.Allow,
param.Notifications.Block
);
setDefaultPermission("desktop-notification", param.Notifications);
}
if ("VirtualReality" in param) {
addAllowDenyPermissions(
"xr",
param.VirtualReality.Allow,
param.VirtualReality.Block
);
setDefaultPermission("xr", param.VirtualReality);
}
},
},
PictureInPicture: {
onBeforeAddons(manager, param) {
if ("Enabled" in param) {
setDefaultPref(
"media.videocontrols.picture-in-picture.video-toggle.enabled",
param.Enabled
);
}
if (param.Locked) {
Services.prefs.lockPref(
"media.videocontrols.picture-in-picture.video-toggle.enabled"
);
}
},
},
PopupBlocking: {
onBeforeUIStartup(manager, param) {
addAllowDenyPermissions("popup", param.Allow, null);
if (param.Locked) {
let blockValue = true;
if (param.Default !== undefined && !param.Default) {
blockValue = false;
}
setAndLockPref("dom.disable_open_during_load", blockValue);
} else if (param.Default !== undefined) {
setDefaultPref("dom.disable_open_during_load", !!param.Default);
}
},
},
Preferences: {
onBeforeAddons(manager, param) {
const allowedPrefixes = [
"accessibility.",
"browser.",
"datareporting.policy.",
"dom.",
"extensions.",
"general.autoScroll",
"general.smoothScroll",
"geo.",
"intl.",
"layout.",
"media.",
"network.",
"places.",
"print.",
"signon.",
"ui.",
"widget.",
];
const allowedSecurityPrefs = [
"security.default_personal_cert",
"security.insecure_connection_text.enabled",
"security.insecure_connection_text.pbmode.enabled",
"security.insecure_field_warning.contextual.enabled",
"security.mixed_content.block_active_content",
"security.osclientcerts.autoload",
"security.ssl.errorReporting.enabled",
"security.tls.hello_downgrade_check",
"security.warn_submit_secure_to_insecure",
];
const blockedPrefs = [];
for (let preference in param) {
if (blockedPrefs.includes(preference)) {
log.error(
`Unable to set preference ${preference}. Preference not allowed for security reasons.`
);
continue;
}
if (preference.startsWith("security.")) {
if (!allowedSecurityPrefs.includes(preference)) {
log.error(
`Unable to set preference ${preference}. Preference not allowed for security reasons.`
);
continue;
}
} else if (
!allowedPrefixes.some(prefix => preference.startsWith(prefix))
) {
log.error(
`Unable to set preference ${preference}. Preference not allowed for stability reasons.`
);
continue;
}
if (typeof param[preference] != "object") {
// Legacy policy preferences
setAndLockPref(preference, param[preference]);
} else {
if (param[preference].Status == "clear") {
Services.prefs.clearUserPref(preference);
continue;
}
if (param[preference].Status == "user") {
var prefBranch = Services.prefs;
} else {
prefBranch = Services.prefs.getDefaultBranch("");
}
try {
switch (typeof param[preference].Value) {
case "boolean":
prefBranch.setBoolPref(preference, param[preference].Value);
break;
case "number":
if (!Number.isInteger(param[preference].Value)) {
throw new Error(`Non-integer value for ${preference}`);
}
// This is ugly, but necessary. On Windows GPO and macOS
// configs, booleans are converted to 0/1. In the previous
// Preferences implementation, the schema took care of
// automatically converting these values to booleans.
// Since we allow arbitrary prefs now, we have to do
// something different. See bug 1666836.
if (
prefBranch.getPrefType(preference) == prefBranch.PREF_INT ||
![0, 1].includes(param[preference].Value)
) {
prefBranch.setIntPref(preference, param[preference].Value);
} else {
prefBranch.setBoolPref(preference, !!param[preference].Value);
}
break;
case "string":
prefBranch.setStringPref(preference, param[preference].Value);
break;
}
} catch (e) {
log.error(
`Unable to set preference ${preference}. Probable type mismatch.`
);
}
if (param[preference].Status == "locked") {
Services.prefs.lockPref(preference);
}
}
}
},
},
PrimaryPassword: {
onAllWindowsRestored(manager, param) {
if (param) {
manager.disallowFeature("removeMasterPassword");
} else {
manager.disallowFeature("createMasterPassword");
}
},
},
PromptForDownloadLocation: {
onBeforeAddons(manager, param) {
setAndLockPref("browser.download.useDownloadDir", !param);
},
},
Proxy: {
onBeforeAddons(manager, param) {
if (param.Locked) {
manager.disallowFeature("changeProxySettings");
ProxyPolicies.configureProxySettings(param, setAndLockPref);
} else {
ProxyPolicies.configureProxySettings(param, setDefaultPref);
}
},
},
RequestedLocales: {
onBeforeAddons(manager, param) {
let requestedLocales;
if (Array.isArray(param)) {
requestedLocales = param;
} else if (param) {
requestedLocales = param.split(",");
} else {
requestedLocales = [];
}
runOncePerModification(
"requestedLocales",
JSON.stringify(requestedLocales),
() => {
Services.locale.requestedLocales = requestedLocales;
}
);
},
},
SanitizeOnShutdown: {
onBeforeUIStartup(manager, param) {
if (typeof param === "boolean") {
setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param);
if (param) {
setAndLockPref("privacy.clearOnShutdown.cache", true);
setAndLockPref("privacy.clearOnShutdown.cookies", true);
setAndLockPref("privacy.clearOnShutdown.downloads", true);
setAndLockPref("privacy.clearOnShutdown.formdata", true);
setAndLockPref("privacy.clearOnShutdown.history", true);
setAndLockPref("privacy.clearOnShutdown.sessions", true);
setAndLockPref("privacy.clearOnShutdown.siteSettings", true);
setAndLockPref("privacy.clearOnShutdown.offlineApps", true);
}
} else {
let locked = true;
// Needed to preserve original behavior in perpetuity.
let lockDefaultPrefs = true;
if ("Locked" in param) {
locked = param.Locked;
lockDefaultPrefs = false;
}
setDefaultPref("privacy.sanitize.sanitizeOnShutdown", true, locked);
if ("Cache" in param) {
setDefaultPref("privacy.clearOnShutdown.cache", param.Cache, locked);
} else {
setDefaultPref(
"privacy.clearOnShutdown.cache",
false,
lockDefaultPrefs
);
}
if ("Cookies" in param) {
setDefaultPref(
"privacy.clearOnShutdown.cookies",
param.Cookies,
locked
);
} else {