Native messaging enables an extension to exchange messages with a native application installed on the user's computer. This enables native applications to provide a service to extensions without needing to be reachable over the web. One common example here is password managers: the native application manages storage and encryption of passwords, and communicates with the extension to populate web forms. Native messaging also enables extensions to access resources that are not accessible through WebExtension APIs, such as some particular piece of hardware.
The native application is not installed or managed by the browser: it's installed using the underlying operating system's installation machinery. Along with the native application itself, you'll need to provide a JSON file called the "host manifest" or "app manifest", and install it in a defined location on the user's computer. The app manifest file describes how the browser can connect to the native application.
The extension must request the "nativeMessaging" permission in its manifest.json file. Conversely, the native application must grant permission for the extension by including its ID in the "allowed_extensions" field of the app manifest.
After that the extension can exchange JSON messages with the native application using a set of functions in the runtime
API. On the native app side, messages are received using standard input (stdin) and sent using standard output (stdout).
Support for native messaging in extensions is mostly compatible with Chrome, with two main differences:
- The app manifest lists "allowed_extensions" as an array of app IDs, while Chrome lists "allowed_origins", as an array of "chrome-extension" URLs.
- The app manifest is stored in a different location, compared to Chrome.
There's a complete example in the "native-messaging" directory of the "webextensions-examples" repository on GitHub. Most of the sample code in this article is taken directly from that example.
Setup
Extension manifest
If you want your extension to communicate with a native application, then:
- You must set the "nativeMessaging" permission in its manifest.json file.
- You should probably specify your add-on ID explicitly, using the applications manifest key (This is because the app's manifest will identify the set of extensions that are allowed to connect to it by listing their IDs).
Here's an example manifest.json file:
{ "description": "Native messaging example extension", "manifest_version": 2, "name": "Native messaging example", "version": "1.0", "icons": { "48": "icons/message.svg" }, "applications": { "gecko": { "id": "ping_pong@example.org", "strict_min_version": "50.0" } }, "background": { "scripts": ["background.js"] }, "browser_action": { "default_icon": "icons/message.svg" }, "permissions": ["nativeMessaging"] }
App manifest
The app manifest describes to the browser how it can connect to the native application.
The app manifest file must be installed along with the native application. That is, the browser reads and validates app manifest files but it does not install or manage them. Thus the security model for when and how these files are installed and updated is much more like that for native applications than that for extensions using WebExtension APIs.
The app manifest contains a single JSON object with the following properties:
Name | Type | Description |
---|---|---|
name |
String |
Name of the native application. This must match the name passed into On OS X and Linux, it must also match the app manifest's filename (excluding the ".json" extension). On Windows, it must match the name of the registry key you create, that contains the location of the app manifest. The name may only contain lowercase alphanumeric characters, underscores and dots. It may not start or end with a dot, and a dot cannot be followed by another dot. |
description |
String | Description of the native application. |
path |
String |
Path to the native application. On Windows, this may be relative to the manifest itself. On OS X and Linux it must be absolute. |
type |
String |
Describes the method used to connect the extension with the app. Currently only one value can be given here, "stdio", which indicates that messages are received by the app using standard input (stdin) and sent using standard output (stdout). |
allowed_extensions |
Array of String |
An array of Add-on ID values. Each value represents an extension which is allowed to communicate with this native application. Note that this means you will probably want to include the applications key in your extension's manifest.json file, so you can set an explicit ID during development. |
For example, here's a manifest for the "ping_pong" native application:
{ "name": "ping_pong", "description": "Example host for native messaging", "path": "/path/to/native-messaging/app/ping_pong.py", "type": "stdio", "allowed_extensions": [ "ping_pong@example.org" ] }
This allows the extension whose ID is "ping_pong@example.org" to connect, by passing the name "ping_pong" into the relevant runtime
API function. The application itself is at "/path/to/native-messaging/app/ping_pong.py".
Note for Windows: in the example above, the native application is a Python script. It can be difficult to get Windows to run Python scripts reliably in this way, so an alternative is to provide a .bat file, and link to that from the manifest:
{ "name": "ping_pong", "description": "Example host for native messaging", "path": "c:\\path\\to\\native-messaging\\app\\ping_pong_win.bat", "type": "stdio", "allowed_extensions": [ "ping_pong@example.org" ] }
The batch file then invokes the Python script:
@echo off python -u "c:\\path\\to\\native-messaging\\app\\ping_pong.py"
App manifest location
The app manifest needs to be stored in a known location on the user's computer, so the browser knows where to find it. This location is different for different operating systems, and different depending on whether the app should be made available globally or only to a single user.
In all the options below, "<name>" is the value of "name" in the manifest: so, "ping_pong" in the example above.
Windows
For global visibility, create a registry key with the following name:
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
The key should have a single default value, which is the path to the manifest.
Note that this key should not be created under Wow6432Node, even if the app is 32-bit. The browser will always look for the key under the "native" view of the registry, not the 32-bit emulation. To ensure that the key is created in the "native" view, you can pass the KEY_WOW64_64KEY or KEY_WOW64_32KEY flags into RegCreateKeyEx. See Accessing an Alternate Registry View.
For per-user visibility, create a registry key with the following name:
HKEY_CURRENT_USER\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
The key should have a single default value, which is the path to the manifest.
For example, if the native application's name is "ping_pong" and its app manifest is at "C:\path\to\ping_pong.json", then you could add the key using the following REG ADD command:
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Mozilla\NativeMessagingHosts\ping_pong" /ve /d "C:\path\to\ping_pong.json" /f
Mac OS X
For global visibility, store the manifest in:
/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
For per-user visibility, store the manifest in:
~/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
Linux
For global visibility, store the manifest in any of:
/usr/lib/mozilla/native-messaging-hosts/<name>.json /usr/lib64/mozilla/native-messaging-hosts/<name>.json
For per-user visibility, store the manifest in:
~/.mozilla/native-messaging-hosts/<name>.json
Exchanging messages
Given the above setup, an extension can exchange JSON messages with a native application.
Extension side
If you've used the messaging APIs to communicate with content scripts, this should look very familiar. There are two patterns: connection-based messaging and connectionless messaging.
Connection-based messaging
With this pattern you call runtime.connectNative()
, passing the name of the application (the value of the "name" property in the app's manifest). This launches the application if it is not already running and returns a runtime.Port
object to the extension.
The native app is passed two arguments when it starts:
- the complete path to the app manifest
- (new in Firefox 55) the ID (as given in the applications manifest.json key) of the add-on that started it.
The application stays running until the extension calls Port.disconnect()
or the page that connected to it is closed.
To send messages using Port
, call its postMessage()
function, passing the JSON message to send. To listen for messages using Port
, add the listener using its onMessage.addListener()
function.
Here's an example background script that establishes a connection with the "ping_pong" app, listens for messages from it, then sends it a "ping" message whenever the user clicks the browser action:
/* On startup, connect to the "ping_pong" app. */ var port = browser.runtime.connectNative("ping_pong"); /* Listen for messages from the app. */ port.onMessage.addListener((response) => { console.log("Received: " + response); }); /* On a click on the browser action, send the app a message. */ browser.browserAction.onClicked.addListener(() => { console.log("Sending: ping"); port.postMessage("ping"); });
Connectionless messaging
With this pattern you call runtime.sendNativeMessage()
, passing it:
- the name of the application
- the JSON message to send
- optionally, a callback.
A new instance of the app is created for each message. The app is passed two arguments when it starts:
- the complete path to the app manifest
- (new in Firefox 55) the ID (as given in the applications manifest.json key) of the add-on that started it.
The first message sent by the app is treated as a response to the sendNativeMessage()
call, and will be passed into the callback.
Here's the example above, rewritten to use runtime.sendNativeMessage()
:
function onResponse(response) { console.log("Received " + response); } function onError(error) { console.log(`Error: ${error}`); } /* On a click on the browser action, send the app a message. */ browser.browserAction.onClicked.addListener(() => { console.log("Sending: ping"); var sending = browser.runtime.sendNativeMessage( "ping_pong", "ping"); sending.then(onResponse, onError); });
App side
On the application side, you use standard input to receive messages and standard output to send them.
Each message is serialized using JSON, UTF-8 encoded and is preceded with a 32-bit value containing the message length in native byte order.
The maximum size of a single message from the application is 1 MB. The maximum size of a message sent to the application is 4 GB.
Here's an example written in Python. It listens for messages from the extension. If the message is "ping", then it responds with a message "pong":
#!/usr/bin/python -u # Note that running python with the `-u` flag is required on Windows, # in order to ensure that stdin and stdout are opened in binary, rather # than text, mode. import sys, json, struct # Read a message from stdin and decode it. def getMessage(): rawLength = sys.stdin.read(4) if len(rawLength) == 0: sys.exit(0) messageLength = struct.unpack('@I', rawLength)[0] message = sys.stdin.read(messageLength) return json.loads(message) # Encode a message for transmission, given its content. def encodeMessage(messageContent): encodedContent = json.dumps(messageContent) encodedLength = struct.pack('@I', len(encodedContent)) return {'length': encodedLength, 'content': encodedContent} # Send an encoded message to stdout. def sendMessage(encodedMessage): sys.stdout.write(encodedMessage['length']) sys.stdout.write(encodedMessage['content']) sys.stdout.flush() while True: receivedMessage = getMessage() if (receivedMessage == "ping"): sendMessage(encodeMessage("pong"))
Closing the native app
If you connected to the native application using runtime.connectNative()
, then it stays running until the extension calls Port.disconnect()
or the page that connected to it is closed. If you started the native application by sending runtime.sendNativeMessage()
, then it is closed after it has received the message and sent a response.
To close the native application:
- On *nix systems like OS X and Linux, the browser sends SIGTERM to the native application, then SIGKILL after the application has had a chance to exit gracefully. These signals propagate to any subprocesses unless they break away into a new process group.
- On Windows, the browser puts the native application's process into a Job object, and kills the job. If the native application launches any additional processes and wants them to remain open after the native application itself is killed, then the native application must launch the additional process with the
CREATE_BREAKAWAY_FROM_JOB
flag.
Troubleshooting
If something goes wrong, check the browser console. If the native application sends any output to stderr, the browser will redirect it to the browser console. So if you've got as far as launching the native application, you will see any error messages it emits.
If you haven't managed to run the application, you should see an error message giving you a clue about the problem.
"No such native application <name>"
- Check that the name passed to
runtime.connectNative()
matches the name in the app manifest - OS X/Linux: check that name of the app manifest is <name>.json.
- Windows: check that the registry key is in the correct place, and that its name matches the name in the app manifest.
- Windows: check that the path given in the registry key points to the app manifest.
"Error: Invalid application <name>"
- Check that the application's name contains no invalid characters.
"'python' is not recognized as an internal or external command, ..."
- Windows: if your application is a Python script, check that you have Python installed and have your path set up for it.
"File at path <path> does not exist, or is not executable"
- If you see this, then the app manifest has been found successfully.
- Check that the "path" in the app's manifest is correct.
- Windows: check that you've escaped the path separators ("c:\\path\\to\\file").
- Check that the app is at the location pointed to by the "path" property in the app's manifest.
- Check that the app is executable.
"This extension does not have permission to use native application <name>"
- Check that the "allowed_extensions" key in the app manifest contains the add-on's ID.
"TypeError: browser.runtime.connectNative is not a function"
- Check that the extension has the "nativeMessaging" permission
"[object Object] NativeMessaging.jsm:218"
- There was a problem starting the application.
Chrome incompatibilities
Command-line arguments
On Linux and Mac, Chrome passes one argument to the native app, which is the origin of the extension that started it, in the form: chrome-extension://[extensionID]
. This enables the app to identify the extension.
On Windows, Chrome passes two arguments: the first is the origin of the extension, and the second is a handle to the Chrome native window that started the app.
allowed_extensions
In Chrome, the allowed_extensions
key in the app manifest is called allowed_origins
instead.
App manifest location
Chrome expects to find the app manifest in a different place. See Native messaging host location in the Chrome docs.