Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(mc): Backport changes from Bug 1350411 #2448

Merged
merged 1 commit into from Apr 20, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+84 −44
Diff settings

Always

Just for now

@@ -7,7 +7,6 @@

const {utils: Cu} = Components;

Cu.import("resource://gre/modules/RemotePageManager.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

const {
@@ -19,16 +18,38 @@ const {
XPCOMUtils.defineLazyModuleGetter(this, "AboutNewTab",
"resource:///modules/AboutNewTab.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
"resource://gre/modules/RemotePageManager.jsm");

const ABOUT_NEW_TAB_URL = "about:newtab";

const DEFAULT_OPTIONS = {
dispatch(action) { dump(`\nMessage manager: Received action ${action.type}, but no dispatcher was defined.\n`); },
dispatch(action) {
throw new Error(`\nMessageChannel: Received action ${action.type}, but no dispatcher was defined.\n`);
},
pageURL: ABOUT_NEW_TAB_URL,
outgoingMessageName: "ActivityStream:MainToContent",
incomingMessageName: "ActivityStream:ContentToMain"
};

class MessageManager {
class ActivityStreamMessageChannel {

/**
* ActivityStreamMessageChannel - This module connects a Redux store to a RemotePageManager in Firefox.
* Call .createChannel to start the connection, and .destroyChannel to destroy it.
* You should use the BroadcastToContent, SendToContent, and SendToMain action creators
* in common/Actions.jsm to help you create actions that will be automatically routed
* to the correct location.
*
* @param {object} options
* @param {function} options.dispatch The dispatch method from a Redux store
* @param {string} options.pageURL The URL to which a RemotePageManager should be attached.
* Note that if it is about:newtab, the existing RemotePageManager
* for about:newtab will also be disabled
* @param {string} options.outgoingMessageName The name of the message sent to child processes
* @param {string} options.incomingMessageName The name of the message received from child processes
* @return {ActivityStreamMessageChannel}
*/
constructor(options = {}) {
Object.assign(this, DEFAULT_OPTIONS, options);
this.channel = null;
@@ -39,6 +60,13 @@ class MessageManager {
this.onNewTabUnload = this.onNewTabUnload.bind(this);
}

/**
* middleware - Redux middleware that looks for SendToContent and BroadcastToContent type
* actions, and sends them out.
*
* @param {object} store A redux store
* @return {function} Redux middleware
*/
middleware(store) {
return next => action => {
if (!this.channel) {
@@ -54,19 +82,35 @@ class MessageManager {
};
}

/**
* onActionFromContent - Handler for actions from a content processes
*
* @param {object} action A Redux action
* @param {string} targetId The portID of the port that sent the message
*/
onActionFromContent(action, targetId) {
this.dispatch(ac.SendToMain(action, {fromTarget: targetId}));
}

/**
* broadcast - Sends an action to all ports
*
* @param {object} action A Redux action
*/
broadcast(action) {
this.channel.sendAsyncMessage(this.outgoingMessageName, action);
}

/**
* send - Sends an action to a specific port
*
* @param {obj} action A redux action; it should contain a portID in the meta.toTarget property
*/
send(action) {
const targetId = action.meta && action.meta.toTarget;
const target = this.getTargetById(targetId);
if (!target) {
Cu.reportError(new Error(`Tried to send a message to a target (${targetId}) that was no longer around.`));
// The target is no longer around - maybe the user closed the page

This comment has been minimized.

Copy link
@k88hudson

k88hudson Apr 19, 2017

Author Member

Sometimes a target is not around anymore for expected reasons, so this isn't an error state

return;
}
target.sendAsyncMessage(this.outgoingMessageName, action);
@@ -104,8 +148,6 @@ class MessageManager {

/**
* destroyChannel - Destroys the RemotePages channel
*
* @return {type} description
*/
destroyChannel() {
this.channel.destroy();
@@ -156,6 +198,6 @@ class MessageManager {
}
}

this.MessageManager = MessageManager;
this.ActivityStreamMessageChannel = ActivityStreamMessageChannel;
this.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
this.EXPORTED_SYMBOLS = ["MessageManager", "DEFAULT_OPTIONS"];
this.EXPORTED_SYMBOLS = ["ActivityStreamMessageChannel", "DEFAULT_OPTIONS"];
@@ -14,7 +14,6 @@ this.NewTabInit = class NewTabInit {
onAction(action) {
let newAction;
switch (action.type) {
// TODO: Replace with sending a copy of the state when a NEW_TAB_LOAD action is received
case at.NEW_TAB_LOAD:
newAction = {type: at.NEW_TAB_INITIAL_STATE, data: this.store.getState()};
this.store.dispatch(ac.SendToContent(newAction, action.meta.fromTarget));
Copy path View file
@@ -8,15 +8,15 @@ const {utils: Cu} = Components;

const {redux} = Cu.import("resource://activity-stream/vendor/Redux.jsm", {});
const {reducers} = Cu.import("resource://activity-stream/common/Reducers.jsm", {});
const {MessageManager} = Cu.import("resource://activity-stream/lib/MessageManager.jsm", {});
const {ActivityStreamMessageChannel} = Cu.import("resource://activity-stream/lib/ActivityStreamMessageChannel.jsm", {});

const PREF_PREFIX = "browser.newtabpage.activity-stream.";
Cu.import("resource://gre/modules/Preferences.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 MessageManager.
* 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.
*/
@@ -38,10 +38,10 @@ this.Store = class Store {
this.feeds = new Map();
this._feedFactories = null;
this._prefHandlers = new Map();
this._mm = new MessageManager({dispatch: this.dispatch});
this._messageChannel = new ActivityStreamMessageChannel({dispatch: this.dispatch});
this._store = redux.createStore(
redux.combineReducers(reducers),
redux.applyMiddleware(this._middleware, this._mm.middleware)
redux.applyMiddleware(this._middleware, this._messageChannel.middleware)
);
}

@@ -116,7 +116,7 @@ this.Store = class Store {
}

/**
* init - Initializes the MessageManager channel, and adds feeds.
* init - Initializes the ActivityStreamMessageChannel channel, and adds feeds.
*
* @param {array} feeds An array of objects with an optional .onAction method
*/
@@ -127,7 +127,7 @@ this.Store = class Store {
this.maybeStartFeedAndListenForPrefChanges(name);
}
}
this._mm.createChannel();
this._messageChannel.createChannel();
}

/**
@@ -142,7 +142,7 @@ this.Store = class Store {
this._prefHandlers.clear();
this._feedFactories = null;
this.feeds.clear();
this._mm.destroyChannel();
this._messageChannel.destroyChannel();
}
};

@@ -1,11 +1,11 @@
const {MessageManager, DEFAULT_OPTIONS} = require("lib/MessageManager.jsm");
const {ActivityStreamMessageChannel, DEFAULT_OPTIONS} = require("lib/ActivityStreamMessageChannel.jsm");
const {addNumberReducer, GlobalOverrider} = require("test/unit/utils");
const {createStore, applyMiddleware} = require("redux");
const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");

const OPTIONS = ["pageURL, outgoingMessageName", "incomingMessageName", "dispatch"];

describe("MessageManager", () => {
describe("ActivityStreamMessageChannel", () => {
let globals;
let dispatch;
let mm;
@@ -26,28 +26,27 @@ describe("MessageManager", () => {
dispatch = globals.sandbox.spy();
});
beforeEach(() => {
mm = new MessageManager({dispatch});
mm = new ActivityStreamMessageChannel({dispatch});
});

afterEach(() => globals.reset());
after(() => globals.restore());

it("should exist", () => {
assert.ok(MessageManager);
assert.ok(ActivityStreamMessageChannel);
});
it("should apply default options", () => {
mm = new MessageManager();
mm = new ActivityStreamMessageChannel();
OPTIONS.forEach(o => assert.equal(mm[o], DEFAULT_OPTIONS[o], o));
});
it("should add options", () => {
const options = {dispatch: () => {}, pageURL: "FOO.html", outgoingMessageName: "OUT", incomingMessageName: "IN"};
mm = new MessageManager(options);
mm = new ActivityStreamMessageChannel(options);
OPTIONS.forEach(o => assert.equal(mm[o], options[o], o));
});
it("should log a message if no dispatcher was provided", () => {
mm = new MessageManager();
mm.dispatch({type: "FOO"});
assert.calledOnce(global.dump);
it("should throw an error if no dispatcher was provided", () => {
mm = new ActivityStreamMessageChannel();
assert.throws(() => mm.dispatch({type: "FOO"}));
});
describe("Creating/destroying the channel", () => {
describe("#createChannel", () => {
@@ -69,7 +68,7 @@ describe("MessageManager", () => {
assert.calledOnce(global.AboutNewTab.override);
});
it("should not override AboutNewTab if the pageURL is not about:newtab", () => {
mm = new MessageManager({pageURL: "foo.html"});
mm = new ActivityStreamMessageChannel({pageURL: "foo.html"});
mm.createChannel();
assert.notCalled(global.AboutNewTab.override);
});
@@ -93,7 +92,7 @@ describe("MessageManager", () => {
assert.calledOnce(global.AboutNewTab.reset);
});
it("should not reset AboutNewTab if the pageURL is not about:newtab", () => {
mm = new MessageManager({pageURL: "foo.html"});
mm = new ActivityStreamMessageChannel({pageURL: "foo.html"});
mm.createChannel();
mm.destroyChannel();
assert.notCalled(global.AboutNewTab.reset);
@@ -163,12 +162,12 @@ describe("MessageManager", () => {
mm.send(action, "foo");
assert.calledWith(t.sendAsyncMessage, DEFAULT_OPTIONS.outgoingMessageName, action);
});
it("should report an error if the target isn't arround", () => {
it("should not throw if the target isn't around", () => {
mm.createChannel();
// port is not added to the channel
const action = ac.SendToContent({type: "HELLO"}, "foo");
mm.send(action, "foo");
assert.calledOnce(global.Components.utils.reportError);

assert.doesNotThrow(() => mm.send(action, "foo"));
});
});
describe("#broadcast", () => {
@@ -16,13 +16,13 @@ describe("Store", () => {
Preferences.observe = sandbox.spy();
Preferences.ignore = sandbox.spy();
globals.set("Preferences", Preferences);
function MessageManager(options) {
function ActivityStreamMessageChannel(options) {
this.dispatch = options.dispatch;
this.createChannel = sandbox.spy();
this.destroyChannel = sandbox.spy();
this.middleware = sandbox.spy(s => next => action => next(action));
}
({Store, PREF_PREFIX} = injector({"lib/MessageManager.jsm": {MessageManager}}));
({Store, PREF_PREFIX} = injector({"lib/ActivityStreamMessageChannel.jsm": {ActivityStreamMessageChannel}}));
store = new Store();
});
afterEach(() => {
@@ -38,13 +38,13 @@ describe("Store", () => {
assert.property(store, "dispatch");
assert.property(store, "getState");
});
it("should create a MessageManager with the right dispatcher", () => {
assert.ok(store._mm);
assert.equal(store._mm.dispatch, store.dispatch);
it("should create a ActivityStreamMessageChannel with the right dispatcher", () => {
assert.ok(store._messageChannel);
assert.equal(store._messageChannel.dispatch, store.dispatch);
});
it("should connect the MessageManager's middleware", () => {
it("should connect the ActivityStreamMessageChannel's middleware", () => {
store.dispatch({type: "FOO"});
assert.calledOnce(store._mm.middleware);
assert.calledOnce(store._messageChannel.middleware);
});
describe("#initFeed", () => {
it("should add an instance of the feed to .feeds", () => {
@@ -144,9 +144,9 @@ describe("Store", () => {
assert.calledWith(store.maybeStartFeedAndListenForPrefChanges, "foo");
assert.calledWith(store.maybeStartFeedAndListenForPrefChanges, "bar");
});
it("should initialize the MessageManager channel", () => {
it("should initialize the ActivityStreamMessageChannel channel", () => {
store.init();
assert.calledOnce(store._mm.createChannel);
assert.calledOnce(store._messageChannel.createChannel);
});
});
describe("#uninit", () => {
@@ -163,9 +163,9 @@ describe("Store", () => {
assert.equal(store._prefHandlers.size, 0);
assert.isNull(store._feedFactories);
});
it("should destroy the MessageManager channel", () => {
it("should destroy the ActivityStreamMessageChannel channel", () => {
store.uninit();
assert.calledOnce(store._mm.destroyChannel);
assert.calledOnce(store._messageChannel.destroyChannel);
});
});
describe("#getState", () => {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.