From 02212cbc3180f896a0061f46a062af0d4d00cef2 Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Wed, 21 Jan 2026 11:43:19 +1300 Subject: [PATCH 1/4] Migrate native-messaging to MV3 --- native-messaging/README.md | 30 ++++++++++++++++++++------- native-messaging/add-on/background.js | 2 +- native-messaging/add-on/manifest.json | 4 ++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/native-messaging/README.md b/native-messaging/README.md index 1cb028dc..b6278c4e 100644 --- a/native-messaging/README.md +++ b/native-messaging/README.md @@ -1,8 +1,24 @@ -This is a very simple example of how to use [native messaging](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_messaging) to exchange messages between a WebExtension and a native application. +# native-messaging -The WebExtension, which can be found under "add-on", connects to the native application and listens to messages from it. It then sends a message to the native application when the user clicks on the WebExtension's browser action. The message payload is just "ping". +This example demonstrates how to use [native messaging](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_messaging) to exchange messages between an extension and a native application. -The native application, which can be found under "app", listens for messages from the WebExtension. When it receives a message, the native application sends a response message whose payload is just "pong". The native application is written in Python. +## What it does + +The extension, located in the "add-on" folder, connects to the native application and listens for messages from it. It then sends a message to the native application when the user clicks the extension's browser action. The message payload is just "ping". + +The native application, located in the "app" folder, listens for messages from the extension. When it receives a message, the native application sends a response message with a payload of just "pong". The native application is written in Python. + +## What it shows + +This extension example shows how to: + +* Add a background script to an extension. +* Set up an action button. +* Request permissions to use native messaging. +* Create a manifest file to define the location of a native application. +* Connect to a native application. +* Send messages to a native application when the user clicks the extension's action button. +* Receive messages from a native application. ## Setup @@ -31,7 +47,7 @@ To get this working, there's a little setup to do. "path": "/Users/MDN/webextensions-examples/native-messaging/app/ping_pong.py" ``` -4. Copy `app/ping_pong.json` to the correct location on your computer. There are too many options to list here; see the [Linux](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#linux) and [macOS](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#macos) secitons of [App manifest location ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find the correct location for your OS and personal preference. +4. Copy `app/ping_pong.json` to the correct location on your computer. There are too many options to list here; see the [Linux](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#linux) and [macOS](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#macos) sections of [App manifest location ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find the correct location for your OS and personal preference. ### Windows setup @@ -39,7 +55,7 @@ To get this working, there's a little setup to do. 2. Update the `"path"` field in `app\ping_pong.json` to use the full path of `app\ping_pong_win.bat` on your computer. Be aware that you must escape the Windows directory separator (`\`). - For example, if you cloned this repository into `C:\Users\MDN\webextensions-examples\`, you update the JSON file like this: + For example, if you cloned this repository into `C:\Users\MDN\webextensions-examples\`, you updaate the JSON file like this: ```json "path": "C:\\Users\\MDN\\webextensions-examples\\native-messaging\\app\\ping_pong_win.bat" @@ -49,13 +65,13 @@ To get this working, there's a little setup to do. 4. Add a registry key containing the full path of `app\ping_pong.json` on your computer. See [App manifest location](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find details of the registry key to add. -To assist in troubleshooting on Windows, there is a script next to this README file named `check_config_win.py`. Running this in a command shell should help you discover of any problems. +To assist with troubleshooting on Windows, the example includes the `check_config_win.py` script. Running this in a command shell should help you discover any problems. ## Testing the example First, install the add-on. Visit `about:debugging#/runtime/this-firefox` or, from `about:debugging` click "This Firefox" (or "This Nightly" in the Nightly version of Firefox), click "Load Temporary Add-on", and open the add-on's `manifest.json`. -Now, open the extension's console using the "Inspect" button. This is where you see communication between the browser and native app. +Now, open the extension's console using the "Inspect" button. This button opens Developer Tools, where you see communication between the browser and native app. You should see a new browser action icon in the toolbar. Click it. You should see output like this in the console: diff --git a/native-messaging/add-on/background.js b/native-messaging/add-on/background.js index de289f31..1062f84d 100644 --- a/native-messaging/add-on/background.js +++ b/native-messaging/add-on/background.js @@ -28,7 +28,7 @@ port.onDisconnect.addListener((port) => { /* When the extension's action icon is clicked, send the app a message. */ -browser.browserAction.onClicked.addListener(() => { +browser.action.onClicked.addListener(() => { console.log("Sending: ping"); port.postMessage("ping"); }); diff --git a/native-messaging/add-on/manifest.json b/native-messaging/add-on/manifest.json index d0555584..9f8c62eb 100644 --- a/native-messaging/add-on/manifest.json +++ b/native-messaging/add-on/manifest.json @@ -1,7 +1,7 @@ { "description": "Native messaging example add-on", - "manifest_version": 2, + "manifest_version": 3, "name": "Native messaging example", "version": "1.0", "icons": { @@ -19,7 +19,7 @@ "scripts": ["background.js"] }, - "browser_action": { + "action": { "default_icon": "icons/message.svg" }, From 3d19446f5faf81c53f885a7dfe26a3419701e435 Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Fri, 23 Jan 2026 04:28:10 +1300 Subject: [PATCH 2/4] Standardized extension ID format --- native-messaging/add-on/manifest.json | 2 +- native-messaging/app/ping_pong.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/native-messaging/add-on/manifest.json b/native-messaging/add-on/manifest.json index 9f8c62eb..63b1206a 100644 --- a/native-messaging/add-on/manifest.json +++ b/native-messaging/add-on/manifest.json @@ -10,7 +10,7 @@ "browser_specific_settings": { "gecko": { - "id": "ping_pong@example.org", + "id": "native-messaging@mozilla.org", "strict_min_version": "50.0" } }, diff --git a/native-messaging/app/ping_pong.json b/native-messaging/app/ping_pong.json index a257b181..4dd7d14c 100644 --- a/native-messaging/app/ping_pong.json +++ b/native-messaging/app/ping_pong.json @@ -3,5 +3,5 @@ "description": "Example host for native messaging", "path": "/path/to/native-messaging/app/ping_pong.py", "type": "stdio", - "allowed_extensions": [ "ping_pong@example.org" ] + "allowed_extensions": [ "native-messaging@mozilla.org" ] } From 34d087d23b7cd36592d3aefa1fcb0a1477f071fb Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Fri, 23 Jan 2026 07:02:22 +1300 Subject: [PATCH 3/4] Added data-collection-permissions to browser-specific-settings --- native-messaging/add-on/manifest.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native-messaging/add-on/manifest.json b/native-messaging/add-on/manifest.json index 63b1206a..31869911 100644 --- a/native-messaging/add-on/manifest.json +++ b/native-messaging/add-on/manifest.json @@ -11,6 +11,9 @@ "browser_specific_settings": { "gecko": { "id": "native-messaging@mozilla.org", + "data_collection_permissions": { + "required": ["none"] + }, "strict_min_version": "50.0" } }, From 5e28c5ed7f1b281e72e75072d2e534424650afad Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Sat, 5 Sep 2026 07:17:40 +1200 Subject: [PATCH 4/4] Update example to use popup --- examples.json | 2 +- native-messaging/README.md | 37 +++++++------- native-messaging/add-on/background.js | 34 ------------- native-messaging/add-on/manifest.json | 10 ++-- native-messaging/add-on/popup/ping_pong.css | 40 +++++++++++++++ native-messaging/add-on/popup/ping_pong.html | 15 ++++++ native-messaging/add-on/popup/ping_pong.js | 52 ++++++++++++++++++++ 7 files changed, 131 insertions(+), 59 deletions(-) delete mode 100644 native-messaging/add-on/background.js create mode 100644 native-messaging/add-on/popup/ping_pong.css create mode 100644 native-messaging/add-on/popup/ping_pong.html create mode 100644 native-messaging/add-on/popup/ping_pong.js diff --git a/examples.json b/examples.json index 7b3d63c5..10d24b3a 100644 --- a/examples.json +++ b/examples.json @@ -384,7 +384,7 @@ }, { "description": "Example of native messaging, including a Python application and an extension which exchanges messages with it.", - "javascript_apis": ["browserAction.onClicked", "runtime.connectNative"], + "javascript_apis": ["runtime.connectNative"], "name": "native-messaging" }, { diff --git a/native-messaging/README.md b/native-messaging/README.md index b6278c4e..b3273689 100644 --- a/native-messaging/README.md +++ b/native-messaging/README.md @@ -4,31 +4,34 @@ This example demonstrates how to use [native messaging](https://developer.mozill ## What it does -The extension, located in the "add-on" folder, connects to the native application and listens for messages from it. It then sends a message to the native application when the user clicks the extension's browser action. The message payload is just "ping". +The extension, located in the "add-on" folder, provides a popup when the user clicks the extension's action button. When the popup opens, it connects to the native application and listens for messages. When the user clicks the popup's "Send ping" button, the popup sends a message with a payload of "ping" to the native application. The popup displays any messages received from the native application. -The native application, located in the "app" folder, listens for messages from the extension. When it receives a message, the native application sends a response message with a payload of just "pong". The native application is written in Python. +The native application, located in the "app" folder, listens for messages from the extension. When it receives a message, the native application sends a response message with a payload of "pong". The native application is written in Python. + +Best practice for extensions is to avoid opening a long-lived connection to a native application from an event page or background service worker. Doing so keeps the background context and the native application running indefinitely. + +In this example, the popup opens the connection to the native application, so the connection lives only as long as the popup is open. Closing the popup disconnects the port, and the browser shuts down the native application. This ensures that the native application and extension's background context are not kept alive when the extension has nothing to do. ## What it shows This extension example shows how to: -* Add a background script to an extension. -* Set up an action button. +* Set up an action button with a popup. * Request permissions to use native messaging. * Create a manifest file to define the location of a native application. -* Connect to a native application. -* Send messages to a native application when the user clicks the extension's action button. -* Receive messages from a native application. +* Connect to a native application from a popup, so that the connection is scoped to the popup's lifetime. +* Send messages to a native application when the user clicks a button in the popup. +* Receive messages from a native application and display them in the popup. ## Setup -To get this working, there's a little setup to do. +To get this working, you need the correct setup. ### Linux/macOS setup -0. (macOS) Store this extension in a location other than the Desktop, Documents, or Downloads folders in your home directory. macOS has access restrictions on these directories that prevent the Python script from executing as expected. +1. (macOS) Store this extension in a location other than the Desktop, Documents, or Downloads folders in your home directory. macOS has access restrictions on these directories that prevent the Python script from executing as expected. -1. Make sure you have Python 3 installed, and your system's PATH environment variable includes the path to Python. You can check by executing this command: +2. Make sure you have Python 3 installed, and your system's PATH environment variable includes the path to Python. You can check by executing this command: ```bash > which python3 @@ -37,9 +40,9 @@ To get this working, there's a little setup to do. If you don't see the path for Python, install Python 3. See [Using Python on Unix platforms](https://docs.python.org/3/using/unix.html) (for Linux) or [Using Python on a Mac](https://docs.python.org/3/using/mac.html). After making this change, restart Firefox so it picks up the new PATH environment variable. -2. Make sure that the [file permissions](https://en.wikipedia.org/wiki/File_system_permissions) for `app/ping_pong.py` include the `execute` permission. See [this article by RedHat](https://www.redhat.com/sysadmin/linux-file-permissions-explained) for more information. +3. Make sure that the [file permissions](https://en.wikipedia.org/wiki/File_system_permissions) for `app/ping_pong.py` include the `execute` permission. See [this article by Red Hat](https://www.redhat.com/sysadmin/linux-file-permissions-explained) for more information. -3. Update the `"path"` field in `app/ping_pong.json` to be the full path to your `app/ping_pong.py` file. +4. Update the `"path"` field in `app/ping_pong.json` to be the full path to your `app/ping_pong.py` file. For example, if you cloned this repository into `/Users/MDN/webextensions-examples/`, you would update the file like this: @@ -47,7 +50,7 @@ To get this working, there's a little setup to do. "path": "/Users/MDN/webextensions-examples/native-messaging/app/ping_pong.py" ``` -4. Copy `app/ping_pong.json` to the correct location on your computer. There are too many options to list here; see the [Linux](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#linux) and [macOS](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#macos) sections of [App manifest location ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find the correct location for your OS and personal preference. +5. Copy `app/ping_pong.json` to the correct location on your computer. There are too many options to list here; see the [Linux](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#linux) and [macOS](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#macos) sections of [App manifest location ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find the correct location for your OS and personal preference. ### Windows setup @@ -55,7 +58,7 @@ To get this working, there's a little setup to do. 2. Update the `"path"` field in `app\ping_pong.json` to use the full path of `app\ping_pong_win.bat` on your computer. Be aware that you must escape the Windows directory separator (`\`). - For example, if you cloned this repository into `C:\Users\MDN\webextensions-examples\`, you updaate the JSON file like this: + For example, if you cloned this repository into `C:\Users\MDN\webextensions-examples\`, you update the JSON file like this: ```json "path": "C:\\Users\\MDN\\webextensions-examples\\native-messaging\\app\\ping_pong_win.bat" @@ -69,11 +72,9 @@ To assist with troubleshooting on Windows, the example includes the `check_confi ## Testing the example -First, install the add-on. Visit `about:debugging#/runtime/this-firefox` or, from `about:debugging` click "This Firefox" (or "This Nightly" in the Nightly version of Firefox), click "Load Temporary Add-on", and open the add-on's `manifest.json`. - -Now, open the extension's console using the "Inspect" button. This button opens Developer Tools, where you see communication between the browser and native app. +Install the add-on. Visit `about:debugging#/runtime/this-firefox` or, from `about:debugging`, click "This Firefox" (or "This Nightly" in the Nightly version of Firefox), click "Load Temporary Add-on", and open the add-on's `manifest.json`. -You should see a new browser action icon in the toolbar. Click it. You should see output like this in the console: +Open the extension from its action icon in the toolbar or from the Extensions list. With the popup open, click "Send ping". You should see output like this in the popup: Sending: ping Received: pong diff --git a/native-messaging/add-on/background.js b/native-messaging/add-on/background.js deleted file mode 100644 index 1062f84d..00000000 --- a/native-messaging/add-on/background.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -On startup, connect to the "ping_pong" app. -*/ -let port = browser.runtime.connectNative("ping_pong"); - -/* -Listen for messages from the app and log them to the console. -*/ -port.onMessage.addListener((response) => { - console.log("Received: " + response); -}); - -/* -Listen for the native messaging port closing. -*/ -port.onDisconnect.addListener((port) => { - if (port.error) { - console.log(`Disconnected due to an error: ${port.error.message}`); - } else { - // The port closed for an unspecified reason. If this occurred right after - // calling `browser.runtime.connectNative()` there may have been a problem - // starting the native messaging client in the first place. - // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#troubleshooting - console.log(`Disconnected`, port); - } -}); - -/* -When the extension's action icon is clicked, send the app a message. -*/ -browser.action.onClicked.addListener(() => { - console.log("Sending: ping"); - port.postMessage("ping"); -}); diff --git a/native-messaging/add-on/manifest.json b/native-messaging/add-on/manifest.json index 31869911..0bd31cc0 100644 --- a/native-messaging/add-on/manifest.json +++ b/native-messaging/add-on/manifest.json @@ -14,16 +14,14 @@ "data_collection_permissions": { "required": ["none"] }, - "strict_min_version": "50.0" + "strict_min_version": "109.0" } }, - "background": { - "scripts": ["background.js"] - }, - "action": { - "default_icon": "icons/message.svg" + "default_icon": "icons/message.svg", + "default_title": "Native messaging example", + "default_popup": "popup/ping_pong.html" }, "permissions": ["nativeMessaging"] diff --git a/native-messaging/add-on/popup/ping_pong.css b/native-messaging/add-on/popup/ping_pong.css new file mode 100644 index 00000000..1c49cf9b --- /dev/null +++ b/native-messaging/add-on/popup/ping_pong.css @@ -0,0 +1,40 @@ +html, body { + width: 200px; + font: caption; +} + +button { + border: none; + width: 100%; + margin: 3% auto; + padding: 4px; + text-align: center; + font-size: 1.2em; + cursor: pointer; + background-color: #E5F2F2; +} + +button:hover { + background-color: #CFF2F2; +} + +button:disabled { + background-color: #EEEEEE; + color: #767676; + cursor: default; +} + +#messages { + margin: 0; + padding: 0; + list-style: none; +} + +#messages li { + padding: 2px 0; + border-top: 1px solid #E0E0E0; +} + +.error { + color: #A4000F; +} diff --git a/native-messaging/add-on/popup/ping_pong.html b/native-messaging/add-on/popup/ping_pong.html new file mode 100644 index 00000000..172238b0 --- /dev/null +++ b/native-messaging/add-on/popup/ping_pong.html @@ -0,0 +1,15 @@ + + + + + + + + + + +
    + + + + diff --git a/native-messaging/add-on/popup/ping_pong.js b/native-messaging/add-on/popup/ping_pong.js new file mode 100644 index 00000000..f0bae496 --- /dev/null +++ b/native-messaging/add-on/popup/ping_pong.js @@ -0,0 +1,52 @@ +const sendPingButton = document.querySelector("#send-ping"); +const messagesList = document.querySelector("#messages"); + +/* +Add a line of text to the popup. +*/ +function log(text, isError = false) { + const item = document.createElement("li"); + item.textContent = text; + if (isError) { + item.classList.add("error"); + } + messagesList.appendChild(item); +} + +/* +When the popup opens, connect to the "ping_pong" app. The port, and with it the +native application, lives only for as long as the popup is open: closing the +popup disconnects the port, and the browser shuts the application down. +*/ +const port = browser.runtime.connectNative("ping_pong"); + +/* +Listen for messages from the app and display them in the popup. +*/ +port.onMessage.addListener((response) => { + log(`Received: ${response}`); +}); + +/* +Listen for the native messaging port closing. +*/ +port.onDisconnect.addListener((port) => { + sendPingButton.disabled = true; + if (port.error) { + log(`Disconnected due to an error: ${port.error.message}`, true); + } else { + // The port closed for an unspecified reason. If this occurred right after + // calling `browser.runtime.connectNative()` there may have been a problem + // starting the native messaging client. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#troubleshooting + log("Disconnected", true); + } +}); + +/* +When the button in the popup is clicked, send the app a message. +*/ +sendPingButton.addEventListener("click", () => { + log("Sending: ping"); + port.postMessage("ping"); +});