summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/system/child_process/subprocess.js
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-09 06:46:43 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-09 06:46:43 -0500
commitac46df8daea09899ce30dc8fd70986e258c746bf (patch)
tree2750d3125fc253fd5b0671e4bd268eff1fd97296 /toolkit/jetpack/sdk/system/child_process/subprocess.js
parent8cecf8d5208f3945b35f879bba3015bb1a11bec6 (diff)
downloadUXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.gz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.lz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.xz
UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.zip
Move Add-on SDK source to toolkit/jetpack
Diffstat (limited to 'toolkit/jetpack/sdk/system/child_process/subprocess.js')
-rw-r--r--toolkit/jetpack/sdk/system/child_process/subprocess.js186
1 files changed, 186 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/system/child_process/subprocess.js b/toolkit/jetpack/sdk/system/child_process/subprocess.js
new file mode 100644
index 000000000..e3454e95b
--- /dev/null
+++ b/toolkit/jetpack/sdk/system/child_process/subprocess.js
@@ -0,0 +1,186 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Ci, Cu } = require("chrome");
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Subprocess.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+const Runtime = require("sdk/system/runtime");
+const Environment = require("sdk/system/environment").env;
+const DEFAULT_ENVIRONMENT = [];
+if (Runtime.OS == "Linux" && "DISPLAY" in Environment) {
+ DEFAULT_ENVIRONMENT.push("DISPLAY=" + Environment.DISPLAY);
+}
+
+function awaitPromise(promise) {
+ let value;
+ let resolved = null;
+ promise.then(val => {
+ resolved = true;
+ value = val;
+ }, val => {
+ resolved = false;
+ value = val;
+ });
+
+ while (resolved === null)
+ Services.tm.mainThread.processNextEvent(true);
+
+ if (resolved === true)
+ return value;
+ throw value;
+}
+
+let readAllData = Task.async(function* (pipe, read, callback) {
+ let string;
+ while (string = yield read(pipe))
+ callback(string);
+});
+
+let write = (pipe, data) => {
+ let buffer = new Uint8Array(Array.from(data, c => c.charCodeAt(0)));
+ return pipe.write(data);
+};
+
+var subprocess = {
+ call: function(options) {
+ var result;
+
+ let procPromise = Task.spawn(function*() {
+ let opts = {};
+
+ if (options.mergeStderr) {
+ opts.stderr = "stdout"
+ } else if (options.stderr) {
+ opts.stderr = "pipe";
+ }
+
+ if (options.command instanceof Ci.nsIFile) {
+ opts.command = options.command.path;
+ } else {
+ opts.command = yield Subprocess.pathSearch(options.command);
+ }
+
+ if (options.workdir) {
+ opts.workdir = options.workdir;
+ }
+
+ opts.arguments = options.arguments || [];
+
+
+ // Set up environment
+
+ let envVars = options.environment || DEFAULT_ENVIRONMENT;
+ if (envVars.length) {
+ let environment = {};
+ for (let val of envVars) {
+ let idx = val.indexOf("=");
+ if (idx >= 0)
+ environment[val.slice(0, idx)] = val.slice(idx + 1);
+ }
+
+ opts.environment = environment;
+ }
+
+
+ let proc = yield Subprocess.call(opts);
+
+ Object.defineProperty(result, "pid", {
+ value: proc.pid,
+ enumerable: true,
+ configurable: true,
+ });
+
+
+ let promises = [];
+
+ // Set up IO handlers.
+
+ let read = pipe => pipe.readString();
+ if (options.charset === null) {
+ read = pipe => {
+ return pipe.read().then(buffer => {
+ return String.fromCharCode(...buffer);
+ });
+ };
+ }
+
+ if (options.stdout)
+ promises.push(readAllData(proc.stdout, read, options.stdout));
+
+ if (options.stderr && proc.stderr)
+ promises.push(readAllData(proc.stderr, read, options.stderr));
+
+ // Process stdin
+
+ if (typeof options.stdin === "string") {
+ write(proc.stdin, options.stdin);
+ proc.stdin.close();
+ }
+
+
+ // Handle process completion
+
+ if (options.done)
+ Promise.all(promises)
+ .then(() => proc.wait())
+ .then(options.done);
+
+ return proc;
+ });
+
+ procPromise.catch(e => {
+ if (options.done)
+ options.done({exitCode: -1}, e);
+ else
+ Cu.reportError(e instanceof Error ? e : e.message || e);
+ });
+
+ if (typeof options.stdin === "function") {
+ // Unfortunately, some callers (child_process.js) depend on this
+ // being called synchronously.
+ options.stdin({
+ write(val) {
+ procPromise.then(proc => {
+ write(proc.stdin, val);
+ });
+ },
+
+ close() {
+ procPromise.then(proc => {
+ proc.stdin.close();
+ });
+ },
+ });
+ }
+
+ result = {
+ get pid() {
+ return awaitPromise(procPromise.then(proc => {
+ return proc.pid;
+ }));
+ },
+
+ wait() {
+ return awaitPromise(procPromise.then(proc => {
+ return proc.wait().then(({exitCode}) => exitCode);
+ }));
+ },
+
+ kill(hard = false) {
+ procPromise.then(proc => {
+ proc.kill(hard ? 0 : undefined);
+ });
+ },
+ };
+
+ return result;
+ },
+};
+
+module.exports = subprocess;