forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			188 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			188 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 { ActivityStreamMessageChannel } from "resource://activity-stream/lib/ActivityStreamMessageChannel.sys.mjs";
 | |
| import { ActivityStreamStorage } from "resource://activity-stream/lib/ActivityStreamStorage.sys.mjs";
 | |
| import { Prefs } from "resource://activity-stream/lib/ActivityStreamPrefs.sys.mjs";
 | |
| import { reducers } from "resource://activity-stream/common/Reducers.sys.mjs";
 | |
| import { redux } from "resource://activity-stream/vendor/Redux.sys.mjs";
 | |
| 
 | |
| /**
 | |
|  * Store - This has a similar structure to a redux store, but includes some extra
 | |
|  *         functionality to allow for routing of actions between the Main processes
 | |
|  *         and child processes via a ActivityStreamMessageChannel.
 | |
|  *         It also accepts an array of "Feeds" on inititalization, which
 | |
|  *         can listen for any action that is dispatched through the store.
 | |
|  */
 | |
| export class Store {
 | |
|   /**
 | |
|    * constructor - The redux store and message manager are created here,
 | |
|    *               but no listeners are added until "init" is called.
 | |
|    */
 | |
|   constructor() {
 | |
|     this._middleware = this._middleware.bind(this);
 | |
|     // Bind each redux method so we can call it directly from the Store. E.g.,
 | |
|     // store.dispatch() will call store._store.dispatch();
 | |
|     for (const method of ["dispatch", "getState", "subscribe"]) {
 | |
|       this[method] = (...args) => this._store[method](...args);
 | |
|     }
 | |
|     this.feeds = new Map();
 | |
|     this._prefs = new Prefs();
 | |
|     this._messageChannel = new ActivityStreamMessageChannel({
 | |
|       dispatch: this.dispatch,
 | |
|     });
 | |
|     this._store = redux.createStore(
 | |
|       redux.combineReducers(reducers),
 | |
|       redux.applyMiddleware(this._middleware, this._messageChannel.middleware)
 | |
|     );
 | |
|     this.storage = null;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * _middleware - This is redux middleware consumed by redux.createStore.
 | |
|    *               it calls each feed's .onAction method, if one
 | |
|    *               is defined.
 | |
|    */
 | |
|   _middleware() {
 | |
|     return next => action => {
 | |
|       next(action);
 | |
|       for (const store of this.feeds.values()) {
 | |
|         if (store.onAction) {
 | |
|           store.onAction(action);
 | |
|         }
 | |
|       }
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * initFeed - Initializes a feed by calling its constructor function
 | |
|    *
 | |
|    * @param  {string} feedName The name of a feed, as defined in the object
 | |
|    *                           passed to Store.init
 | |
|    * @param {Action} initAction An optional action to initialize the feed
 | |
|    */
 | |
|   initFeed(feedName, initAction) {
 | |
|     const feed = this._feedFactories.get(feedName)();
 | |
|     feed.store = this;
 | |
|     this.feeds.set(feedName, feed);
 | |
|     if (initAction && feed.onAction) {
 | |
|       feed.onAction(initAction);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * uninitFeed - Removes a feed and calls its uninit function if defined
 | |
|    *
 | |
|    * @param  {string} feedName The name of a feed, as defined in the object
 | |
|    *                           passed to Store.init
 | |
|    * @param {Action} uninitAction An optional action to uninitialize the feed
 | |
|    */
 | |
|   uninitFeed(feedName, uninitAction) {
 | |
|     const feed = this.feeds.get(feedName);
 | |
|     if (!feed) {
 | |
|       return;
 | |
|     }
 | |
|     if (uninitAction && feed.onAction) {
 | |
|       feed.onAction(uninitAction);
 | |
|     }
 | |
|     this.feeds.delete(feedName);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * onPrefChanged - Listener for handling feed changes.
 | |
|    */
 | |
|   onPrefChanged(name, value) {
 | |
|     if (this._feedFactories.has(name)) {
 | |
|       if (value) {
 | |
|         this.initFeed(name, this._initAction);
 | |
|       } else {
 | |
|         this.uninitFeed(name, this._uninitAction);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * init - Initializes the ActivityStreamMessageChannel channel, and adds feeds.
 | |
|    *
 | |
|    * Note that it intentionally initializes the TelemetryFeed first so that the
 | |
|    * addon is able to report the init errors from other feeds.
 | |
|    *
 | |
|    * @param  {Map} feedFactories A Map of feeds with the name of the pref for
 | |
|    *                                the feed as the key and a function that
 | |
|    *                                constructs an instance of the feed.
 | |
|    * @param {Action} initAction An optional action that will be dispatched
 | |
|    *                            to feeds when they're created.
 | |
|    * @param {Action} uninitAction An optional action for when feeds uninit.
 | |
|    */
 | |
|   async init(feedFactories, initAction, uninitAction) {
 | |
|     this._feedFactories = feedFactories;
 | |
|     this._initAction = initAction;
 | |
|     this._uninitAction = uninitAction;
 | |
| 
 | |
|     const telemetryKey = "feeds.telemetry";
 | |
|     if (feedFactories.has(telemetryKey) && this._prefs.get(telemetryKey)) {
 | |
|       this.initFeed(telemetryKey);
 | |
|     }
 | |
| 
 | |
|     await this._initIndexedDB(telemetryKey);
 | |
| 
 | |
|     for (const pref of feedFactories.keys()) {
 | |
|       if (pref !== telemetryKey && this._prefs.get(pref)) {
 | |
|         this.initFeed(pref);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     this._prefs.observeBranch(this);
 | |
| 
 | |
|     // Dispatch an initial action after all enabled feeds are ready
 | |
|     if (initAction) {
 | |
|       this.dispatch(initAction);
 | |
|     }
 | |
| 
 | |
|     // Dispatch NEW_TAB_INIT/NEW_TAB_LOAD events after INIT event.
 | |
|     this._messageChannel.simulateMessagesForExistingTabs();
 | |
|   }
 | |
| 
 | |
|   async _initIndexedDB() {
 | |
|     // "snippets" is the name of one storage space, but these days it is used
 | |
|     // not for snippet-related data (snippets were removed in bug 1715158),
 | |
|     // but storage for impression or session data for all ASRouter messages.
 | |
|     //
 | |
|     // We keep the name "snippets" to avoid having to do an IndexedDB database
 | |
|     // migration.
 | |
|     this.dbStorage = new ActivityStreamStorage({
 | |
|       storeNames: ["sectionPrefs", "snippets"],
 | |
|     });
 | |
|     // Accessing the db causes the object stores to be created / migrated.
 | |
|     // This needs to happen before other instances try to access the db, which
 | |
|     // would update only a subset of the stores to the latest version.
 | |
|     try {
 | |
|       await this.dbStorage.db; // eslint-disable-line no-unused-expressions
 | |
|     } catch (e) {
 | |
|       this.dbStorage.telemetry = null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * uninit -  Uninitalizes each feed, clears them, and destroys the message
 | |
|    *           manager channel.
 | |
|    *
 | |
|    * @return {type}  description
 | |
|    */
 | |
|   uninit() {
 | |
|     if (this._uninitAction) {
 | |
|       this.dispatch(this._uninitAction);
 | |
|     }
 | |
|     this._prefs.ignoreBranch(this);
 | |
|     this.feeds.clear();
 | |
|     this._feedFactories = null;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * getMessageChannel - Used by the AboutNewTabParent actor to get the message channel.
 | |
|    */
 | |
|   getMessageChannel() {
 | |
|     return this._messageChannel;
 | |
|   }
 | |
| }
 | 
