I have simple Firefox add-on that, I am using it to test and discover the capabilities of Native Messaging, something I recently came across.
The add-on side of things works as I expect it, nothing out of order there, I am struggling with the Python side of the add-on.
The add-on works like this:
- On the browser, when the page HTML basics is visited
- The add-on will send the message “ping” to a Python script, on the local system, via stdin
- The python script should reply back to the browser the message it received via stdin and run a process via
subprocess.Popen()
Sure it enough, in all my tests, on the browser console, I can see the Python programme sending back a reply like THIS. But the line subprocess.Popen(["explorer", "C:/Temp"])
is never executed at all. No matter where I place it in the Python script.
If I create a separate Python script with just the following code and run it by double clicking the file in explorer, it works. A explorer window is created:
import subprocess
subprocess.Popen(["explorer", "C:/Temp"])
Of course I am looking to do more than just open a explorer window, its just a simple example. The point is for some reason, my Python programme is stuck either reading at stdin or somewhere else.
I tried restructuring the Python code to something simple and tried “closing” the stdin stream to see if that will help it carry on with the execution of the remaining lines:
import sys
import json
import struct
import subprocess
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')
sys.stdin.buffer.flush() #Try closing the stdin buffer
sys.stdin.buffer.close() #Try closing the stdin buffer
subprocess.Popen(["explorer", "C:/Temp"]) #Again not executed
Same issue persists, the last line is again not executed. I am new to Python, JavaScript and add-on development. I asked around for any debugging tools for such a novel, edge use case but sadly I have not turned up with answers. The python programme does not spawn its own console window so its hard to tell where execution is stuck at with something like print()
I did try the following in its own python script file:
import sys
import json
import struct
import subprocess
rawLength = sys.stdin.buffer.read(4)
print(rawLength)
subprocess.Popen(["explorer", "C:/Temp"])
It will spawn its own console window, the programme is stuck at rawLength = sys.stdin.buffer.read(4)
and will remain there, even if I provide just a letter and press enter
, it continues when I provide four letters, opening file explorer at c:/Temp
.
Last time I asked around. I was told this might be what is happening and I looked for a way to stop the stdin stream reading or close it, which is what I tried to do with flush()
/close()
but it does not help.
Am I attempting to close the stdin stream the right way? If so am I succeeding? How does one know for sure? Is stdin even the culprit here?
I am out of ideas, any help would be greatly appreciated!
For completeness, my add-on is compromised of only two files, a manifest.json
and a background.file
.
Manifest.json file:
{
"name": "test",
"manifest_version": 2,
"version": "1.0",
"browser_action": {"default_icon": "icons/message.svg"},
"browser_specific_settings": {"gecko": {"id": "test@example.org","strict_min_version": "50.0"}},
"background": {"scripts": ["background.js"]},
"permissions": ["tabs","activeTab", "webRequest", "<all_urls>", "nativeMessaging"]
}
Background.json file:
browser.webRequest.onCompleted.addListener(sendNativeMsg, {urls:["https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics"]});
function onResponse(response) {console.log(`INCOMING MSG: ${response}`);}
function sendNativeMsg(activeTab) {
let thisMsg = "ping"
console.log(`OUTGOING MSG: "${thisMsg}"`);
let sending = browser.runtime.sendNativeMessage("test", thisMsg);
sending.then(onResponse);
}
And the source code for the Python script is the following, which I got from the Native Messaging, MDN page, linked above:
import sys
import json
import struct
import subprocess
# 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,
def encodeMessage(messageContent):
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:
subprocess.Popen(["explorer", "C:/Temp"]) #This line is never executed. The lines after here are executed.
receivedMessage = getMessage()
if receivedMessage == "ping":
sendMessage(encodeMessage('stdin was "' + receivedMessage + '", Task is done'))