Bug 1586801 - Use the contextual WalkerFront in _duplicateNode(). r=pbro
[gecko.git] / toolkit / modules / ActorManagerParent.jsm
blobaedb8f40722dacba5f0ccf620eeb9277321190bb
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/. */
5 "use strict";
7 /**
8  * This module implements logic for managing JavaScript actor instances bound to
9  * message managers. It handles lazily instantiating those actors based on
10  * DOM events, IPC messages, or observer notifications, and is meant to entirely
11  * replace the existing concept of frame scripts.
12  *
13  * All actors must be registered in the parent process, before the first child
14  * process starts. Once all actors have been registered, the actor data is
15  * mangled into a form which can be handled efficiently during content process
16  * startup, and shared with all content processes. Frame scripts in those
17  * content processes attach that data to frame message managers via
18  * ActorManagerChild, which handles instantiating and dispatching to those
19  * actors as necessary.
20  *
21  *
22  * Each actor is a class which lives in a JSM, and has a constructor which takes
23  * a single message manager argument. Each actor may conceptually have both
24  * Child and Parent variants, but only Child variants are currently implemented.
25  * The parent and child variants live in separate JSMs, and have separate class
26  * names, each of which have Child or Parent appended to their names, as
27  * appropriate. For instance, the AudioPlayback actor has a child instance named
28  * AudioPlaybackChild which lives in AudioPlaybackChild.jsm.
29  *
30  *
31  * Actors are defined by calling ActorManagerParent.addActors, with an object
32  * containing a property for each actor being defined, whose value is an object
33  * describing how the actor should be loaded. That object may have the following
34  * properties:
35  *
36  * - "child": The actor definition for the child side of the actor.
37  *
38  * Each "child" (or "parent", when it is implemented) actor definition may
39  * contain the following properties:
40  *
41  * - "module": The URI from which the modules is loaded. This should be a
42  *   resource: URI, ideally beginning with "resource://gre/actors/" or
43  *   "resource:///actors/", with a filename matching the name of the actor for
44  *   the given side. So, the child side of the AudioPlayback actor should live at
45  *   "resource://gre/actors/AudioPlaybackChild.jsm".
46  *
47  * - "group": A group name which restricts the message managers to which this
48  *   actor may be attached. This should match the "messagemanagergroup"
49  *   attribute of a <browser> element. Frame scripts are responsible for
50  *   attaching the appropriate actors to the appropriate browsers using
51  *   ActorManagerChild.attach().
52  *
53  * - "events": An object containing a property for each event the actor will
54  *   listen for, with an options object, as accepted by addEventListener, as its
55  *   value. For each such property, an event listener will be added to the
56  *   message manager[1] for the given event name, which delegates to the actor's
57  *   handleEvent method.
58  *
59  * - "messages": An array of message manager message names. For each message
60  *   name in the list, a message listener will be added to the frame message
61  *   manager, and the messages it receives will be delegated to the actor's
62  *   receiveMessage method.
63  *
64  * - "observers": An array of observer topics. A global observer will be added
65  *   for each topic in the list, and observer notifications for it will be
66  *   delegated to the actor's observe method. Note that observers are global in
67  *   nature, and these notifications may therefore have nothing to do with the
68  *   message manager the actor is bound to. The actor itself is responsible for
69  *   filtering the notifications that apply to it.
70  *
71  *   These observers are automatically unregistered when the message manager is
72  *   destroyed.
73  *
74  * - "matches": An array of URL match patterns (as accepted by the MatchPattern
75  *   class in MatchPattern.webidl) which restrict which pages the actor may be
76  *   instantiated for. If this is defined, the actor will only receive DOM
77  *   events sent to windows which match this pattern, and will only receive
78  *   message manager messages for frame message managers which are currently
79  *   hosting a matching DOM window.
80  *
81  * - "allFrames": this modifies its behavior to allow it to match sub-frames
82  *   as well as top-level frames. If "allFrames" is not specified, it will
83  *   match only top-level frames.
84  *
85  * - "matchAboutBlank": If "matches" is specified, this modifies its behavior to
86  *   allow it to match about:blank pages. See MozDocumentMatcher.webidl for more
87  *   information.
88  *
89  * [1]: For actors which specify "matches" or "allFrames", the listener will be
90  *      added to the DOM window rather than the frame message manager.
91  *
92  * If Fission is being simulated, and an actor needs to receive events from
93  * sub-frames, it must use "allFrames".
94  */
96 var EXPORTED_SYMBOLS = ["ActorManagerParent"];
98 const { ExtensionUtils } = ChromeUtils.import(
99   "resource://gre/modules/ExtensionUtils.jsm"
101 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
103 const { DefaultMap } = ExtensionUtils;
105 let ACTORS = {
106   AudioPlayback: {
107     parent: {
108       moduleURI: "resource://gre/actors/AudioPlaybackParent.jsm",
109     },
111     child: {
112       moduleURI: "resource://gre/actors/AudioPlaybackChild.jsm",
113       observers: ["audio-playback"],
114     },
116     allFrames: true,
117   },
119   Autoplay: {
120     parent: {
121       moduleURI: "resource://gre/actors/AutoplayParent.jsm",
122     },
124     child: {
125       moduleURI: "resource://gre/actors/AutoplayChild.jsm",
126       events: {
127         GloballyAutoplayBlocked: {},
128       },
129     },
131     allFrames: true,
132   },
134   BrowserElement: {
135     parent: {
136       moduleURI: "resource://gre/actors/BrowserElementParent.jsm",
137     },
139     child: {
140       moduleURI: "resource://gre/actors/BrowserElementChild.jsm",
141       events: {
142         DOMWindowClose: {},
143       },
144     },
146     allFrames: true,
147   },
149   DateTimePicker: {
150     parent: {
151       moduleURI: "resource://gre/actors/DateTimePickerParent.jsm",
152     },
154     child: {
155       moduleURI: "resource://gre/actors/DateTimePickerChild.jsm",
156       events: {
157         MozOpenDateTimePicker: {},
158         MozUpdateDateTimePicker: {},
159         MozCloseDateTimePicker: {},
160       },
161     },
163     allFrames: true,
164   },
166   ExtFind: {
167     child: {
168       moduleURI: "resource://gre/actors/ExtFindChild.jsm",
169       messages: [
170         "ext-Finder:CollectResults",
171         "ext-Finder:HighlightResults",
172         "ext-Finder:ClearHighlighting",
173       ],
174     },
176     allFrames: true,
177   },
179   FindBar: {
180     parent: {
181       moduleURI: "resource://gre/actors/FindBarParent.jsm",
182       messages: ["Findbar:Keypress", "Findbar:Mouseup"],
183     },
184     child: {
185       moduleURI: "resource://gre/actors/FindBarChild.jsm",
186       events: {
187         keypress: { mozSystemGroup: true },
188       },
189     },
191     allFrames: true,
192   },
194   // This is the actor that responds to requests from the find toolbar and
195   // searches for matches and highlights them.
196   Finder: {
197     child: {
198       moduleURI: "resource://gre/actors/FinderChild.jsm",
199       messages: [
200         "Finder:CaseSensitive",
201         "Finder:EntireWord",
202         "Finder:Find",
203         "Finder:SetSearchStringToSelection",
204         "Finder:GetInitialSelection",
205         "Finder:Highlight",
206         "Finder:UpdateHighlightAndMatchCount",
207         "Finder:HighlightAllChange",
208         "Finder:EnableSelection",
209         "Finder:RemoveSelection",
210         "Finder:FocusContent",
211         "Finder:FindbarClose",
212         "Finder:FindbarOpen",
213         "Finder:KeyPress",
214         "Finder:MatchesCount",
215         "Finder:ModalHighlightChange",
216       ],
217     },
219     allFrames: true,
220   },
222   InlineSpellChecker: {
223     parent: {
224       moduleURI: "resource://gre/actors/InlineSpellCheckerParent.jsm",
225     },
227     child: {
228       moduleURI: "resource://gre/actors/InlineSpellCheckerChild.jsm",
229     },
231     allFrames: true,
232   },
234   Select: {
235     parent: {
236       moduleURI: "resource://gre/actors/SelectParent.jsm",
237     },
239     child: {
240       moduleURI: "resource://gre/actors/SelectChild.jsm",
241       events: {
242         mozshowdropdown: {},
243         "mozshowdropdown-sourcetouch": {},
244         mozhidedropdown: { mozSystemGroup: true },
245       },
246     },
248     allFrames: true,
249   },
251   Zoom: {
252     parent: {
253       moduleURI: "resource://gre/actors/ZoomParent.jsm",
254     },
255     child: {
256       moduleURI: "resource://gre/actors/ZoomChild.jsm",
257       events: {
258         FullZoomChange: {},
259         TextZoomChange: {},
260         ZoomChangeUsingMouseWheel: {},
261       },
262     },
264     allFrames: true,
265   },
267   UAWidgets: {
268     child: {
269       moduleURI: "resource://gre/actors/UAWidgetsChild.jsm",
270       events: {
271         UAWidgetSetupOrChange: {},
272         UAWidgetTeardown: {},
273       },
274     },
276     allFrames: true,
277   },
278   PurgeSessionHistory: {
279     child: {
280       moduleURI: "resource://gre/actors/PurgeSessionHistoryChild.jsm",
281     },
282     allFrames: true,
283   },
286 let LEGACY_ACTORS = {
287   Controllers: {
288     child: {
289       module: "resource://gre/actors/ControllersChild.jsm",
290       messages: ["ControllerCommands:Do", "ControllerCommands:DoWithParams"],
291     },
292   },
294   FormSubmit: {
295     child: {
296       module: "resource://gre/actors/FormSubmitChild.jsm",
297       allFrames: true,
298       events: {
299         DOMFormBeforeSubmit: {},
300       },
301     },
302   },
304   KeyPressEventModelChecker: {
305     child: {
306       module: "resource://gre/actors/KeyPressEventModelCheckerChild.jsm",
307       events: {
308         CheckKeyPressEventModel: { capture: true, mozSystemGroup: true },
309       },
310     },
311   },
313   ManifestMessages: {
314     child: {
315       module: "resource://gre/modules/ManifestMessagesChild.jsm",
316       messages: [
317         "DOM:Manifest:FireAppInstalledEvent",
318         "DOM:ManifestObtainer:Obtain",
319         "DOM:WebManifest:fetchIcon",
320         "DOM:WebManifest:hasManifestLink",
321       ],
322     },
323   },
325   PictureInPicture: {
326     child: {
327       module: "resource://gre/actors/PictureInPictureChild.jsm",
328       events: {
329         MozTogglePictureInPicture: { capture: true },
330         MozStopPictureInPicture: { capture: true },
331       },
333       messages: [
334         "PictureInPicture:SetupPlayer",
335         "PictureInPicture:Play",
336         "PictureInPicture:Pause",
337         "PictureInPicture:KeyToggle",
338       ],
339     },
340   },
342   PictureInPictureToggle: {
343     child: {
344       allFrames: true,
345       module: "resource://gre/actors/PictureInPictureChild.jsm",
346       events: {
347         UAWidgetSetupOrChange: {},
348         contextmenu: { capture: true },
349       },
350     },
351   },
353   PopupBlocking: {
354     child: {
355       module: "resource://gre/actors/PopupBlockingChild.jsm",
356       events: {
357         DOMPopupBlocked: { capture: true },
358       },
359     },
360   },
362   Printing: {
363     child: {
364       module: "resource://gre/actors/PrintingChild.jsm",
365       events: {
366         PrintingError: { capture: true },
367         printPreviewUpdate: { capture: true },
368       },
369       messages: [
370         "Printing:Preview:Enter",
371         "Printing:Preview:Exit",
372         "Printing:Preview:Navigate",
373         "Printing:Preview:ParseDocument",
374         "Printing:Print",
375       ],
376     },
377   },
379   SelectionSource: {
380     child: {
381       module: "resource://gre/actors/SelectionSourceChild.jsm",
382       messages: ["ViewSource:GetSelection"],
383     },
384   },
386   Thumbnails: {
387     child: {
388       module: "resource://gre/actors/ThumbnailsChild.jsm",
389       messages: [
390         "Browser:Thumbnail:Request",
391         "Browser:Thumbnail:CheckState",
392         "Browser:Thumbnail:GetOriginalURL",
393       ],
394     },
395   },
397   UnselectedTabHover: {
398     child: {
399       module: "resource://gre/actors/UnselectedTabHoverChild.jsm",
400       events: {
401         "UnselectedTabHover:Enable": {},
402         "UnselectedTabHover:Disable": {},
403       },
404       messages: ["Browser:UnselectedTabHover"],
405     },
406   },
408   WebChannel: {
409     child: {
410       module: "resource://gre/actors/WebChannelChild.jsm",
411       events: {
412         WebChannelMessageToChrome: { capture: true, wantUntrusted: true },
413       },
414       messages: ["WebChannelMessageToContent"],
415     },
416   },
418   WebNavigation: {
419     child: {
420       module: "resource://gre/actors/WebNavigationChild.jsm",
421       messages: [
422         "WebNavigation:GoBack",
423         "WebNavigation:GoForward",
424         "WebNavigation:GotoIndex",
425         "WebNavigation:LoadURI",
426         "WebNavigation:Reload",
427         "WebNavigation:SetOriginAttributes",
428         "WebNavigation:Stop",
429       ],
430     },
431   },
434 class ActorSet {
435   constructor(group, actorSide) {
436     this.group = group;
437     this.actorSide = actorSide;
439     this.actors = new Map();
440     this.events = [];
441     this.messages = new DefaultMap(() => []);
442     this.observers = new DefaultMap(() => []);
443   }
445   addActor(actorName, actor) {
446     actorName += this.actorSide;
447     this.actors.set(actorName, { module: actor.module });
449     if (actor.events) {
450       for (let [event, options] of Object.entries(actor.events)) {
451         this.events.push({ actor: actorName, event, options });
452       }
453     }
454     for (let msg of actor.messages || []) {
455       this.messages.get(msg).push(actorName);
456     }
457     for (let topic of actor.observers || []) {
458       this.observers.get(topic).push(actorName);
459     }
460   }
463 const { sharedData } = Services.ppmm;
465 var ActorManagerParent = {
466   // Actor sets which should be loaded in the child side, keyed by
467   // "messagemanagergroup".
468   childGroups: new DefaultMap(group => new ActorSet(group, "Child")),
469   // Actor sets which should be loaded in the parent side, keyed by
470   // "messagemanagergroup".
471   parentGroups: new DefaultMap(group => new ActorSet(group, "Parent")),
473   // Singleton actor sets, which should be loaded only for documents which match
474   // a specific pattern. The keys in this map are plain objects specifying
475   // filter keys as understood by MozDocumentMatcher.
476   singletons: new DefaultMap(() => new ActorSet(null, "Child")),
478   addActors(actors) {
479     for (let [actorName, actor] of Object.entries(actors)) {
480       ChromeUtils.registerWindowActor(actorName, actor);
481     }
482   },
484   addLegacyActors(actors) {
485     for (let [actorName, actor] of Object.entries(actors)) {
486       let { child } = actor;
487       {
488         let actorSet;
489         if (child.matches || child.allFrames) {
490           actorSet = this.singletons.get({
491             matches: child.matches || ["<all_urls>"],
492             allFrames: child.allFrames,
493             matchAboutBlank: child.matchAboutBlank,
494           });
495         } else {
496           actorSet = this.childGroups.get(child.group || null);
497         }
499         actorSet.addActor(actorName, child);
500       }
502       if (actor.parent) {
503         let { parent } = actor;
504         this.parentGroups.get(parent.group || null).addActor(actorName, parent);
505       }
506     }
507   },
509   /**
510    * Serializes the current set of registered actors into ppmm.sharedData, for
511    * use by ActorManagerChild. This must be called before any frame message
512    * managers have been created. It will have no effect on existing message
513    * managers.
514    */
515   flush() {
516     for (let [name, data] of this.childGroups) {
517       sharedData.set(`ChildActors:${name || ""}`, data);
518     }
519     sharedData.set("ChildSingletonActors", this.singletons);
520   },
523 ActorManagerParent.addActors(ACTORS);
524 ActorManagerParent.addLegacyActors(LEGACY_ACTORS);