mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-12 06:08:24 +02:00
MozReview-Commit-ID: 6ZZxDp04oPX --HG-- extra : rebase_source : 8f58895a8fc1b2999426da02918c8638bd789203
201 lines
6.6 KiB
JavaScript
201 lines
6.6 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 {utils: Cu} = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
const {
|
|
actionUtils: au,
|
|
actionCreators: ac,
|
|
actionTypes: at
|
|
} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
|
|
|
|
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) {
|
|
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"
|
|
};
|
|
|
|
this.ActivityStreamMessageChannel = 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;
|
|
|
|
this.middleware = this.middleware.bind(this);
|
|
this.onMessage = this.onMessage.bind(this);
|
|
this.onNewTabLoad = this.onNewTabLoad.bind(this);
|
|
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) {
|
|
next(action);
|
|
return;
|
|
}
|
|
if (au.isSendToContent(action)) {
|
|
this.send(action);
|
|
} else if (au.isBroadcastToContent(action)) {
|
|
this.broadcast(action);
|
|
}
|
|
next(action);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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, 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) {
|
|
// The target is no longer around - maybe the user closed the page
|
|
return;
|
|
}
|
|
target.sendAsyncMessage(this.outgoingMessageName, action);
|
|
}
|
|
|
|
/**
|
|
* getIdByTarget - Retrieve the id of a message target, if it exists in this.targets
|
|
*
|
|
* @param {obj} targetObj A message target
|
|
* @return {string|null} The unique id of the target, if it exists.
|
|
*/
|
|
getTargetById(id) {
|
|
for (let port of this.channel.messagePorts) {
|
|
if (port.portID === id) {
|
|
return port;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* createChannel - Create RemotePages channel to establishing message passing
|
|
* between the main process and child pages
|
|
*/
|
|
createChannel() {
|
|
// RemotePageManager must be disabled for about:newtab, since only one can exist at once
|
|
if (this.pageURL === ABOUT_NEW_TAB_URL) {
|
|
AboutNewTab.override();
|
|
}
|
|
this.channel = new RemotePages(this.pageURL);
|
|
this.channel.addMessageListener("RemotePage:Load", this.onNewTabLoad);
|
|
this.channel.addMessageListener("RemotePage:Unload", this.onNewTabUnload);
|
|
this.channel.addMessageListener(this.incomingMessageName, this.onMessage);
|
|
}
|
|
|
|
/**
|
|
* destroyChannel - Destroys the RemotePages channel
|
|
*/
|
|
destroyChannel() {
|
|
this.channel.destroy();
|
|
this.channel = null;
|
|
if (this.pageURL === ABOUT_NEW_TAB_URL) {
|
|
AboutNewTab.reset();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* onNewTabLoad - Handler for special RemotePage:Load message fired by RemotePages
|
|
*
|
|
* @param {obj} msg The messsage from a page that was just loaded
|
|
*/
|
|
onNewTabLoad(msg) {
|
|
this.onActionFromContent({type: at.NEW_TAB_LOAD}, msg.target.portID);
|
|
}
|
|
|
|
/**
|
|
* onNewTabUnloadLoad - Handler for special RemotePage:Unload message fired by RemotePages
|
|
*
|
|
* @param {obj} msg The messsage from a page that was just unloaded
|
|
*/
|
|
onNewTabUnload(msg) {
|
|
this.onActionFromContent({type: at.NEW_TAB_UNLOAD}, msg.target.portID);
|
|
}
|
|
|
|
/**
|
|
* onMessage - Handles custom messages from content. It expects all messages to
|
|
* be formatted as Redux actions, and dispatches them to this.store
|
|
*
|
|
* @param {obj} msg A custom message from content
|
|
* @param {obj} msg.action A Redux action (e.g. {type: "HELLO_WORLD"})
|
|
* @param {obj} msg.target A message target
|
|
*/
|
|
onMessage(msg) {
|
|
const {portID} = msg.target;
|
|
if (!msg.data || !msg.data.type) {
|
|
Cu.reportError(new Error(`Received an improperly formatted message from ${portID}`));
|
|
return;
|
|
}
|
|
let action = {};
|
|
Object.assign(action, msg.data);
|
|
// target is used to access a browser reference that came from the content
|
|
// and should only be used in feeds (not reducers)
|
|
action._target = msg.target;
|
|
this.onActionFromContent(action, portID);
|
|
}
|
|
};
|
|
|
|
this.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
|
|
this.EXPORTED_SYMBOLS = ["ActivityStreamMessageChannel", "DEFAULT_OPTIONS"];
|