diff options
Diffstat (limited to 'toolkit/jetpack/sdk/system/child_process/subprocess.js')
-rw-r--r-- | toolkit/jetpack/sdk/system/child_process/subprocess.js | 186 |
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; |