summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/lib/PrefsFeed.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/lib/PrefsFeed.jsm')
-rw-r--r--browser/components/newtab/lib/PrefsFeed.jsm291
1 files changed, 291 insertions, 0 deletions
diff --git a/browser/components/newtab/lib/PrefsFeed.jsm b/browser/components/newtab/lib/PrefsFeed.jsm
new file mode 100644
index 0000000000..9f858b9154
--- /dev/null
+++ b/browser/components/newtab/lib/PrefsFeed.jsm
@@ -0,0 +1,291 @@
+/* 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 { actionCreators: ac, actionTypes: at } = ChromeUtils.import(
+ "resource://activity-stream/common/Actions.jsm"
+);
+const { Prefs } = ChromeUtils.import(
+ "resource://activity-stream/lib/ActivityStreamPrefs.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "AppConstants",
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "ExperimentAPI",
+ "resource://messaging-system/experiments/ExperimentAPI.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "Region",
+ "resource://gre/modules/Region.jsm"
+);
+
+this.PrefsFeed = class PrefsFeed {
+ constructor(prefMap) {
+ this._prefMap = prefMap;
+ this._prefs = new Prefs();
+ this.onExperimentUpdated = this.onExperimentUpdated.bind(this);
+ }
+
+ onPrefChanged(name, value) {
+ const prefItem = this._prefMap.get(name);
+ if (prefItem) {
+ this.store.dispatch(
+ ac[prefItem.skipBroadcast ? "OnlyToMain" : "BroadcastToContent"]({
+ type: at.PREF_CHANGED,
+ data: { name, value },
+ })
+ );
+ }
+ }
+
+ _setStringPref(values, key, defaultValue) {
+ this._setPref(values, key, defaultValue, Services.prefs.getStringPref);
+ }
+
+ _setBoolPref(values, key, defaultValue) {
+ this._setPref(values, key, defaultValue, Services.prefs.getBoolPref);
+ }
+
+ _setIntPref(values, key, defaultValue) {
+ this._setPref(values, key, defaultValue, Services.prefs.getIntPref);
+ }
+
+ _setPref(values, key, defaultValue, getPrefFunction) {
+ let value = getPrefFunction(
+ `browser.newtabpage.activity-stream.${key}`,
+ defaultValue
+ );
+ values[key] = value;
+ this._prefMap.set(key, { value });
+ }
+
+ /**
+ * Combine default values with experiment values for
+ * the feature config.
+ * */
+ getFeatureConfigFromExperimentData(experimentData) {
+ return {
+ // Icon that shows up in the corner to link to preferences
+ prefsButtonIcon: "icon-settings",
+
+ // Override defaults with any experiment values, if any exist.
+ ...(experimentData?.branch?.feature?.value || {}),
+ };
+ }
+
+ /**
+ * Helper for initializing experiment and feature config data in .init()
+ * */
+ addExperimentDataToValues(values) {
+ let experimentData;
+ try {
+ experimentData = ExperimentAPI.getExperiment({
+ featureId: "newtab",
+ });
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ values.experimentData = experimentData;
+ values.featureConfig = this.getFeatureConfigFromExperimentData(
+ experimentData
+ );
+ }
+
+ /**
+ * Handler for when experiment data updates.
+ */
+ onExperimentUpdated(event, experimentData) {
+ this.store.dispatch(
+ ac.BroadcastToContent({
+ type: at.PREF_CHANGED,
+ data: { name: "experimentData", value: experimentData },
+ })
+ );
+ this.store.dispatch(
+ ac.BroadcastToContent({
+ type: at.PREF_CHANGED,
+ data: {
+ name: "featureConfig",
+ value: this.getFeatureConfigFromExperimentData(experimentData),
+ },
+ })
+ );
+ }
+
+ init() {
+ this._prefs.observeBranch(this);
+ ExperimentAPI.on(
+ "update",
+ { featureId: "newtab" },
+ this.onExperimentUpdated
+ );
+
+ this._storage = this.store.dbStorage.getDbTable("sectionPrefs");
+
+ // Get the initial value of each activity stream pref
+ const values = {};
+ for (const name of this._prefMap.keys()) {
+ values[name] = this._prefs.get(name);
+ }
+
+ // These are not prefs, but are needed to determine stuff in content that can only be
+ // computed in main process
+ values.isPrivateBrowsingEnabled = PrivateBrowsingUtils.enabled;
+ values.platform = AppConstants.platform;
+
+ // Save the geo pref if we have it
+ if (Region.home) {
+ values.region = Region.home;
+ this.geo = values.region;
+ } else if (this.geo !== "") {
+ // Watch for geo changes and use a dummy value for now
+ Services.obs.addObserver(this, Region.REGION_TOPIC);
+ this.geo = "";
+ }
+
+ // Get the firefox accounts url for links and to send firstrun metrics to.
+ values.fxa_endpoint = Services.prefs.getStringPref(
+ "browser.newtabpage.activity-stream.fxaccounts.endpoint",
+ "https://accounts.firefox.com"
+ );
+
+ // Get the firefox update channel with values as default, nightly, beta or release
+ values.appUpdateChannel = Services.prefs.getStringPref(
+ "app.update.channel",
+ ""
+ );
+
+ // Read the pref for search shortcuts top sites experiment from firefox.js and store it
+ // in our interal list of prefs to watch
+ let searchTopSiteExperimentPrefValue = Services.prefs.getBoolPref(
+ "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts"
+ );
+ values[
+ "improvesearch.topSiteSearchShortcuts"
+ ] = searchTopSiteExperimentPrefValue;
+ this._prefMap.set("improvesearch.topSiteSearchShortcuts", {
+ value: searchTopSiteExperimentPrefValue,
+ });
+
+ values.mayHaveSponsoredTopSites = Services.prefs.getBoolPref(
+ "browser.topsites.useRemoteSetting"
+ );
+
+ // Read the pref for search hand-off from firefox.js and store it
+ // in our interal list of prefs to watch
+ let handoffToAwesomebarPrefValue = Services.prefs.getBoolPref(
+ "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar"
+ );
+ values["improvesearch.handoffToAwesomebar"] = handoffToAwesomebarPrefValue;
+ this._prefMap.set("improvesearch.handoffToAwesomebar", {
+ value: handoffToAwesomebarPrefValue,
+ });
+
+ this.addExperimentDataToValues(values);
+
+ this._setBoolPref(values, "newNewtabExperience.enabled", false);
+ this._setBoolPref(values, "customizationMenu.enabled", false);
+ this._setBoolPref(values, "logowordmark.alwaysVisible", false);
+ this._setBoolPref(values, "feeds.section.topstories", false);
+ this._setBoolPref(values, "discoverystream.enabled", false);
+ this._setBoolPref(values, "discoverystream.isCollectionDismissible", false);
+ this._setBoolPref(values, "discoverystream.hardcoded-basic-layout", false);
+ this._setBoolPref(values, "discoverystream.recs.personalized", false);
+ this._setBoolPref(values, "discoverystream.spocs.personalized", false);
+ this._setStringPref(
+ values,
+ "discoverystream.personalization.modelKeys",
+ ""
+ );
+ this._setIntPref(values, "discoverystream.personalization.version", 1);
+ this._setIntPref(values, "discoverystream.personalization.overrideVersion");
+ this._setStringPref(values, "discoverystream.spocs-endpoint", "");
+ this._setStringPref(values, "discoverystream.spocs-endpoint-query", "");
+ this._setStringPref(values, "newNewtabExperience.colors", "");
+
+ // Set the initial state of all prefs in redux
+ this.store.dispatch(
+ ac.BroadcastToContent({
+ type: at.PREFS_INITIAL_VALUES,
+ data: values,
+ meta: {
+ isStartup: true,
+ },
+ })
+ );
+ }
+
+ uninit() {
+ this.removeListeners();
+ }
+
+ removeListeners() {
+ this._prefs.ignoreBranch(this);
+ ExperimentAPI.off(this.onExperimentUpdated);
+ if (this.geo === "") {
+ Services.obs.removeObserver(this, Region.REGION_TOPIC);
+ }
+ }
+
+ async _setIndexedDBPref(id, value) {
+ const name = id === "topsites" ? id : `feeds.section.${id}`;
+ try {
+ await this._storage.set(name, value);
+ } catch (e) {
+ Cu.reportError("Could not set section preferences.");
+ }
+ }
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case Region.REGION_TOPIC:
+ if (data === Region.REGION_UPDATED) {
+ this.store.dispatch(
+ ac.BroadcastToContent({
+ type: at.PREF_CHANGED,
+ data: { name: "region", value: Region.home },
+ })
+ );
+ }
+ break;
+ }
+ }
+
+ onAction(action) {
+ switch (action.type) {
+ case at.INIT:
+ this.init();
+ break;
+ case at.UNINIT:
+ this.uninit();
+ break;
+ case at.CLEAR_PREF:
+ Services.prefs.clearUserPref(this._prefs._branchStr + action.data.name);
+ break;
+ case at.SET_PREF:
+ this._prefs.set(action.data.name, action.data.value);
+ break;
+ case at.UPDATE_SECTION_PREFS:
+ this._setIndexedDBPref(action.data.id, action.data.value);
+ break;
+ }
+ }
+};
+
+const EXPORTED_SYMBOLS = ["PrefsFeed"];