Running an external command from Firefox right now is only supported with the nsIProcess API. You figured out this bit for yourself already.
Packaging a python script.
The SDK will only package certain files/locations. It would be easiest to just place the python script in the data/ folder, because that is one of the locations the SDK will package all files from.
nsIProcess
nsIProcess needs an executable file, and that file actually needs to be a real file (not just something contained within the XPI, which by default is not unpacked).
So there are two cases we might want to handle:
- File is a real file - Just execute it.
- File is packaged - Need to extract/copy the data into a (temporary) real file and execute that.
The following code deals with both. I tested and it works for me on OSX (*nix, so should work on Linux or BSDs as well) and Windows, with and without em:unpack (and you should avoid em:unpack).
const self = require("sdk/self"); const {Cc, Ci, Cu} = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); const ChromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"]. getService(Ci.nsIChromeRegistry); const ResProtoHandler = Services.io.getProtocolHandler("resource"). QueryInterface(Ci.nsIResProtocolHandler); function copyToTemp(uri, callback) { // Based on https://stackoverflow.com/a/24850643/484441 let file = Services.dirsvc.get("TmpD", Ci.nsIFile); file.append(self.name + "_" + uri.spec.replace(/^.+\//, "")); file.createUnique(Ci.nsIFile, 0o0700); NetUtil.asyncFetch(uri, function(istream) { let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); ostream.init(file, -1, -1, Ci.nsIFileOutputStream.DEFER_OPEN); NetUtil.asyncCopy(istream, ostream, function(result) { callback && callback(file, result); }); }); } function runProcessAndThen(file, callback) { console.log("running", file.path); let proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); try { // Set executable bit on unix file.permissions = file.permissions | 0o0500; } catch (ex) { // Might throw?! } proc.init(file); proc.runAsync([], 0, callback); } function runFromURIWithPotentialCopy(uri, callback) { if (!uri.spec) { uri = Services.io.newURI(uri, null, null); } if (uri.scheme === "resource") { // Need to resolve futher. Strip one layer of indirection and recursively // call ourselves. uri = Services.io.newURI(ResProtoHandler.resolveURI(uri), null, null); return runFromURIWithPotentialCopy(uri, callback); } if (uri.scheme === "chrome") { // Need to resolve futher. Strip one layer of indirection and recursively // call ourselves. return runFromURIWithPotentialCopy(ChromeRegistry.convertChromeURL(uri), callback); } if (uri instanceof Ci.nsIFileURL) { // A plain file we can execute directly. return runProcessAndThen(uri.file, callback); } if (uri instanceof Ci.nsIJARURI) { // A packaged file (in an XPI most likely). // Need to copy the data into some plain file and run the result. return copyToTemp(uri, function(f) { runProcessAndThen(f, function() { try { // Clean up after ourselves. f.remove(false); } catch (ex) { console.error("Failed to remove tmp file again", ex); } callback.apply(null, arguments); }); }); } throw new Error("Cannot handle URI"); } function afterRun(subject, topic, data) { console.log(subject, topic, data); } function runFileFromDataDirectory(name, callback) { try { runFromURIWithPotentialCopy(self.data.url(name), callback); } catch (ex) { console.error(ex); } } runFileFromDataDirectory("test.py", afterRun);
Running a script
Running a script (as opposed to a full-blown binary) can be tricky. In case of Python e.g. the *nix OSes needs to be told that there is an interpreter and what this is, which is done by a shebang. On Windows, Python needs to be installed with .py file type registration, which the default installer will do, but not "portable" versions".
Cu.import("resource://gre/modules/FileUtils.jsm"); var py = FileUtils.getFile('ProfD', ['extensions', 'smartProxy.py']);unpack truein install.rdf?