516 lines
14 KiB
JavaScript
516 lines
14 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
/* 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";
|
|
|
|
var { ExtensionPreferencesManager } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/ExtensionPreferencesManager.sys.mjs"
|
|
);
|
|
|
|
var { ExtensionError } = ExtensionUtils;
|
|
var { getSettingsAPI, getPrimedSettingsListener } = ExtensionPreferencesManager;
|
|
|
|
const cookieSvc = Ci.nsICookieService;
|
|
|
|
const getIntPref = p => Services.prefs.getIntPref(p, undefined);
|
|
const getBoolPref = p => Services.prefs.getBoolPref(p, undefined);
|
|
|
|
const TLS_MIN_PREF = "security.tls.version.min";
|
|
const TLS_MAX_PREF = "security.tls.version.max";
|
|
|
|
const cookieBehaviorValues = new Map([
|
|
["allow_all", cookieSvc.BEHAVIOR_ACCEPT],
|
|
["reject_third_party", cookieSvc.BEHAVIOR_REJECT_FOREIGN],
|
|
["reject_all", cookieSvc.BEHAVIOR_REJECT],
|
|
["allow_visited", cookieSvc.BEHAVIOR_LIMIT_FOREIGN],
|
|
["reject_trackers", cookieSvc.BEHAVIOR_REJECT_TRACKER],
|
|
[
|
|
"reject_trackers_and_partition_foreign",
|
|
cookieSvc.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
|
|
],
|
|
]);
|
|
|
|
function isTLSMinVersionLowerOrEQThan(version) {
|
|
return (
|
|
Services.prefs.getDefaultBranch("").getIntPref(TLS_MIN_PREF) <= version
|
|
);
|
|
}
|
|
|
|
const TLS_VERSIONS = [
|
|
{ version: 1, name: "TLSv1", settable: isTLSMinVersionLowerOrEQThan(1) },
|
|
{ version: 2, name: "TLSv1.1", settable: isTLSMinVersionLowerOrEQThan(2) },
|
|
{ version: 3, name: "TLSv1.2", settable: true },
|
|
{ version: 4, name: "TLSv1.3", settable: true },
|
|
];
|
|
|
|
// Add settings objects for supported APIs to the preferences manager.
|
|
ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", {
|
|
permission: "privacy",
|
|
prefNames: [
|
|
"network.predictor.enabled",
|
|
"network.prefetch-next",
|
|
"network.http.speculative-parallel-limit",
|
|
"network.dns.disablePrefetch",
|
|
],
|
|
|
|
setCallback(value) {
|
|
return {
|
|
"network.http.speculative-parallel-limit": value ? undefined : 0,
|
|
"network.dns.disablePrefetch": !value,
|
|
"network.predictor.enabled": value,
|
|
"network.prefetch-next": value,
|
|
};
|
|
},
|
|
|
|
getCallback() {
|
|
return (
|
|
getBoolPref("network.predictor.enabled") &&
|
|
getBoolPref("network.prefetch-next") &&
|
|
getIntPref("network.http.speculative-parallel-limit") > 0 &&
|
|
!getBoolPref("network.dns.disablePrefetch")
|
|
);
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("network.globalPrivacyControl", {
|
|
permission: "privacy",
|
|
prefNames: ["privacy.globalprivacycontrol.enabled"],
|
|
readOnly: true,
|
|
|
|
setCallback(value) {
|
|
return {
|
|
"privacy.globalprivacycontrol.enabled": value,
|
|
};
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("privacy.globalprivacycontrol.enabled");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("network.httpsOnlyMode", {
|
|
permission: "privacy",
|
|
prefNames: [
|
|
"dom.security.https_only_mode",
|
|
"dom.security.https_only_mode_pbm",
|
|
],
|
|
readOnly: true,
|
|
|
|
setCallback(value) {
|
|
let prefs = {
|
|
"dom.security.https_only_mode": false,
|
|
"dom.security.https_only_mode_pbm": false,
|
|
};
|
|
|
|
switch (value) {
|
|
case "always":
|
|
prefs["dom.security.https_only_mode"] = true;
|
|
break;
|
|
|
|
case "private_browsing":
|
|
prefs["dom.security.https_only_mode_pbm"] = true;
|
|
break;
|
|
|
|
case "never":
|
|
break;
|
|
}
|
|
|
|
return prefs;
|
|
},
|
|
|
|
getCallback() {
|
|
if (getBoolPref("dom.security.https_only_mode")) {
|
|
return "always";
|
|
}
|
|
if (getBoolPref("dom.security.https_only_mode_pbm")) {
|
|
return "private_browsing";
|
|
}
|
|
return "never";
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("network.peerConnectionEnabled", {
|
|
permission: "privacy",
|
|
prefNames: ["media.peerconnection.enabled"],
|
|
|
|
setCallback(value) {
|
|
return { [this.prefNames[0]]: value };
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("media.peerconnection.enabled");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("network.webRTCIPHandlingPolicy", {
|
|
permission: "privacy",
|
|
prefNames: [
|
|
"media.peerconnection.ice.default_address_only",
|
|
"media.peerconnection.ice.no_host",
|
|
"media.peerconnection.ice.proxy_only_if_behind_proxy",
|
|
"media.peerconnection.ice.proxy_only",
|
|
],
|
|
|
|
setCallback(value) {
|
|
let prefs = {};
|
|
switch (value) {
|
|
case "default":
|
|
// All prefs are already set to be reset.
|
|
break;
|
|
|
|
case "default_public_and_private_interfaces":
|
|
prefs["media.peerconnection.ice.default_address_only"] = true;
|
|
break;
|
|
|
|
case "default_public_interface_only":
|
|
prefs["media.peerconnection.ice.default_address_only"] = true;
|
|
prefs["media.peerconnection.ice.no_host"] = true;
|
|
break;
|
|
|
|
case "disable_non_proxied_udp":
|
|
prefs["media.peerconnection.ice.default_address_only"] = true;
|
|
prefs["media.peerconnection.ice.no_host"] = true;
|
|
prefs["media.peerconnection.ice.proxy_only_if_behind_proxy"] = true;
|
|
break;
|
|
|
|
case "proxy_only":
|
|
prefs["media.peerconnection.ice.proxy_only"] = true;
|
|
break;
|
|
}
|
|
return prefs;
|
|
},
|
|
|
|
getCallback() {
|
|
if (getBoolPref("media.peerconnection.ice.proxy_only")) {
|
|
return "proxy_only";
|
|
}
|
|
|
|
let default_address_only = getBoolPref(
|
|
"media.peerconnection.ice.default_address_only"
|
|
);
|
|
if (default_address_only) {
|
|
let no_host = getBoolPref("media.peerconnection.ice.no_host");
|
|
if (no_host) {
|
|
if (
|
|
getBoolPref("media.peerconnection.ice.proxy_only_if_behind_proxy")
|
|
) {
|
|
return "disable_non_proxied_udp";
|
|
}
|
|
return "default_public_interface_only";
|
|
}
|
|
return "default_public_and_private_interfaces";
|
|
}
|
|
|
|
return "default";
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("services.passwordSavingEnabled", {
|
|
permission: "privacy",
|
|
prefNames: ["signon.rememberSignons"],
|
|
|
|
setCallback(value) {
|
|
return { [this.prefNames[0]]: value };
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("signon.rememberSignons");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.cookieConfig", {
|
|
permission: "privacy",
|
|
prefNames: ["network.cookie.cookieBehavior"],
|
|
|
|
setCallback(value) {
|
|
const cookieBehavior = cookieBehaviorValues.get(value.behavior);
|
|
|
|
// Intentionally use Preferences.get("network.cookie.cookieBehavior") here
|
|
// to read the "real" preference value.
|
|
const needUpdate =
|
|
cookieBehavior !== getIntPref("network.cookie.cookieBehavior");
|
|
if (
|
|
needUpdate &&
|
|
getBoolPref("privacy.firstparty.isolate") &&
|
|
cookieBehavior === cookieSvc.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
|
|
) {
|
|
throw new ExtensionError(
|
|
`Invalid cookieConfig '${value.behavior}' when firstPartyIsolate is enabled`
|
|
);
|
|
}
|
|
|
|
if (typeof value.nonPersistentCookies === "boolean") {
|
|
Cu.reportError(
|
|
"'nonPersistentCookies' has been deprecated and it has no effect anymore."
|
|
);
|
|
}
|
|
|
|
return {
|
|
"network.cookie.cookieBehavior": cookieBehavior,
|
|
};
|
|
},
|
|
|
|
getCallback() {
|
|
let prefValue = getIntPref("network.cookie.cookieBehavior");
|
|
return {
|
|
behavior: Array.from(cookieBehaviorValues.entries()).find(
|
|
entry => entry[1] === prefValue
|
|
)[0],
|
|
// Bug 1754924 - this property is now deprecated.
|
|
nonPersistentCookies: false,
|
|
};
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.firstPartyIsolate", {
|
|
permission: "privacy",
|
|
prefNames: ["privacy.firstparty.isolate"],
|
|
|
|
setCallback(value) {
|
|
// Intentionally use Preferences.get("network.cookie.cookieBehavior") here
|
|
// to read the "real" preference value.
|
|
const cookieBehavior = getIntPref("network.cookie.cookieBehavior");
|
|
|
|
const needUpdate = value !== getBoolPref("privacy.firstparty.isolate");
|
|
if (
|
|
needUpdate &&
|
|
value &&
|
|
cookieBehavior === cookieSvc.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
|
|
) {
|
|
const behavior = Array.from(cookieBehaviorValues.entries()).find(
|
|
entry => entry[1] === cookieBehavior
|
|
)[0];
|
|
throw new ExtensionError(
|
|
`Can't enable firstPartyIsolate when cookieBehavior is '${behavior}'`
|
|
);
|
|
}
|
|
|
|
return { [this.prefNames[0]]: value };
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("privacy.firstparty.isolate");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.hyperlinkAuditingEnabled", {
|
|
permission: "privacy",
|
|
prefNames: ["browser.send_pings"],
|
|
|
|
setCallback(value) {
|
|
return { [this.prefNames[0]]: value };
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("browser.send_pings");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.referrersEnabled", {
|
|
permission: "privacy",
|
|
prefNames: ["network.http.sendRefererHeader"],
|
|
|
|
// Values for network.http.sendRefererHeader:
|
|
// 0=don't send any, 1=send only on clicks, 2=send on image requests as well
|
|
// http://searchfox.org/mozilla-central/rev/61054508641ee76f9c49bcf7303ef3cfb6b410d2/modules/libpref/init/all.js#1585
|
|
setCallback(value) {
|
|
return { [this.prefNames[0]]: value ? 2 : 0 };
|
|
},
|
|
|
|
getCallback() {
|
|
return getIntPref("network.http.sendRefererHeader") !== 0;
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.resistFingerprinting", {
|
|
permission: "privacy",
|
|
prefNames: ["privacy.resistFingerprinting"],
|
|
|
|
setCallback(value) {
|
|
return { [this.prefNames[0]]: value };
|
|
},
|
|
|
|
getCallback() {
|
|
return getBoolPref("privacy.resistFingerprinting");
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("websites.trackingProtectionMode", {
|
|
permission: "privacy",
|
|
prefNames: [
|
|
"privacy.trackingprotection.enabled",
|
|
"privacy.trackingprotection.pbmode.enabled",
|
|
],
|
|
|
|
setCallback(value) {
|
|
// Default to private browsing.
|
|
let prefs = {
|
|
"privacy.trackingprotection.enabled": false,
|
|
"privacy.trackingprotection.pbmode.enabled": true,
|
|
};
|
|
|
|
switch (value) {
|
|
case "private_browsing":
|
|
break;
|
|
|
|
case "always":
|
|
prefs["privacy.trackingprotection.enabled"] = true;
|
|
break;
|
|
|
|
case "never":
|
|
prefs["privacy.trackingprotection.pbmode.enabled"] = false;
|
|
break;
|
|
}
|
|
|
|
return prefs;
|
|
},
|
|
|
|
getCallback() {
|
|
if (getBoolPref("privacy.trackingprotection.enabled")) {
|
|
return "always";
|
|
} else if (getBoolPref("privacy.trackingprotection.pbmode.enabled")) {
|
|
return "private_browsing";
|
|
}
|
|
return "never";
|
|
},
|
|
});
|
|
|
|
ExtensionPreferencesManager.addSetting("network.tlsVersionRestriction", {
|
|
permission: "privacy",
|
|
prefNames: [TLS_MIN_PREF, TLS_MAX_PREF],
|
|
|
|
setCallback(value) {
|
|
function tlsStringToVersion(string) {
|
|
const version = TLS_VERSIONS.find(a => a.name === string);
|
|
if (version && version.settable) {
|
|
return version.version;
|
|
}
|
|
|
|
throw new ExtensionError(
|
|
`Setting TLS version ${string} is not allowed for security reasons.`
|
|
);
|
|
}
|
|
|
|
const prefs = {};
|
|
|
|
if (value.minimum) {
|
|
prefs[TLS_MIN_PREF] = tlsStringToVersion(value.minimum);
|
|
}
|
|
|
|
if (value.maximum) {
|
|
prefs[TLS_MAX_PREF] = tlsStringToVersion(value.maximum);
|
|
}
|
|
|
|
// If minimum has passed and it's greater than the max value.
|
|
if (prefs[TLS_MIN_PREF]) {
|
|
const max = prefs[TLS_MAX_PREF] || getIntPref(TLS_MAX_PREF);
|
|
if (max < prefs[TLS_MIN_PREF]) {
|
|
throw new ExtensionError(
|
|
`Setting TLS min version grater than the max version is not allowed.`
|
|
);
|
|
}
|
|
}
|
|
|
|
// If maximum has passed and it's lower than the min value.
|
|
else if (prefs[TLS_MAX_PREF]) {
|
|
const min = getIntPref(TLS_MIN_PREF);
|
|
if (min > prefs[TLS_MAX_PREF]) {
|
|
throw new ExtensionError(
|
|
`Setting TLS max version lower than the min version is not allowed.`
|
|
);
|
|
}
|
|
}
|
|
|
|
return prefs;
|
|
},
|
|
|
|
getCallback() {
|
|
function tlsVersionToString(pref) {
|
|
const value = getIntPref(pref);
|
|
const version = TLS_VERSIONS.find(a => a.version === value);
|
|
if (version) {
|
|
return version.name;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
return {
|
|
minimum: tlsVersionToString(TLS_MIN_PREF),
|
|
maximum: tlsVersionToString(TLS_MAX_PREF),
|
|
};
|
|
},
|
|
|
|
validate(extension) {
|
|
if (!extension.isPrivileged) {
|
|
throw new ExtensionError(
|
|
"tlsVersionRestriction can be set by privileged extensions only."
|
|
);
|
|
}
|
|
},
|
|
});
|
|
|
|
this.privacy = class extends ExtensionAPI {
|
|
primeListener(event, fire) {
|
|
let { extension } = this;
|
|
let listener = getPrimedSettingsListener({
|
|
extension,
|
|
name: event,
|
|
});
|
|
return listener(fire);
|
|
}
|
|
|
|
getAPI(context) {
|
|
function makeSettingsAPI(name) {
|
|
return getSettingsAPI({
|
|
context,
|
|
module: "privacy",
|
|
name,
|
|
});
|
|
}
|
|
|
|
return {
|
|
privacy: {
|
|
network: {
|
|
networkPredictionEnabled: makeSettingsAPI(
|
|
"network.networkPredictionEnabled"
|
|
),
|
|
globalPrivacyControl: makeSettingsAPI("network.globalPrivacyControl"),
|
|
httpsOnlyMode: makeSettingsAPI("network.httpsOnlyMode"),
|
|
peerConnectionEnabled: makeSettingsAPI(
|
|
"network.peerConnectionEnabled"
|
|
),
|
|
webRTCIPHandlingPolicy: makeSettingsAPI(
|
|
"network.webRTCIPHandlingPolicy"
|
|
),
|
|
tlsVersionRestriction: makeSettingsAPI(
|
|
"network.tlsVersionRestriction"
|
|
),
|
|
},
|
|
|
|
services: {
|
|
passwordSavingEnabled: makeSettingsAPI(
|
|
"services.passwordSavingEnabled"
|
|
),
|
|
},
|
|
|
|
websites: {
|
|
cookieConfig: makeSettingsAPI("websites.cookieConfig"),
|
|
firstPartyIsolate: makeSettingsAPI("websites.firstPartyIsolate"),
|
|
hyperlinkAuditingEnabled: makeSettingsAPI(
|
|
"websites.hyperlinkAuditingEnabled"
|
|
),
|
|
referrersEnabled: makeSettingsAPI("websites.referrersEnabled"),
|
|
resistFingerprinting: makeSettingsAPI(
|
|
"websites.resistFingerprinting"
|
|
),
|
|
trackingProtectionMode: makeSettingsAPI(
|
|
"websites.trackingProtectionMode"
|
|
),
|
|
},
|
|
},
|
|
};
|
|
}
|
|
};
|