summaryrefslogtreecommitdiffstats
path: root/remote/marionette/actors/MarionetteEventsParent.sys.mjs
blob: 4211f99e59712782488891dc5ba9dd88604cbfc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* 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/. */

import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs",

  Log: "chrome://remote/content/shared/Log.sys.mjs",
});

XPCOMUtils.defineLazyGetter(lazy, "logger", () =>
  lazy.Log.get(lazy.Log.TYPES.MARIONETTE)
);

// Singleton to allow forwarding events to registered listeners.
export const EventDispatcher = {
  init() {
    lazy.EventEmitter.decorate(this);
  },
};

EventDispatcher.init();

export class MarionetteEventsParent extends JSWindowActorParent {
  async receiveMessage(msg) {
    const { name, data } = msg;

    let rv;
    switch (name) {
      case "MarionetteEventsChild:PageLoadEvent":
        EventDispatcher.emit("page-load", data);
        break;
    }

    return rv;
  }
}

// Flag to check if the MarionetteEvents actors have already been registed.
let eventsActorRegistered = false;

/**
 * Register Events actors to listen for page load events via EventDispatcher.
 */
function registerEventsActor() {
  if (eventsActorRegistered) {
    return;
  }

  try {
    // Register the JSWindowActor pair for events as used by Marionette
    ChromeUtils.registerWindowActor("MarionetteEvents", {
      kind: "JSWindowActor",
      parent: {
        esModuleURI:
          "chrome://remote/content/marionette/actors/MarionetteEventsParent.sys.mjs",
      },
      child: {
        esModuleURI:
          "chrome://remote/content/marionette/actors/MarionetteEventsChild.sys.mjs",
        events: {
          beforeunload: { capture: true },
          DOMContentLoaded: { mozSystemGroup: true },
          hashchange: { mozSystemGroup: true },
          pagehide: { mozSystemGroup: true },
          pageshow: { mozSystemGroup: true },
          // popstate doesn't bubble, as such use capturing phase
          popstate: { capture: true, mozSystemGroup: true },

          click: {},
          dblclick: {},
          unload: { capture: true, createActor: false },
        },
      },

      allFrames: true,
      includeChrome: true,
    });

    eventsActorRegistered = true;
  } catch (e) {
    if (e.name === "NotSupportedError") {
      lazy.logger.warn(`MarionetteEvents actor is already registered!`);
    } else {
      throw e;
    }
  }
}

/**
 * Enable MarionetteEvents actors to start forwarding page load events from the
 * child actor to the parent actor. Register the MarionetteEvents actor if necessary.
 */
export function enableEventsActor() {
  // sharedData is replicated across processes and will be checked by
  // MarionetteEventsChild before forward events to the parent actor.
  Services.ppmm.sharedData.set("MARIONETTE_EVENTS_ENABLED", true);
  // Request to immediately flush the data to the content processes to avoid races.
  Services.ppmm.sharedData.flush();

  registerEventsActor();
}

/**
 * Disable MarionetteEvents actors to stop forwarding page load events from the
 * child actor to the parent actor.
 */
export function disableEventsActor() {
  Services.ppmm.sharedData.set("MARIONETTE_EVENTS_ENABLED", false);
  Services.ppmm.sharedData.flush();
}