1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * This module implements logic for creating JavaScript IPC actors, as defined
9 * in ActorManagerParent, for frame message manager contexts. See
10 * ActorManagerParent.jsm for more information.
13 var EXPORTED_SYMBOLS = ["ActorManagerChild"];
15 const { ExtensionUtils } = ChromeUtils.import(
16 "resource://gre/modules/ExtensionUtils.jsm"
18 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
19 const { XPCOMUtils } = ChromeUtils.import(
20 "resource://gre/modules/XPCOMUtils.jsm"
23 ChromeUtils.defineModuleGetter(
25 "WebNavigationFrames",
26 "resource://gre/modules/WebNavigationFrames.jsm"
29 const { DefaultMap } = ExtensionUtils;
31 const { sharedData } = Services.cpmm;
33 XPCOMUtils.defineLazyPreferenceGetter(
36 "fission.frontend.simulate-events",
39 XPCOMUtils.defineLazyPreferenceGetter(
42 "fission.frontend.simulate-messages",
46 function getMessageManager(window) {
47 return window.docShell.messageManager;
51 constructor(mm, data) {
54 this.actors = data.actors;
55 this.events = data.events;
56 this.messages = data.messages;
57 this.observers = data.observers;
59 this.instances = new Map();
63 for (let msg of this.messages.keys()) {
64 // This is directly called on the message manager
65 // because this.addMessageListener is meant to handle
66 // additions after initialization.
67 this.mm.addMessageListener(msg, this);
69 for (let topic of this.observers.keys()) {
70 Services.obs.addObserver(this, topic, true);
72 for (let { event, options, actor } of this.events) {
73 this.addEventListener(event, actor, options);
76 this.mm.addEventListener("unload", this);
80 for (let topic of this.observers.keys()) {
81 Services.obs.removeObserver(this, topic);
84 this.mm.removeEventListener("unload", this);
88 return this.mm.content;
92 // 0 for top-level windows, outerWindowId otherwise
93 return WebNavigationFrames.getFrameId(this.window);
96 get browsingContextId() {
97 return this.window.docShell.browsingContext.id;
100 addEventListener(event, actor, options) {
101 let listener = this.handleActorEvent.bind(this, actor);
102 this.mm.addEventListener(event, listener, options);
105 addMessageListener(msg, actor) {
106 let actors = this.messages.get(msg);
110 this.messages.set(msg, actors);
113 if (!actors.length) {
114 this.mm.addMessageListener(msg, this);
117 if (!actors.includes(actor)) {
122 getActor(actorName) {
123 let inst = this.instances.get(actorName);
125 let actor = this.actors.get(actorName);
128 ChromeUtils.import(actor.module, obj);
130 inst = new obj[actorName](this);
131 this.instances.set(actorName, inst);
137 if (event.type == "unload") {
142 handleActorEvent(actor, event) {
143 let targetWindow = null;
145 if (simulateEvents) {
146 targetWindow = event.target.ownerGlobal;
147 if (targetWindow != this.window) {
148 // events can't propagate across frame boundaries because the
149 // frames will be hosted on separated processes.
153 this.getActor(actor).handleEvent(event);
156 receiveMessage(message) {
157 let actors = this.messages.get(message.name);
159 if (simulateMessages) {
161 let data = message.data || {};
162 if (data.hasOwnProperty("frameId")) {
163 match = data.frameId == this.frameId;
164 } else if (data.hasOwnProperty("browsingContextId")) {
165 match = data.browsingContextId == this.browsingContextId;
167 // if no specific target was given, just dispatch it to
169 match = this.frameId == 0;
177 for (let actor of actors) {
179 this.getActor(actor).receiveMessage(message);
186 observe(subject, topic, data) {
187 let actors = this.observers.get(topic);
188 for (let actor of actors) {
190 this.getActor(actor).observe(subject, topic, data);
198 Dispatcher.prototype.QueryInterface = ChromeUtils.generateQI([
200 "nsISupportsWeakReference",
203 class SingletonDispatcher extends Dispatcher {
204 constructor(window, data) {
205 super(getMessageManager(window), data);
207 window.addEventListener("pageshow", this, { mozSystemGroup: true });
208 window.addEventListener("pagehide", this, { mozSystemGroup: true });
210 this._window = Cu.getWeakReference(window);
217 for (let actor of this.instances.values()) {
218 if (typeof actor.init === "function") {
231 for (let msg of this.messages.keys()) {
232 this.mm.removeMessageListener(msg, this);
234 for (let [event, listener, options] of this.listeners) {
235 this.mm.removeEventListener(event, listener, options);
238 for (let actor of this.instances.values()) {
239 if (typeof actor.cleanup == "function") {
252 return this._window.get();
256 if (event.type == "pageshow") {
261 } else if (event.type == "pagehide") {
267 handleActorEvent(actor, event) {
268 if (event.target.ownerGlobal == this.window) {
269 const inst = this.getActor(actor);
270 if (typeof inst.handleEvent != "function") {
272 `Unhandled event for ${actor}: ${event.type}: missing handleEvent`
275 inst.handleEvent(event);
279 addEventListener(event, actor, options) {
280 let listener = this.handleActorEvent.bind(this, actor);
281 this.listeners.push([event, listener, options]);
282 this.mm.addEventListener(event, listener, options);
286 /* globals MatchPatternSet, MozDocumentMatcher, MozDocumentObserver */
288 var ActorManagerChild = {
289 groups: new DefaultMap(group => {
290 return sharedData.get(`ChildActors:${group || ""}`);
293 singletons: new Map(),
296 let singletons = sharedData.get("ChildSingletonActors");
297 for (let [filter, data] of singletons.entries()) {
299 matches: new MatchPatternSet(filter.matches, {
300 restrictSchemes: false,
302 allFrames: filter.allFrames,
303 matchAboutBlank: filter.matchAboutBlank,
306 this.singletons.set(new MozDocumentMatcher(options), data);
309 this.observer = new MozDocumentObserver(this);
310 this.observer.observe(this.singletons.keys());
316 * MozDocumentObserver callbacks. These handle instantiating singleton actors
317 * for documents which match their MozDocumentMatcher filters.
319 onNewDocument(matcher, window) {
320 new SingletonDispatcher(window, this.singletons.get(matcher)).init();
322 onPreloadDocument(matcher, loadInfo) {},
325 * Attaches the appropriate set of actors to the given frame message manager.
327 * @param {ContentFrameMessageManager} mm
328 * The message manager to which to attach the actors.
329 * @param {string} [group]
330 * The messagemanagergroup of the <browser> to which the caller frame
331 * script belongs. This restricts the attached set of actors based on
332 * the "group" that their actor definitions specify.
334 attach(mm, group = null) {
335 new Dispatcher(mm, this.groups.get(group)).init();
338 getActor(mm, actorName) {
339 for (let dispatcher of this.dispatchers.get(mm)) {
340 let actor = dispatcher.getActor(actorName);
349 ActorManagerChild.init();