summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/test/mochitest/FissionTestHelperChild.sys.mjs
blob: c0695b7abb14e6120046278d53ed893756f68e3b (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// This code runs in the content process that holds the window to which
// this actor is attached. There is one instance of this class for each
// "inner window" (i.e. one per content document, including iframes/nested
// iframes).
// There is a 1:1 relationship between instances of this class and
// FissionTestHelperParent instances, and the pair are entangled such
// that they can communicate with each other regardless of which process
// they live in.

export class FissionTestHelperChild extends JSWindowActorChild {
  constructor() {
    super();
    this._msgCounter = 0;
    this._oopifResponsePromiseResolvers = [];
  }

  cw() {
    return this.contentWindow.wrappedJSObject;
  }

  initialize() {
    // This exports a bunch of things into the content window so that
    // the test can access them. Most things are scoped inside the
    // FissionTestHelper object on the window to avoid polluting the global
    // namespace.

    let cw = this.cw();
    Cu.exportFunction(
      (cond, msg) => this.sendAsyncMessage("ok", { cond, msg }),
      cw,
      { defineAs: "ok" }
    );
    Cu.exportFunction(
      (a, b, msg) => this.sendAsyncMessage("is", { a, b, msg }),
      cw,
      { defineAs: "is" }
    );

    let FissionTestHelper = Cu.createObjectIn(cw, {
      defineAs: "FissionTestHelper",
    });
    FissionTestHelper.startTestPromise = new cw.Promise(
      Cu.exportFunction(resolve => {
        this._startTestPromiseResolver = resolve;
      }, cw)
    );

    Cu.exportFunction(this.subtestDone.bind(this), FissionTestHelper, {
      defineAs: "subtestDone",
    });

    Cu.exportFunction(this.subtestFailed.bind(this), FissionTestHelper, {
      defineAs: "subtestFailed",
    });

    Cu.exportFunction(this.sendToOopif.bind(this), FissionTestHelper, {
      defineAs: "sendToOopif",
    });
    Cu.exportFunction(this.fireEventInEmbedder.bind(this), FissionTestHelper, {
      defineAs: "fireEventInEmbedder",
    });
  }

  // Called by the subtest to indicate completion to the top-level browser-chrome
  // mochitest.
  subtestDone() {
    let cw = this.cw();
    if (cw.ApzCleanup) {
      cw.ApzCleanup.execute();
    }
    this.sendAsyncMessage("Test:Complete", {});
  }

  // Called by the subtest to indicate subtest failure. Only one of subtestDone
  // or subtestFailed should be called.
  subtestFailed(msg) {
    this.sendAsyncMessage("ok", { cond: false, msg });
    this.subtestDone();
  }

  // Called by the subtest to eval some code in the OOP iframe. This returns
  // a promise that resolves to the return value from the eval.
  sendToOopif(iframeElement, stringToEval) {
    let browsingContextId = iframeElement.browsingContext.id;
    let msgId = ++this._msgCounter;
    let cw = this.cw();
    let responsePromise = new cw.Promise(
      Cu.exportFunction(resolve => {
        this._oopifResponsePromiseResolvers[msgId] = resolve;
      }, cw)
    );
    this.sendAsyncMessage("EmbedderToOopif", {
      browsingContextId,
      msgId,
      stringToEval,
    });
    return responsePromise;
  }

  // Called by OOP iframes to dispatch an event in the embedder window. This
  // can be used by the OOP iframe to asynchronously notify the embedder of
  // things that happen. The embedder can use promiseOneEvent from
  // helper_fission_utils.js to listen for these events.
  fireEventInEmbedder(eventType, data) {
    this.sendAsyncMessage("OopifToEmbedder", { eventType, data });
  }

  handleEvent(evt) {
    switch (evt.type) {
      case "FissionTestHelper:Init":
        this.initialize();
        break;
    }
  }

  receiveMessage(msg) {
    switch (msg.name) {
      case "Test:Start":
        this._startTestPromiseResolver();
        delete this._startTestPromiseResolver;
        break;
      case "FromEmbedder":
        let evalResult = this.contentWindow.eval(msg.data.stringToEval);
        this.sendAsyncMessage("OopifToEmbedder", {
          msgId: msg.data.msgId,
          evalResult,
        });
        break;
      case "FromOopif":
        if (typeof msg.data.msgId == "number") {
          if (!(msg.data.msgId in this._oopifResponsePromiseResolvers)) {
            dump(
              "Error: FromOopif got a message with unknown numeric msgId in " +
                this.contentWindow.location.href +
                "\n"
            );
          }
          this._oopifResponsePromiseResolvers[msg.data.msgId](
            msg.data.evalResult
          );
          delete this._oopifResponsePromiseResolvers[msg.data.msgId];
        } else if (typeof msg.data.eventType == "string") {
          let cw = this.cw();
          let event = new cw.Event(msg.data.eventType);
          event.data = Cu.cloneInto(msg.data.data, cw);
          this.contentWindow.dispatchEvent(event);
        } else {
          dump(
            "Warning: Unrecognized FromOopif message received in " +
              this.contentWindow.location.href +
              "\n"
          );
        }
        break;
    }
  }
}