summaryrefslogtreecommitdiffstats
path: root/toolkit/components/webextensions/test/xpcshell/test_ext_legacy_extension_context.js
blob: 63d5361a1e7e71ab8c59c6fb69e609be105fb394 (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
158
159
160
161
162
163
164
165
166
167
168
"use strict";

/* globals browser */

Cu.import("resource://gre/modules/Extension.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

const {LegacyExtensionContext} = Cu.import("resource://gre/modules/LegacyExtensionsUtils.jsm");

/**
 * This test case ensures that LegacyExtensionContext instances:
 *  - expose the expected API object and can join the messaging
 *    of a webextension given its addon id
 *  - the exposed API object can receive a port related to a `runtime.connect`
 *    Port created in the webextension's background page
 *  - the received Port instance can exchange messages with the background page
 *  - the received Port receive a disconnect event when the webextension is
 *    shutting down
 */
add_task(function* test_legacy_extension_context() {
  function background() {
    let bgURL = window.location.href;

    let extensionInfo = {
      bgURL,
      // Extract the assigned uuid from the background page url.
      uuid: window.location.hostname,
    };

    browser.test.sendMessage("webextension-ready", extensionInfo);

    let port;

    browser.test.onMessage.addListener(async msg => {
      if (msg == "do-send-message") {
        let reply = await browser.runtime.sendMessage("webextension -> legacy_extension message");

        browser.test.assertEq("legacy_extension -> webextension reply", reply,
                              "Got the expected message from the LegacyExtensionContext");
        browser.test.sendMessage("got-reply-message");
      } else if (msg == "do-connect") {
        port = browser.runtime.connect();

        port.onMessage.addListener(portMsg => {
          browser.test.assertEq("legacy_extension -> webextension port message", portMsg,
                                "Got the expected message from the LegacyExtensionContext");
          port.postMessage("webextension -> legacy_extension port message");
        });
      } else if (msg == "do-disconnect") {
        port.disconnect();
      }
    });
  }

  let extensionData = {
    background,
  };

  let extension = Extension.generate(extensionData);

  let waitForExtensionInfo = new Promise((resolve, reject) => {
    extension.on("test-message", function testMessageListener(kind, msg, ...args) {
      if (msg != "webextension-ready") {
        reject(new Error(`Got an unexpected test-message: ${msg}`));
      } else {
        extension.off("test-message", testMessageListener);
        resolve(args[0]);
      }
    });
  });

  // Connect to the target extension as an external context
  // using the given custom sender info.
  let legacyContext;

  extension.on("startup", function onStartup() {
    extension.off("startup", onStartup);
    legacyContext = new LegacyExtensionContext(extension);
    extension.callOnClose({
      close: () => legacyContext.unload(),
    });
  });

  yield extension.startup();

  let extensionInfo = yield waitForExtensionInfo;

  equal(legacyContext.envType, "legacy_extension",
     "LegacyExtensionContext instance has the expected type");

  ok(legacyContext.api, "Got the expected API object");
  ok(legacyContext.api.browser, "Got the expected browser property");

  let waitMessage = new Promise(resolve => {
    const {browser} = legacyContext.api;
    browser.runtime.onMessage.addListener((singleMsg, msgSender) => {
      resolve({singleMsg, msgSender});

      // Send a reply to the sender.
      return Promise.resolve("legacy_extension -> webextension reply");
    });
  });

  extension.testMessage("do-send-message");

  let {singleMsg, msgSender} = yield waitMessage;
  equal(singleMsg, "webextension -> legacy_extension message",
     "Got the expected message");
  ok(msgSender, "Got a message sender object");

  equal(msgSender.id, extensionInfo.uuid, "The sender has the expected id property");
  equal(msgSender.url, extensionInfo.bgURL, "The sender has the expected url property");

  // Wait confirmation that the reply has been received.
  yield new Promise((resolve, reject) => {
    extension.on("test-message", function testMessageListener(kind, msg, ...args) {
      if (msg != "got-reply-message") {
        reject(new Error(`Got an unexpected test-message: ${msg}`));
      } else {
        extension.off("test-message", testMessageListener);
        resolve();
      }
    });
  });

  let waitConnectPort = new Promise(resolve => {
    let {browser} = legacyContext.api;
    browser.runtime.onConnect.addListener(port => {
      resolve(port);
    });
  });

  extension.testMessage("do-connect");

  let port = yield waitConnectPort;

  ok(port, "Got the Port API object");
  ok(port.sender, "The port has a sender property");
  equal(port.sender.id, extensionInfo.uuid,
     "The port sender has the expected id property");
  equal(port.sender.url, extensionInfo.bgURL,
     "The port sender has the expected url property");

  let waitPortMessage = new Promise(resolve => {
    port.onMessage.addListener((msg) => {
      resolve(msg);
    });
  });

  port.postMessage("legacy_extension -> webextension port message");

  let msg = yield waitPortMessage;

  equal(msg, "webextension -> legacy_extension port message",
     "LegacyExtensionContext received the expected message from the webextension");

  let waitForDisconnect = new Promise(resolve => {
    port.onDisconnect.addListener(resolve);
  });

  extension.testMessage("do-disconnect");

  yield waitForDisconnect;

  do_print("Got the disconnect event on unload");

  yield extension.shutdown();
});