gecko-dev/browser/components/newtab/lib/Store.jsm
Kris Maglione e930b89c34 Bug 1514594: Part 3 - Change ChromeUtils.import API.
***
Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8

This changes the behavior of ChromeUtils.import() to return an exports object,
rather than a module global, in all cases except when `null` is passed as a
second argument, and changes the default behavior not to pollute the global
scope with the module's exports. Thus, the following code written for the old
model:

  ChromeUtils.import("resource://gre/modules/Services.jsm");

is approximately the same as the following, in the new model:

  var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Since the two behaviors are mutually incompatible, this patch will land with a
scripted rewrite to update all existing callers to use the new model rather
than the old.
***
Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs

This was done using the followng script:

https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm
***
Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D16747
***
Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16748
***
Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16749
***
Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs
***
Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16750

--HG--
extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895
extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 10:18:31 -08:00

180 lines
6.1 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/. */
"use strict";
const {ActivityStreamMessageChannel} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamMessageChannel.jsm");
const {ActivityStreamStorage} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamStorage.jsm");
const {Prefs} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm");
const {reducers} = ChromeUtils.import("resource://activity-stream/common/Reducers.jsm");
const {redux} = ChromeUtils.import("resource://activity-stream/vendor/Redux.jsm");
/**
* 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.
*/
this.Store = 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);
this._messageChannel.createChannel();
// 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(telemetryKey) {
this.dbStorage = new ActivityStreamStorage({
storeNames: ["sectionPrefs", "snippets"],
telemetry: this.feeds.get(telemetryKey),
});
// 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;
this._messageChannel.destroyChannel();
}
};
const EXPORTED_SYMBOLS = ["Store"];