Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: (os != 'android' && appname != 'thunderbird') && (os == 'linux' && os_version == '26.04' && arch == 'x86_64')
- Manifest: toolkit/components/extensions/test/xpcshell/native_messaging.toml
/* We need xdg-native-messaging-proxy which is available in Ubuntu 26.04 and higher:
*/
"use strict";
const NATIVE_APP_BASENAME = "pingpong_nativemessaging_proxy_test";
/*
* Write native application manifest to $HOME/.mozilla/native-messaging-hosts
* We write it there because that is where the xdg-desktop-portal looks and we
* are supposedly running this inside a sandbox (snap or flatpak).
*/
const NATIVE_APP_PATH = do_get_file(
"data/pingpong_nativemessaging_test.py"
).path;
info(`This test will launch ${NATIVE_APP_PATH}`);
/*
* This extension ID cannot be registered by regular users
* because the addons.mozilla.org website won't sign such extensions.
*
* This is important, to make sure that nobody can trick a Mozilla developer
* into installing an extension and then abusing the functionality exposed in
* the native messaging host from this test.
*/
const EXTENSION_ID = "native-messaging-portal-test@tests.mozilla.org";
let pingpong_manifest = `
{
"name": "${NATIVE_APP_BASENAME}",
"description": "Example host for native messaging",
"path": "${NATIVE_APP_PATH}",
"type": "stdio",
"allowed_extensions": [ "${EXTENSION_ID}" ]
}
`;
add_setup(async function setup() {
// This environment variable must be enabled
Assert.equal(
Services.env.get("GTK_USE_PORTAL"),
"1",
"GTK_USE_PORTAL needs to be set."
);
// Change from default 0 (proxy disabled) to 2 (auto detect).
Services.prefs.setIntPref(
"widget.use-xdg-desktop-portal.native-messaging-proxy",
2
);
/* Although this test doesn't use any native app script,
* head_native_messaging.js relies on dirsvc, which this line sets up. */
await setupHosts([]);
/* Write to $HOME/.mozilla/native-messaging-hosts/ because that is the
* location with the highest precedence, where native messaging hosts should
* be looked up. Firefox looks up the path from XREUserNativeManifests, which
* effectively maps to that path on regular releases of Firefox. But in this
* test, we want the portal to look up the path, which is independent of
* Firefox, and therefore the value of XREUserNativeManifests is meaningless.
* Therefore, we write directly to ~/.mozilla/native-messaging-hosts/.
*
* Side note: in reality, Firefox should not even be able to write to that
* path when inside a sandbox (snap or flatpak). In this test, the xpcshell
* runtime (which shares the same process as the parts of Firefox that is
* being tested) is not actually being run in a real snap/flatpak. We merely
* verify that the portal works.
*/
let home = Services.env.get("HOME");
Assert.notEqual(home, "", "HOME is set");
/* This path should match XREUserNativeManifests, which is already verified
* by test_force_legacy in
* toolkit/components/extensions/test/xpcshell/test_native_messaging_paths_linux.js.
*/
let pingpong_manifest_file =
home + `/.mozilla/native-messaging-hosts/${NATIVE_APP_BASENAME}.json`;
await IOUtils.writeUTF8(pingpong_manifest_file, pingpong_manifest, {
flush: true,
});
registerCleanupFunction(async function () {
await IOUtils.remove(pingpong_manifest_file);
});
});
async function background() {
try {
let res = await browser.runtime.sendNativeMessage(
"pingpong_nativemessaging_proxy_test",
"ping"
);
browser.test.assertEq(
"native app sends pong",
res,
"Expected reply from native messaging host"
);
await browser.test.assertRejects(
browser.runtime.sendNativeMessage(
"non_existent_nativemessaging_host",
"ping"
),
"No such native application non_existent_nativemessaging_host",
"Expected error for non-existing native messaging host"
);
} catch (e) {
browser.test.fail(`Unexpected error: ${e}`);
}
browser.test.sendMessage("done");
}
const { sinon } = ChromeUtils.importESModule(
);
add_task(async function test_talk_to_native_application() {
const sinonSandbox = sinon.createSandbox();
const { NativeApp } = ChromeUtils.importESModule(
"resource://gre/modules/NativeMessaging.sys.mjs"
);
const spyInit = sinonSandbox.spy(
NativeApp.prototype,
"_doInitPortalOrNMProxy"
);
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
browser_specific_settings: { gecko: { id: EXTENSION_ID } },
permissions: ["nativeMessaging"],
},
});
await extension.startup();
await extension.awaitMessage("done");
await extension.unload();
// Check if native messaging proxy was used in the test
Assert.equal(
spyInit.thisValues[0].portalImp instanceof Ci.nsINativeMessagingProxy,
true,
"instance of nsINativeMessagingProxy"
);
Assert.equal(
spyInit.callCount,
2,
"Used nmproxy for each sendNativeMessage callImplementation used nmproxy"
);
sinonSandbox.restore();
});