158 lines
6.2 KiB
JavaScript
158 lines
6.2 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/. */
|
|
"use strict";
|
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this, "AppConstants", "resource://gre/modules/AppConstants.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm"
|
|
);
|
|
|
|
var EXPORTED_SYMBOLS = ["ShieldPreferences"];
|
|
|
|
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; // from modules/libpref/nsIPrefBranch.idl
|
|
const FHR_UPLOAD_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
|
|
const OPT_OUT_STUDIES_ENABLED_PREF = "app.shield.optoutstudies.enabled";
|
|
|
|
/**
|
|
* Handles Shield-specific preferences, including their UI.
|
|
*/
|
|
var ShieldPreferences = {
|
|
init() {
|
|
// If the FHR pref was disabled since our last run, disable opt-out as well.
|
|
if (!Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF)) {
|
|
Services.prefs.setBoolPref(OPT_OUT_STUDIES_ENABLED_PREF, false);
|
|
}
|
|
|
|
// Watch for changes to the FHR pref
|
|
Services.prefs.addObserver(FHR_UPLOAD_ENABLED_PREF, this);
|
|
CleanupManager.addCleanupHandler(() => {
|
|
Services.prefs.removeObserver(FHR_UPLOAD_ENABLED_PREF, this);
|
|
});
|
|
|
|
// Watch for changes to the Opt-out pref
|
|
Services.prefs.addObserver(OPT_OUT_STUDIES_ENABLED_PREF, this);
|
|
CleanupManager.addCleanupHandler(() => {
|
|
Services.prefs.removeObserver(OPT_OUT_STUDIES_ENABLED_PREF, this);
|
|
});
|
|
|
|
// Disabled outside of en-* locales temporarily (bug 1377192).
|
|
// Disabled when MOZ_DATA_REPORTING is false since the FHR UI is also hidden
|
|
// when data reporting is false.
|
|
if (AppConstants.MOZ_DATA_REPORTING && Services.locale.getAppLocaleAsLangTag().startsWith("en")) {
|
|
Services.obs.addObserver(this, "privacy-pane-loaded");
|
|
CleanupManager.addCleanupHandler(() => {
|
|
Services.obs.removeObserver(this, "privacy-pane-loaded");
|
|
});
|
|
}
|
|
},
|
|
|
|
observe(subject, topic, data) {
|
|
switch (topic) {
|
|
// Add the opt-out-study checkbox to the Privacy preferences when it is shown.
|
|
case "privacy-pane-loaded":
|
|
this.injectOptOutStudyCheckbox(subject.document);
|
|
break;
|
|
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
|
|
this.observePrefChange(data);
|
|
break;
|
|
}
|
|
},
|
|
|
|
async observePrefChange(prefName) {
|
|
let prefValue;
|
|
switch (prefName) {
|
|
// If the FHR pref changes, set the opt-out-study pref to the value it is changing to.
|
|
case FHR_UPLOAD_ENABLED_PREF: {
|
|
prefValue = Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF);
|
|
Services.prefs.setBoolPref(OPT_OUT_STUDIES_ENABLED_PREF, prefValue);
|
|
break;
|
|
}
|
|
|
|
// If the opt-out pref changes to be false, disable all current studies.
|
|
case OPT_OUT_STUDIES_ENABLED_PREF: {
|
|
prefValue = Services.prefs.getBoolPref(OPT_OUT_STUDIES_ENABLED_PREF);
|
|
if (!prefValue) {
|
|
for (const study of await AddonStudies.getAll()) {
|
|
if (study.active) {
|
|
await AddonStudies.stop(study.recipeId, "general-opt-out");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Injects the opt-out-study preference checkbox into about:preferences and
|
|
* handles events coming from the UI for it.
|
|
*/
|
|
injectOptOutStudyCheckbox(doc) {
|
|
const container = doc.createElementNS(XUL_NS, "vbox");
|
|
container.classList.add("indent");
|
|
|
|
const hContainer = doc.createElementNS(XUL_NS, "hbox");
|
|
hContainer.setAttribute("align", "center");
|
|
container.appendChild(hContainer);
|
|
|
|
const checkbox = doc.createElementNS(XUL_NS, "checkbox");
|
|
checkbox.setAttribute("id", "optOutStudiesEnabled");
|
|
checkbox.setAttribute("class", "tail-with-learn-more");
|
|
checkbox.setAttribute("label", "Allow Firefox to install and run studies");
|
|
|
|
let allowedByPolicy = Services.policies.isAllowed("Shield");
|
|
if (allowedByPolicy) {
|
|
// If Shield is not allowed by policy, don't tie this checkbox to the preference,
|
|
// so that the checkbox remains unchecked.
|
|
// Otherwise, it would be grayed out but still checked, which looks confusing
|
|
// because it appears it's enabled with no way to disable it.
|
|
checkbox.setAttribute("preference", OPT_OUT_STUDIES_ENABLED_PREF);
|
|
}
|
|
hContainer.appendChild(checkbox);
|
|
|
|
const viewStudies = doc.createElementNS(XUL_NS, "label");
|
|
viewStudies.setAttribute("id", "viewShieldStudies");
|
|
viewStudies.setAttribute("href", "about:studies");
|
|
viewStudies.setAttribute("useoriginprincipal", true);
|
|
viewStudies.textContent = "View Firefox Studies";
|
|
viewStudies.classList.add("learnMore", "text-link");
|
|
hContainer.appendChild(viewStudies);
|
|
|
|
// Preference instances for prefs that we need to monitor while the page is open.
|
|
doc.defaultView.Preferences.add({ id: OPT_OUT_STUDIES_ENABLED_PREF, type: "bool" });
|
|
|
|
// Weirdly, FHR doesn't have a Preference instance on the page, so we create it.
|
|
const fhrPref = doc.defaultView.Preferences.add({ id: FHR_UPLOAD_ENABLED_PREF, type: "bool" });
|
|
function onChangeFHRPref() {
|
|
let isDisabled = Services.prefs.prefIsLocked(FHR_UPLOAD_ENABLED_PREF) ||
|
|
!AppConstants.MOZ_TELEMETRY_REPORTING ||
|
|
!Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF) ||
|
|
!allowedByPolicy;
|
|
// We can't use checkbox.disabled here because the XBL binding may not be present,
|
|
// in which case setting the property won't work properly.
|
|
if (isDisabled) {
|
|
checkbox.setAttribute("disabled", "true");
|
|
} else {
|
|
checkbox.removeAttribute("disabled");
|
|
}
|
|
}
|
|
fhrPref.on("change", onChangeFHRPref);
|
|
onChangeFHRPref();
|
|
doc.defaultView.addEventListener("unload", () => fhrPref.off("change", onChangeFHRPref), { once: true });
|
|
|
|
// Actually inject the elements we've created.
|
|
const parent = doc.getElementById("submitHealthReportBox").closest("description");
|
|
parent.appendChild(container);
|
|
},
|
|
};
|