diff --git a/native-messaging/README.md b/native-messaging/README.md index 8d69c6a5..1cb028dc 100644 --- a/native-messaging/README.md +++ b/native-messaging/README.md @@ -4,36 +4,62 @@ The WebExtension, which can be found under "add-on", connects to the native appl 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. -## Setup ## +## Setup To get this working, there's a little setup to do. -### Mac OS/Linux setup ### +### Linux/macOS setup -1. Check that the [file permissions](https://en.wikipedia.org/wiki/File_system_permissions) for "ping_pong.py" include the `execute` permission. -2. Edit the "path" property of "ping_pong.json" to point to the location of "ping_pong.py" on your computer. -3. copy "ping_pong.json" to the correct location on your computer. See [App manifest location ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location) to find the correct location for your OS. +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. -### Windows setup ### +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: -1. Check you have Python installed, and that your system's PATH environment variable includes the path to Python. See [Using Python on Windows](https://docs.python.org/3/using/windows.html). You'll need to restart the web browser after making this change, or the browser won't pick up the new environment variable. -2. Edit the "path" property of "ping_pong.json" to point to the location of "ping_pong_win.bat" on your computer. Note that you'll need to escape the Windows directory separator, like this: `"path": "C:\\Users\\MDN\\native-messaging\\app\\ping_pong_win.bat"`. -3. Edit "ping_pong_win.bat" to refer to the location of "ping_pong.py" on your computer. -4. Add a registry key containing the path to "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. + ```bash + > which python3 + /usr/local/bin/python3 + ``` -To assist in troubleshooting on Windows, there is a script called `check_config_win.py`. Running this from the command line should give you an idea of any problems. + 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. -## Testing the example ## +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. -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". +3. Update the `"path"` field in `app/ping_pong.json` to be the full path to your `app/ping_pong.py` file. -Now, open the extension's console using the "Inspect" button - this is where you'll see communication between the browser and native app. + For example, if you cloned this repository into `/Users/MDN/webextensions-examples/`, you would update the file like this: + + ```json + "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. + +### Windows setup + +1. Make sure that you have Python 3 installed and that your system's PATH environment variable includes the path to Python. See [Using Python on Windows](https://docs.python.org/3/using/windows.html). After making this change, restart Firefox so it picks up the new PATH environment variable. + +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: + + ```json + "path": "C:\\Users\\MDN\\webextensions-examples\\native-messaging\\app\\ping_pong_win.bat" + ``` + +3. Update `app\ping_pong_win.bat` to use the full path of `app\ping_pong.py` on your computer. + +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. + +## 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. You should see a new browser action icon in the toolbar. Click it. You should see output like this in the console: Sending: ping - Received: pong3 - -If you're running Python 2.x, you'll see "pong2" as the response instead. + Received: pong If you don't see this output, see the [Troubleshooting guide](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_messaging#Troubleshooting) for ideas. diff --git a/native-messaging/add-on/background.js b/native-messaging/add-on/background.js index 020e6edd..f4c9b092 100644 --- a/native-messaging/add-on/background.js +++ b/native-messaging/add-on/background.js @@ -4,14 +4,29 @@ On startup, connect to the "ping_pong" app. let port = browser.runtime.connectNative("ping_pong"); /* -Listen for messages from the app. +Listen for messages from the app and log them to the console. */ port.onMessage.addListener((response) => { console.log("Received: " + response); }); /* -On a click on the browser action, send the app a message. +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 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.browserAction.onClicked.addListener(() => { console.log("Sending: ping"); diff --git a/native-messaging/add-on/manifest.json b/native-messaging/add-on/manifest.json index 0a7800c1..d0555584 100644 --- a/native-messaging/add-on/manifest.json +++ b/native-messaging/add-on/manifest.json @@ -22,7 +22,7 @@ "browser_action": { "default_icon": "icons/message.svg" }, - + "permissions": ["nativeMessaging"] } diff --git a/native-messaging/app/ping_pong.py b/native-messaging/app/ping_pong.py index e9343800..39e3d117 100755 --- a/native-messaging/app/ping_pong.py +++ b/native-messaging/app/ping_pong.py @@ -1,72 +1,36 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import json import struct -try: - # Python 3.x version - # Read a message from stdin and decode it. - def getMessage(): - rawLength = sys.stdin.buffer.read(4) - if len(rawLength) == 0: - sys.exit(0) - messageLength = struct.unpack('@I', rawLength)[0] - message = sys.stdin.buffer.read(messageLength).decode('utf-8') - return json.loads(message) - - # Encode a message for transmission, - # given its content. - def encodeMessage(messageContent): - # https://docs.python.org/3/library/json.html#basic-usage - # To get the most compact JSON representation, you should specify - # (',', ':') to eliminate whitespace. - # We want the most compact representation because the browser rejects - # messages that exceed 1 MB. - encodedContent = json.dumps(messageContent, separators=(',', ':')).encode('utf-8') - encodedLength = struct.pack('@I', len(encodedContent)) - return {'length': encodedLength, 'content': encodedContent} - - # Send an encoded message to stdout - def sendMessage(encodedMessage): - sys.stdout.buffer.write(encodedMessage['length']) - sys.stdout.buffer.write(encodedMessage['content']) - sys.stdout.buffer.flush() - - while True: - receivedMessage = getMessage() - if receivedMessage == "ping": - sendMessage(encodeMessage("pong3")) -except AttributeError: - # Python 2.x version (if sys.stdin.buffer is not defined) - # 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): - # https://docs.python.org/3/library/json.html#basic-usage - # To get the most compact JSON representation, you should specify - # (',', ':') to eliminate whitespace. - # We want the most compact representation because the browser rejects - # messages that exceed 1 MB. - encodedContent = json.dumps(messageContent, separators=(',', ':')) - 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("pong2")) +# Read a message from stdin and decode it. +def getMessage(): + rawLength = sys.stdin.buffer.read(4) + if len(rawLength) == 0: + sys.exit(0) + messageLength = struct.unpack('@I', rawLength)[0] + message = sys.stdin.buffer.read(messageLength).decode('utf-8') + return json.loads(message) + +# Encode a message for transmission, given its content. +def encodeMessage(messageContent): + # https://docs.python.org/3/library/json.html#basic-usage + # To get the most compact JSON representation, you should specify + # (',', ':') to eliminate whitespace. + # We want the most compact representation because the browser rejects + # messages that exceed 1 MB. + encodedContent = json.dumps(messageContent, separators=(',', ':')).encode('utf-8') + encodedLength = struct.pack('@I', len(encodedContent)) + return {'length': encodedLength, 'content': encodedContent} + +# Send an encoded message to stdout +def sendMessage(encodedMessage): + sys.stdout.buffer.write(encodedMessage['length']) + sys.stdout.buffer.write(encodedMessage['content']) + sys.stdout.buffer.flush() + +while True: + receivedMessage = getMessage() + if receivedMessage == "ping": + sendMessage(encodeMessage("pong")) diff --git a/native-messaging/app/ping_pong_win.bat b/native-messaging/app/ping_pong_win.bat index aac2019f..0d52af96 100644 --- a/native-messaging/app/ping_pong_win.bat +++ b/native-messaging/app/ping_pong_win.bat @@ -1,3 +1,3 @@ @echo off -call python C:\path\to\ping_pong.py +call python3 C:\path\to\ping_pong.py