diff options
Diffstat (limited to 'browser/components/newtab/lib/PrefsFeed.jsm')
-rw-r--r-- | browser/components/newtab/lib/PrefsFeed.jsm | 291 |
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"]; |