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 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.
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.
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.
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
36 * - "child": The actor definition for the child side of the actor.
38 * Each "child" (or "parent", when it is implemented) actor definition may
39 * contain the following properties:
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".
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().
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
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.
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.
71 * These observers are automatically unregistered when the message manager is
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.
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.
85 * - "matchAboutBlank": If "matches" is specified, this modifies its behavior to
86 * allow it to match about:blank pages. See MozDocumentMatcher.webidl for more
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.
92 * If Fission is being simulated, and an actor needs to receive events from
93 * sub-frames, it must use "allFrames".
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;
108 moduleURI: "resource://gre/actors/AudioPlaybackParent.jsm",
112 moduleURI: "resource://gre/actors/AudioPlaybackChild.jsm",
113 observers: ["audio-playback"],
121 moduleURI: "resource://gre/actors/AutoplayParent.jsm",
125 moduleURI: "resource://gre/actors/AutoplayChild.jsm",
127 GloballyAutoplayBlocked: {},
136 moduleURI: "resource://gre/actors/BrowserElementParent.jsm",
140 moduleURI: "resource://gre/actors/BrowserElementChild.jsm",
151 moduleURI: "resource://gre/actors/DateTimePickerParent.jsm",
155 moduleURI: "resource://gre/actors/DateTimePickerChild.jsm",
157 MozOpenDateTimePicker: {},
158 MozUpdateDateTimePicker: {},
159 MozCloseDateTimePicker: {},
168 moduleURI: "resource://gre/actors/ExtFindChild.jsm",
170 "ext-Finder:CollectResults",
171 "ext-Finder:HighlightResults",
172 "ext-Finder:ClearHighlighting",
181 moduleURI: "resource://gre/actors/FindBarParent.jsm",
182 messages: ["Findbar:Keypress", "Findbar:Mouseup"],
185 moduleURI: "resource://gre/actors/FindBarChild.jsm",
187 keypress: { mozSystemGroup: true },
194 // This is the actor that responds to requests from the find toolbar and
195 // searches for matches and highlights them.
198 moduleURI: "resource://gre/actors/FinderChild.jsm",
200 "Finder:CaseSensitive",
203 "Finder:SetSearchStringToSelection",
204 "Finder:GetInitialSelection",
206 "Finder:UpdateHighlightAndMatchCount",
207 "Finder:HighlightAllChange",
208 "Finder:EnableSelection",
209 "Finder:RemoveSelection",
210 "Finder:FocusContent",
211 "Finder:FindbarClose",
212 "Finder:FindbarOpen",
214 "Finder:MatchesCount",
215 "Finder:ModalHighlightChange",
222 InlineSpellChecker: {
224 moduleURI: "resource://gre/actors/InlineSpellCheckerParent.jsm",
228 moduleURI: "resource://gre/actors/InlineSpellCheckerChild.jsm",
236 moduleURI: "resource://gre/actors/SelectParent.jsm",
240 moduleURI: "resource://gre/actors/SelectChild.jsm",
243 "mozshowdropdown-sourcetouch": {},
244 mozhidedropdown: { mozSystemGroup: true },
253 moduleURI: "resource://gre/actors/ZoomParent.jsm",
256 moduleURI: "resource://gre/actors/ZoomChild.jsm",
260 ZoomChangeUsingMouseWheel: {},
269 moduleURI: "resource://gre/actors/UAWidgetsChild.jsm",
271 UAWidgetSetupOrChange: {},
272 UAWidgetTeardown: {},
278 PurgeSessionHistory: {
280 moduleURI: "resource://gre/actors/PurgeSessionHistoryChild.jsm",
286 let LEGACY_ACTORS = {
289 module: "resource://gre/actors/ControllersChild.jsm",
290 messages: ["ControllerCommands:Do", "ControllerCommands:DoWithParams"],
296 module: "resource://gre/actors/FormSubmitChild.jsm",
299 DOMFormBeforeSubmit: {},
304 KeyPressEventModelChecker: {
306 module: "resource://gre/actors/KeyPressEventModelCheckerChild.jsm",
308 CheckKeyPressEventModel: { capture: true, mozSystemGroup: true },
315 module: "resource://gre/modules/ManifestMessagesChild.jsm",
317 "DOM:Manifest:FireAppInstalledEvent",
318 "DOM:ManifestObtainer:Obtain",
319 "DOM:WebManifest:fetchIcon",
320 "DOM:WebManifest:hasManifestLink",
327 module: "resource://gre/actors/PictureInPictureChild.jsm",
329 MozTogglePictureInPicture: { capture: true },
330 MozStopPictureInPicture: { capture: true },
334 "PictureInPicture:SetupPlayer",
335 "PictureInPicture:Play",
336 "PictureInPicture:Pause",
337 "PictureInPicture:KeyToggle",
342 PictureInPictureToggle: {
345 module: "resource://gre/actors/PictureInPictureChild.jsm",
347 UAWidgetSetupOrChange: {},
348 contextmenu: { capture: true },
355 module: "resource://gre/actors/PopupBlockingChild.jsm",
357 DOMPopupBlocked: { capture: true },
364 module: "resource://gre/actors/PrintingChild.jsm",
366 PrintingError: { capture: true },
367 printPreviewUpdate: { capture: true },
370 "Printing:Preview:Enter",
371 "Printing:Preview:Exit",
372 "Printing:Preview:Navigate",
373 "Printing:Preview:ParseDocument",
381 module: "resource://gre/actors/SelectionSourceChild.jsm",
382 messages: ["ViewSource:GetSelection"],
388 module: "resource://gre/actors/ThumbnailsChild.jsm",
390 "Browser:Thumbnail:Request",
391 "Browser:Thumbnail:CheckState",
392 "Browser:Thumbnail:GetOriginalURL",
397 UnselectedTabHover: {
399 module: "resource://gre/actors/UnselectedTabHoverChild.jsm",
401 "UnselectedTabHover:Enable": {},
402 "UnselectedTabHover:Disable": {},
404 messages: ["Browser:UnselectedTabHover"],
410 module: "resource://gre/actors/WebChannelChild.jsm",
412 WebChannelMessageToChrome: { capture: true, wantUntrusted: true },
414 messages: ["WebChannelMessageToContent"],
420 module: "resource://gre/actors/WebNavigationChild.jsm",
422 "WebNavigation:GoBack",
423 "WebNavigation:GoForward",
424 "WebNavigation:GotoIndex",
425 "WebNavigation:LoadURI",
426 "WebNavigation:Reload",
427 "WebNavigation:SetOriginAttributes",
428 "WebNavigation:Stop",
435 constructor(group, actorSide) {
437 this.actorSide = actorSide;
439 this.actors = new Map();
441 this.messages = new DefaultMap(() => []);
442 this.observers = new DefaultMap(() => []);
445 addActor(actorName, actor) {
446 actorName += this.actorSide;
447 this.actors.set(actorName, { module: actor.module });
450 for (let [event, options] of Object.entries(actor.events)) {
451 this.events.push({ actor: actorName, event, options });
454 for (let msg of actor.messages || []) {
455 this.messages.get(msg).push(actorName);
457 for (let topic of actor.observers || []) {
458 this.observers.get(topic).push(actorName);
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")),
479 for (let [actorName, actor] of Object.entries(actors)) {
480 ChromeUtils.registerWindowActor(actorName, actor);
484 addLegacyActors(actors) {
485 for (let [actorName, actor] of Object.entries(actors)) {
486 let { child } = actor;
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,
496 actorSet = this.childGroups.get(child.group || null);
499 actorSet.addActor(actorName, child);
503 let { parent } = actor;
504 this.parentGroups.get(parent.group || null).addActor(actorName, parent);
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
516 for (let [name, data] of this.childGroups) {
517 sharedData.set(`ChildActors:${name || ""}`, data);
519 sharedData.set("ChildSingletonActors", this.singletons);
523 ActorManagerParent.addActors(ACTORS);
524 ActorManagerParent.addLegacyActors(LEGACY_ACTORS);