221 lines
6.4 KiB
JavaScript
221 lines
6.4 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-globals-from preferences.js */
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
|
FirefoxLabs: "resource://nimbus/FirefoxLabs.sys.mjs",
|
|
});
|
|
|
|
const gExperimentalPane = {
|
|
inited: false,
|
|
_featureGatesContainer: null,
|
|
_firefoxLabs: null,
|
|
|
|
async init() {
|
|
if (this.inited) {
|
|
return;
|
|
}
|
|
|
|
this.inited = true;
|
|
this._featureGatesContainer = document.getElementById(
|
|
"pane-experimental-featureGates"
|
|
);
|
|
|
|
this._onCheckboxChanged = this._onCheckboxChanged.bind(this);
|
|
this._onNimbusUpdate = this._onNimbusUpdate.bind(this);
|
|
this._onStudiesEnabledChanged = this._onStudiesEnabledChanged.bind(this);
|
|
this._resetAllFeatures = this._resetAllFeatures.bind(this);
|
|
|
|
setEventListener(
|
|
"experimentalCategory-reset",
|
|
"click",
|
|
this._resetAllFeatures
|
|
);
|
|
|
|
Services.obs.addObserver(
|
|
this._onStudiesEnabledChanged,
|
|
ExperimentAPI.STUDIES_ENABLED_CHANGED
|
|
);
|
|
window.addEventListener("unload", () => this._removeObservers());
|
|
|
|
await this._maybeRenderLabsRecipes();
|
|
},
|
|
|
|
async _maybeRenderLabsRecipes() {
|
|
this._firefoxLabs = await FirefoxLabs.create();
|
|
|
|
const shouldHide = this._firefoxLabs.count === 0;
|
|
this._setCategoryVisibility(shouldHide);
|
|
|
|
if (shouldHide) {
|
|
return;
|
|
}
|
|
|
|
const frag = document.createDocumentFragment();
|
|
|
|
const groups = new Map();
|
|
for (const optIn of this._firefoxLabs.all()) {
|
|
if (!groups.has(optIn.firefoxLabsGroup)) {
|
|
groups.set(optIn.firefoxLabsGroup, []);
|
|
}
|
|
|
|
groups.get(optIn.firefoxLabsGroup).push(optIn);
|
|
}
|
|
|
|
for (const [group, optIns] of groups) {
|
|
const card = document.createElement("moz-card");
|
|
card.classList.add("featureGate");
|
|
|
|
const fieldset = document.createElement("moz-fieldset");
|
|
document.l10n.setAttributes(fieldset, group);
|
|
|
|
card.append(fieldset);
|
|
|
|
for (const optIn of optIns) {
|
|
const checkbox = document.createElement("moz-checkbox");
|
|
checkbox.dataset.nimbusSlug = optIn.slug;
|
|
checkbox.dataset.nimbusBranchSlug = optIn.branches[0].slug;
|
|
const description = document.createElement("div");
|
|
description.slot = "description";
|
|
description.id = `${optIn.slug}-description`;
|
|
description.classList.add("featureGateDescription");
|
|
|
|
for (const [key, value] of Object.entries(
|
|
optIn.firefoxLabsDescriptionLinks ?? {}
|
|
)) {
|
|
const link = document.createElement("a");
|
|
link.setAttribute("data-l10n-name", key);
|
|
link.setAttribute("href", value);
|
|
link.setAttribute("target", "_blank");
|
|
|
|
description.append(link);
|
|
}
|
|
|
|
document.l10n.setAttributes(description, optIn.firefoxLabsDescription);
|
|
checkbox.id = optIn.slug;
|
|
checkbox.setAttribute("aria-describedby", description.id);
|
|
document.l10n.setAttributes(checkbox, optIn.firefoxLabsTitle);
|
|
|
|
checkbox.checked =
|
|
ExperimentAPI.manager.store.get(optIn.slug)?.active ?? false;
|
|
checkbox.addEventListener("change", this._onCheckboxChanged);
|
|
|
|
checkbox.append(description);
|
|
fieldset.append(checkbox);
|
|
}
|
|
|
|
frag.append(card);
|
|
}
|
|
|
|
this._featureGatesContainer.appendChild(frag);
|
|
|
|
ExperimentAPI.manager.store.on("update", this._onNimbusUpdate);
|
|
|
|
Services.obs.notifyObservers(window, "experimental-pane-loaded");
|
|
},
|
|
|
|
_removeLabsRecipes() {
|
|
ExperimentAPI.manager.store.off("update", this._onNimbusUpdate);
|
|
|
|
this._featureGatesContainer
|
|
.querySelectorAll(".featureGate")
|
|
.forEach(el => el.remove());
|
|
},
|
|
|
|
async _onCheckboxChanged(event) {
|
|
const target = event.target;
|
|
|
|
const slug = target.dataset.nimbusSlug;
|
|
const branchSlug = target.dataset.nimbusBranchSlug;
|
|
|
|
const enrolling = !(ExperimentAPI.manager.store.get(slug)?.active ?? false);
|
|
|
|
let shouldRestart = false;
|
|
if (this._firefoxLabs.get(slug).requiresRestart) {
|
|
const buttonIndex = await confirmRestartPrompt(enrolling, 1, true, false);
|
|
shouldRestart = buttonIndex === CONFIRM_RESTART_PROMPT_RESTART_NOW;
|
|
|
|
if (!shouldRestart) {
|
|
// The user declined to restart, so we will not enroll in the opt-in.
|
|
target.checked = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Disable the checkbox so that the user cannot interact with it during enrollment.
|
|
target.disabled = true;
|
|
|
|
if (enrolling) {
|
|
await this._firefoxLabs.enroll(slug, branchSlug);
|
|
} else {
|
|
await this._firefoxLabs.unenroll(slug);
|
|
}
|
|
|
|
target.disabled = false;
|
|
|
|
if (shouldRestart) {
|
|
Services.startup.quit(
|
|
Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
|
|
);
|
|
}
|
|
},
|
|
|
|
_onNimbusUpdate(_event, { slug, active }) {
|
|
if (this._firefoxLabs.get(slug)) {
|
|
document.getElementById(slug).checked = active;
|
|
}
|
|
},
|
|
|
|
async _onStudiesEnabledChanged() {
|
|
const studiesEnabled = ExperimentAPI.studiesEnabled;
|
|
|
|
if (studiesEnabled) {
|
|
await this._maybeRenderLabsRecipes();
|
|
} else {
|
|
this._setCategoryVisibility(true);
|
|
this._removeLabsRecipes();
|
|
this._firefoxLabs = null;
|
|
}
|
|
},
|
|
|
|
_removeObservers() {
|
|
ExperimentAPI.manager.store.off("update", this._onNimbusUpdate);
|
|
Services.obs.removeObserver(
|
|
this._onStudiesEnabledChanged,
|
|
ExperimentAPI.STUDIES_ENABLED_CHANGED
|
|
);
|
|
},
|
|
|
|
// Reset the features to their default values
|
|
async _resetAllFeatures() {
|
|
for (const optIn of this._firefoxLabs.all()) {
|
|
const enrolled =
|
|
(await ExperimentAPI.manager.store.get(optIn.slug)?.active) ?? false;
|
|
if (enrolled) {
|
|
await this._firefoxLabs.unenroll(optIn.slug);
|
|
}
|
|
}
|
|
},
|
|
|
|
_setCategoryVisibility(shouldHide) {
|
|
document.getElementById("category-experimental").hidden = shouldHide;
|
|
|
|
// Cache the visibility so we can show it quicker in subsequent loads.
|
|
Services.prefs.setBoolPref(
|
|
"browser.preferences.experimental.hidden",
|
|
shouldHide
|
|
);
|
|
|
|
if (
|
|
shouldHide &&
|
|
document.getElementById("categories").selectedItem?.id ==
|
|
"category-experimental"
|
|
) {
|
|
// Leave the 'experimental' category if there are no available features
|
|
gotoPref("general");
|
|
}
|
|
},
|
|
};
|