Files
UXP-Fixed/toolkit/jetpack/sdk/system/child_process/subprocess.js
T
2018-02-09 06:46:43 -05:00

187 lines
4.2 KiB
JavaScript

/* 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;