Bug 1350646: Part 3 - Remove sdk/system/child_process modules. r=Mossop

MozReview-Commit-ID: LQgUe8E8M4k

--HG--
extra : source : 3ca040743aebaa3f0c971fcd8de3fe798121bcd8
This commit is contained in:
Kris Maglione 2017-08-02 13:52:47 -07:00
parent 6e2ec006a5
commit cc4c331174
3 changed files with 0 additions and 518 deletions

View File

@ -184,8 +184,6 @@ modules = [
'sdk/stylesheet/style.js',
'sdk/stylesheet/utils.js',
'sdk/system.js',
'sdk/system/child_process.js',
'sdk/system/child_process/subprocess.js',
'sdk/system/environment.js',
'sdk/system/events-shimmed.js',
'sdk/system/events.js',

View File

@ -1,332 +0,0 @@
/* 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';
module.metadata = {
'stability': 'experimental'
};
var { Ci } = require('chrome');
var subprocess = require('./child_process/subprocess');
var { EventTarget } = require('../event/target');
var { Stream } = require('../io/stream');
var { on, emit, off } = require('../event/core');
var { Class } = require('../core/heritage');
var { platform } = require('../system');
var { isFunction, isArray } = require('../lang/type');
var { delay } = require('../lang/functional');
var { merge } = require('../util/object');
var { setTimeout, clearTimeout } = require('../timers');
var isWindows = platform.indexOf('win') === 0;
var processes = new WeakMap();
/**
* The `Child` class wraps a subprocess command, exposes
* the stdio streams, and methods to manipulate the subprocess
*/
var Child = Class({
implements: [EventTarget],
initialize: function initialize (options) {
let child = this;
let proc;
this.killed = false;
this.exitCode = undefined;
this.signalCode = undefined;
this.stdin = Stream();
this.stdout = Stream();
this.stderr = Stream();
try {
proc = subprocess.call({
command: options.file,
arguments: options.cmdArgs,
environment: serializeEnv(options.env),
workdir: options.cwd,
charset: options.encoding,
stdout: data => emit(child.stdout, 'data', data),
stderr: data => emit(child.stderr, 'data', data),
stdin: stream => {
child.stdin.on('data', pumpStdin);
child.stdin.on('end', function closeStdin () {
child.stdin.off('data', pumpStdin);
child.stdin.off('end', closeStdin);
stream.close();
});
function pumpStdin (data) {
stream.write(data);
}
},
done: function (result, error) {
if (error)
return handleError(error);
// Only emit if child is not killed; otherwise,
// the `kill` method will handle this
if (!child.killed) {
child.exitCode = result.exitCode;
child.signalCode = null;
// If process exits with < 0, there was an error
if (child.exitCode < 0) {
handleError(new Error('Process exited with exit code ' + child.exitCode));
}
else {
// Also do 'exit' event as there's not much of
// a difference in our implementation as we're not using
// node streams
emit(child, 'exit', child.exitCode, child.signalCode);
}
// Emit 'close' event with exit code and signal,
// which is `null`, as it was not a killed process
emit(child, 'close', child.exitCode, child.signalCode);
}
}
});
processes.set(child, proc);
} catch (e) {
// Delay the error handling so an error handler can be set
// during the same tick that the Child was created
delay(() => handleError(e));
}
// `handleError` is called when process could not even
// be spawned
function handleError (e) {
// If error is an nsIObject, make a fresh error object
// so we're not exposing nsIObjects, and we can modify it
// with additional process information, like node
let error = e;
if (e instanceof Ci.nsISupports) {
error = new Error(e.message, e.filename, e.lineNumber);
}
emit(child, 'error', error);
child.exitCode = -1;
child.signalCode = null;
emit(child, 'close', child.exitCode, child.signalCode);
}
},
kill: function kill (signal) {
let proc = processes.get(this);
proc.kill(signal);
this.killed = true;
this.exitCode = null;
this.signalCode = signal;
emit(this, 'exit', this.exitCode, this.signalCode);
emit(this, 'close', this.exitCode, this.signalCode);
},
get pid() { return processes.get(this, {}).pid || -1; }
});
function spawn (file, ...args) {
let cmdArgs = [];
// Default options
let options = {
cwd: null,
env: null,
encoding: 'UTF-8'
};
if (args[1]) {
merge(options, args[1]);
cmdArgs = args[0];
}
else {
if (isArray(args[0]))
cmdArgs = args[0];
else
merge(options, args[0]);
}
if ('gid' in options)
console.warn('`gid` option is not yet supported for `child_process`');
if ('uid' in options)
console.warn('`uid` option is not yet supported for `child_process`');
if ('detached' in options)
console.warn('`detached` option is not yet supported for `child_process`');
options.file = file;
options.cmdArgs = cmdArgs;
return Child(options);
}
exports.spawn = spawn;
/**
* exec(command, options, callback)
*/
function exec (cmd, ...args) {
let file, cmdArgs, callback, options = {};
if (isFunction(args[0]))
callback = args[0];
else {
merge(options, args[0]);
callback = args[1];
}
if (isWindows) {
file = 'C:\\Windows\\System32\\cmd.exe';
cmdArgs = ['/S/C', cmd || ''];
}
else {
file = '/bin/sh';
cmdArgs = ['-c', cmd];
}
// Undocumented option from node being able to specify shell
if (options && options.shell)
file = options.shell;
return execFile(file, cmdArgs, options, callback);
}
exports.exec = exec;
/**
* execFile (file, args, options, callback)
*/
function execFile (file, ...args) {
let cmdArgs = [], callback;
// Default options
let options = {
cwd: null,
env: null,
encoding: 'utf8',
timeout: 0,
maxBuffer: 204800, //200 KB (200*1024 bytes)
killSignal: 'SIGTERM'
};
if (isFunction(args[args.length - 1]))
callback = args[args.length - 1];
if (isArray(args[0])) {
cmdArgs = args[0];
merge(options, args[1]);
} else if (!isFunction(args[0]))
merge(options, args[0]);
let child = spawn(file, cmdArgs, options);
let exited = false;
let stdout = '';
let stderr = '';
let error = null;
let timeoutId = null;
child.stdout.setEncoding(options.encoding);
child.stderr.setEncoding(options.encoding);
on(child.stdout, 'data', pumpStdout);
on(child.stderr, 'data', pumpStderr);
on(child, 'close', exitHandler);
on(child, 'error', errorHandler);
if (options.timeout > 0) {
setTimeout(() => {
kill();
timeoutId = null;
}, options.timeout);
}
function exitHandler (code, signal) {
// Return if exitHandler called previously, occurs
// when multiple maxBuffer errors thrown and attempt to kill multiple
// times
if (exited) return;
exited = true;
if (!isFunction(callback)) return;
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (!error && (code !== 0 || signal !== null))
error = createProcessError(new Error('Command failed: ' + stderr), {
code: code,
signal: signal,
killed: !!child.killed
});
callback(error, stdout, stderr);
off(child.stdout, 'data', pumpStdout);
off(child.stderr, 'data', pumpStderr);
off(child, 'close', exitHandler);
off(child, 'error', errorHandler);
}
function errorHandler (e) {
error = e;
exitHandler();
}
function kill () {
try {
child.kill(options.killSignal);
} catch (e) {
// In the scenario where the kill signal happens when
// the process is already closing, just abort the kill fail
if (/library is not open/.test(e))
return;
error = e;
exitHandler(-1, options.killSignal);
}
}
function pumpStdout (data) {
stdout += data;
if (stdout.length > options.maxBuffer) {
error = new Error('stdout maxBuffer exceeded');
kill();
}
}
function pumpStderr (data) {
stderr += data;
if (stderr.length > options.maxBuffer) {
error = new Error('stderr maxBuffer exceeded');
kill();
}
}
return child;
}
exports.execFile = execFile;
exports.fork = function fork () {
throw new Error("child_process#fork is not currently supported");
};
function serializeEnv (obj) {
return Object.keys(obj || {}).map(prop => prop + '=' + obj[prop]);
}
function createProcessError (err, options = {}) {
// If code and signal look OK, this was probably a failure
// attempting to spawn the process (like ENOENT in node) -- use
// the code from the error message
if (!options.code && !options.signal) {
let match = err.message.match(/(NS_ERROR_\w*)/);
if (match && match.length > 1)
err.code = match[1];
else {
// If no good error message found, use the passed in exit code;
// this occurs when killing a process that's already closing,
// where we want both a valid exit code (0) and the error
err.code = options.code != null ? options.code : null;
}
}
else
err.code = options.code != null ? options.code : null;
err.signal = options.signal || null;
err.killed = options.killed || false;
return err;
}

View File

@ -1,184 +0,0 @@
/* 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");
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;
});
Services.tm.spinEventLoopUntil(() => resolved !== null);
if (resolved === true)
return value;
throw value;
}
let readAllData = async function(pipe, read, callback) {
let string;
while (string = await 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 = (async 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 = await 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 = await 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;