summaryrefslogtreecommitdiffstats
path: root/mobile/android/components/extensions/test/xpcshell/test_ext_native_messaging_permissions.js
blob: 63f64b487e9f6bc14429945ee1efc59352ee6635 (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
"use strict";

const server = createHttpServer({ hosts: ["example.com"] });
server.registerPathHandler("/dum", (request, response) => {
  response.setStatusLine(request.httpVersion, 200, "OK");
  response.setHeader("Content-Type", "text/html; charset=utf-8", false);
  response.write("<!DOCTYPE html><html></html>");
});

async function testNativeMessaging({
  isPrivileged = false,
  permissions,
  testBackground,
  testContent,
}) {
  async function runTest(testFn, completionMessage) {
    try {
      dump(`Running test before sending ${completionMessage}\n`);
      await testFn();
    } catch (e) {
      browser.test.fail(`Unexpected error: ${e}`);
    }
    browser.test.sendMessage(completionMessage);
  }
  const extension = ExtensionTestUtils.loadExtension({
    isPrivileged,
    background: `(${runTest})(${testBackground}, "background_done");`,
    manifest: {
      content_scripts: [
        {
          run_at: "document_end",
          js: ["test.js"],
          matches: ["http://example.com/dummy"],
        },
      ],
      permissions,
    },
    files: {
      "test.js": `(${runTest})(${testContent}, "content_done");`,
    },
  });

  // Run background script.
  await extension.startup();
  await extension.awaitMessage("background_done");

  // Run content script.
  const page = await ExtensionTestUtils.loadContentPage(
    "http://example.com/dummy"
  );
  await extension.awaitMessage("content_done");
  await page.close();

  await extension.unload();
}

// Checks that unprivileged extensions cannot use any of the nativeMessaging
// APIs on Android.
add_task(async function test_nativeMessaging_unprivileged() {
  function testScript() {
    browser.test.assertEq(
      browser.runtime.connectNative,
      undefined,
      "connectNative should not be available in unprivileged extensions"
    );
    browser.test.assertEq(
      browser.runtime.sendNativeMessage,
      undefined,
      "sendNativeMessage should not be available in unprivileged extensions"
    );
  }

  const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
    await testNativeMessaging({
      isPrivileged: false,
      permissions: [
        "geckoViewAddons",
        "nativeMessaging",
        "nativeMessagingFromContent",
      ],
      testBackground: testScript,
      testContent: testScript,
    });
  });
  AddonTestUtils.checkMessages(messages, {
    expected: [
      { message: /Invalid extension permission: geckoViewAddons/ },
      { message: /Invalid extension permission: nativeMessaging/ },
      { message: /Invalid extension permission: nativeMessagingFromContent/ },
    ],
  });
});

// Checks that privileged extensions can still not use native messaging without
// the geckoViewAddons permission.
add_task(async function test_geckoViewAddons_missing() {
  const ERROR_NATIVE_MESSAGE_FROM_BACKGROUND =
    "Native manifests are not supported on android";
  const ERROR_NATIVE_MESSAGE_FROM_CONTENT =
    /^Native messaging not allowed: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\}$/;

  async function testBackground() {
    await browser.test.assertRejects(
      browser.runtime.sendNativeMessage("dummy_nativeApp", "DummyMsg"),
      // Redacted error: ERROR_NATIVE_MESSAGE_FROM_BACKGROUND
      "An unexpected error occurred",
      "Background script cannot use nativeMessaging without geckoViewAddons"
    );
  }
  async function testContent() {
    await browser.test.assertRejects(
      browser.runtime.sendNativeMessage("dummy_nativeApp", "DummyMsg"),
      // Redacted error: ERROR_NATIVE_MESSAGE_FROM_CONTENT
      "An unexpected error occurred",
      "Content script cannot use nativeMessaging without geckoViewAddons"
    );
  }

  const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
    await testNativeMessaging({
      isPrivileged: true,
      permissions: ["nativeMessaging", "nativeMessagingFromContent"],
      testBackground,
      testContent,
    });
  });
  AddonTestUtils.checkMessages(messages, {
    expected: [
      { errorMessage: ERROR_NATIVE_MESSAGE_FROM_BACKGROUND },
      { errorMessage: ERROR_NATIVE_MESSAGE_FROM_CONTENT },
    ],
  });
});

// Checks that privileged extensions cannot use native messaging from content
// without the nativeMessagingFromContent permission.
add_task(async function test_nativeMessagingFromContent_missing() {
  const ERROR_NATIVE_MESSAGE_FROM_CONTENT_NO_PERM =
    /^Unexpected messaging sender: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\}$/;
  function testBackground() {
    // sendNativeMessage / connectNative are expected to succeed, but we
    // are not testing that here because XpcshellTestRunnerService does not
    // have a WebExtension.MessageDelegate that handles the message.
    // There are plenty of mochitests that rely on connectNative, so we are
    // not testing that here.
  }
  async function testContent() {
    await browser.test.assertRejects(
      browser.runtime.sendNativeMessage("dummy_nativeApp", "DummyMsg"),
      // Redacted error: ERROR_NATIVE_MESSAGE_FROM_CONTENT_NO_PERM
      "An unexpected error occurred",
      "Trying to get through to native messaging but without luck"
    );
  }

  const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
    await testNativeMessaging({
      isPrivileged: true,
      permissions: ["geckoViewAddons", "nativeMessaging"],
      testBackground,
      testContent,
    });
  });
  AddonTestUtils.checkMessages(messages, {
    expected: [{ errorMessage: ERROR_NATIVE_MESSAGE_FROM_CONTENT_NO_PERM }],
  });
});