1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* import-globals-from preferences.js */
7 var gExperimentalPane = {
10 _featureGatesContainer: null,
11 _boundRestartObserver: null,
13 _shouldPromptForRestart: true,
15 _featureGatePrefTypeToPrefServiceType(featureGatePrefType) {
16 if (featureGatePrefType != "boolean") {
17 throw new Error("Only boolean FeatureGates are supported");
22 async _observeRestart(aSubject, aTopic, aData) {
23 if (!this._shouldPromptForRestart) {
26 let prefValue = Services.prefs.getBoolPref(aData);
27 let buttonIndex = await confirmRestartPrompt(prefValue, 1, true, false);
28 if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
29 Services.startup.quit(
30 Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
34 this._shouldPromptForRestart = false;
35 Services.prefs.setBoolPref(aData, !prefValue);
36 this._shouldPromptForRestart = true;
39 addPrefObserver(name, fn) {
40 this._observedPrefs.push({ name, fn });
41 Services.prefs.addObserver(name, fn);
44 removePrefObservers() {
45 for (let { name, fn } of this._observedPrefs) {
46 Services.prefs.removeObserver(name, fn);
48 this._observedPrefs = [];
51 // Reset the features to their default values
52 async resetAllFeatures() {
53 let features = await gExperimentalPane.getFeatures();
54 for (let feature of features) {
55 Services.prefs.setBoolPref(feature.preference, feature.defaultValue);
60 let searchParams = new URLSearchParams(document.documentURIObject.query);
61 let definitionsUrl = searchParams.get("definitionsUrl");
62 let features = await FeatureGate.all(definitionsUrl);
63 return features.filter(f => f.isPublic);
66 async _sortFeatures(features) {
67 // Sort the features alphabetically by their title
68 let titles = await document.l10n.formatMessages(
70 return { id: f.title };
73 titles = titles.map((title, index) => [title.attributes[0].value, index]);
74 titles.sort((a, b) => a[0].toLowerCase().localeCompare(b[0].toLowerCase()));
75 // Get the features in order of sorted titles.
76 return titles.map(([, index]) => features[index]);
85 let features = await this.getFeatures();
86 let shouldHide = !features.length;
87 document.getElementById("category-experimental").hidden = shouldHide;
88 // Cache the visibility so we can show it quicker in subsequent loads.
89 Services.prefs.setBoolPref(
90 "browser.preferences.experimental.hidden",
94 // Remove the 'experimental' category if there are no available features
95 document.getElementById("firefoxExperimentalCategory").remove();
97 document.getElementById("categories").selectedItem?.id ==
98 "category-experimental"
100 // Leave the 'experimental' category if there are no available features
106 features = await this._sortFeatures(features);
109 "experimentalCategory-reset",
111 gExperimentalPane.resetAllFeatures
114 window.addEventListener("unload", () => this.removePrefObservers());
115 this._template = document.getElementById("template-featureGate");
116 this._featureGatesContainer = document.getElementById(
117 "pane-experimental-featureGates"
119 this._boundRestartObserver = this._observeRestart.bind(this);
120 let frag = document.createDocumentFragment();
121 for (let feature of features) {
122 if (Preferences.get(feature.preference)) {
124 "Preference control already exists for experimental feature '" +
126 "' with preference '" +
132 if (feature.restartRequired) {
133 this.addPrefObserver(feature.preference, this._boundRestartObserver);
135 let template = this._template.content.cloneNode(true);
136 let description = template.querySelector(".featureGateDescription");
137 description.id = feature.id + "-description";
138 let descriptionLinks = feature.descriptionLinks || {};
139 for (let [key, value] of Object.entries(descriptionLinks)) {
140 let link = document.createElement("a");
141 link.setAttribute("data-l10n-name", key);
142 link.setAttribute("href", value);
143 link.setAttribute("target", "_blank");
144 description.append(link);
146 document.l10n.setAttributes(description, feature.description);
147 let checkbox = template.querySelector(".featureGateCheckbox");
148 checkbox.setAttribute("preference", feature.preference);
149 checkbox.id = feature.id;
150 checkbox.setAttribute("aria-describedby", description.id);
151 document.l10n.setAttributes(checkbox, feature.title);
152 frag.appendChild(template);
153 let preference = Preferences.add({
154 id: feature.preference,
155 type: gExperimentalPane._featureGatePrefTypeToPrefServiceType(
159 preference.setElementValue(checkbox);
161 this._featureGatesContainer.appendChild(frag);