Bug 1309351: Part 1 - Replace child_process/subprocess.js with a thin wrapper around Subprocess.jsm. r?rpl draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 12 Oct 2016 21:29:43 +0100
changeset 424679 75fa66d6f9a98d4bc3aa0776848e8eea7227bddf
parent 424602 eafc7904aedcb41d1180ba0c631b8b5c51218b4a
child 424680 292e833393cf7c262e6ad446476d8fca2213959d
push id32217
push usermaglione.k@gmail.com
push dateThu, 13 Oct 2016 05:44:55 +0000
reviewersrpl
bugs1309351
milestone52.0a1
Bug 1309351: Part 1 - Replace child_process/subprocess.js with a thin wrapper around Subprocess.jsm. r?rpl MozReview-Commit-ID: 7r9pSC8v1T
addon-sdk/moz.build
addon-sdk/source/lib/sdk/system/child_process/LICENSE
addon-sdk/source/lib/sdk/system/child_process/README.md
addon-sdk/source/lib/sdk/system/child_process/subprocess.js
addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_unix.js
addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_win.js
addon-sdk/source/test/addons/child_process/index.js
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -472,18 +472,16 @@ EXTRA_JS_MODULES.commonjs.sdk.system += 
     'source/lib/sdk/system/runtime.js',
     'source/lib/sdk/system/unload.js',
     'source/lib/sdk/system/xul-app.js',
     'source/lib/sdk/system/xul-app.jsm',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.system.child_process += [
     'source/lib/sdk/system/child_process/subprocess.js',
-    'source/lib/sdk/system/child_process/subprocess_worker_unix.js',
-    'source/lib/sdk/system/child_process/subprocess_worker_win.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.tab += [
     'source/lib/sdk/tab/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.button.view += [
     'source/lib/sdk/ui/button/view/events.js',
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/LICENSE
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- ***** BEGIN LICENSE BLOCK *****
-   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
-   -
-   - The contents of this file are subject to the Mozilla Public License Version
-   - 1.1 (the "License"); you may not use this file except in compliance with
-   - the License. You may obtain a copy of the License at
-   - http://www.mozilla.org/MPL/
-   -
-   - Software distributed under the License is distributed on an "AS IS" basis,
-   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   - for the specific language governing rights and limitations under the
-   - License.
-   -
-   - The Original Code is subprocess.jsm.
-   -
-   - The Initial Developer of this code is Jan Gerber.
-   - Portions created by Jan Gerber <j@mailb.org>
-   - are Copyright (C) 2011 Jan Gerber.
-   - All Rights Reserved.
-   -
-   -
-   - Contributor(s):
-   -
-   - Alternatively, the contents of this file may be used under the terms of
-   - either the GNU General Public License Version 2 or later (the "GPL"), or
-   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   - in which case the provisions of the GPL or the LGPL are applicable instead
-   - of those above. If you wish to allow use of your version of this file only
-   - under the terms of either the GPL or the LGPL, and not to allow others to
-   - use your version of this file under the terms of the MPL, indicate your
-   - decision by deleting the provisions above and replace them with the notice
-   - and other provisions required by the LGPL or the GPL. If you do not delete
-   - the provisions above, a recipient may use your version of this file under
-   - the terms of any one of the MPL, the GPL or the LGPL.
-   -
-   - ***** END LICENSE BLOCK ***** -->
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/README.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# subprocess
-
-
-Created by [Jan Gerber](j@mailb.org), with contributions from [Patrick Brunschwig](patrick@enigmail.net), subprocess.jsm is used as the underlying library, supporting the Addon-SDK's implementation of Node's `child-process` API.
-
-`subprocess.jsm` is originally from [http://hg.mozilla.org/ipccode/](http://hg.mozilla.org/ipccode/).
-
-How to Use subprocess.jsm in your Add-on
-----------------------------------------
-
-1. copy subprocess.jsm and subprocess_worker_*.js into the modules/ directory
-   of your add-on.
-
-2. add this line to chrome.manifest:
- resource EXTENSION modules/
-
-3. import it where needed:
- Components.utils.import("resource://EXTENSION/subprocess.jsm");
-
-This object allows to start a process, and read/write data to/from it
-using stdin/stdout/stderr streams.
-
-Usage example:
-
-  var p = subprocess.call({
-    command: '/bin/foo',
-    arguments: ['-v', 'foo'],
-    environment: [ "XYZ=abc", "MYVAR=def" ],
-    charset: 'UTF-8',
-    workdir: '/home/foo',
-    //stdin: "some value to write to stdin\nfoobar",
-    stdin: function(stdin) {
-      stdin.write("some value to write to stdin\nfoobar");
-      stdin.close();
-    },
-    stdout: function(data) {
-      dump("got data on stdout:" + data + "\n");
-    },
-    stderr: function(data) {
-      dump("got data on stderr:" + data + "\n");
-    },
-    done: function(result) {
-      dump("process terminated with " + result.exitCode + "\n");
-    },
-    mergeStderr: false
-  });
-
-  p.wait(); // wait for the subprocess to terminate,
-            // this will block the main thread,
-            // only do if you can wait that long
-
-
-Description of subprocess.call(...) Parameters
-----------------------------------------------
-Apart from <command>, all arguments are optional.
-
- command: either a |nsIFile| object pointing to an executable file or a
-              String containing the platform-dependent path to an executable
-              file.
-
- arguments: optional string array containing the arguments to the command.
-
- environment: optional string array containing environment variables to pass
-              to the command. The array elements must have the form
-              "VAR=data". Please note that if environment is defined, it
-              replaces any existing environment variables for the subprocess.
-
- charset: Output is decoded with given charset and a string is returned.
-              If charset is undefined, "UTF-8" is used as default.
-              To get binary data, set this to null and the returned string
-              is not decoded in any way.
-
- workdir: Optional; either a |nsIFile| object or string containing the
-              platform-dependent path to a directory to become the current
-              working directory of the subprocess.
-
- stdin: Optional input data for the process to be passed on standard
-              input. stdin can either be a string or a function.
-              A |string| gets written to stdin and stdin gets closed;
-              A |function| gets passed an object with write and close function.
-              Please note that the write() function will return almost immediately;
-              data is always written asynchronously on a separate thread.
-
- stdout: An optional function that can receive output data from the
-              process. The stdout-function is called asynchronously; it can be
-              called mutliple times during the execution of a process.
-              At a minimum at each occurance of \n or \r.
-              Please note that null-characters might need to be escaped
-              with something like 'data.replace(/\0/g, "\\0");'.
-
- stderr: An optional function that can receive stderr data from the
-              process. The stderr-function is called asynchronously; it can be
-              called mutliple times during the execution of a process. Please
-              note that null-characters might need to be escaped with
-              something like 'data.replace(/\0/g, "\\0");'.
-              (on windows it only gets called once right now)
-
- done: Optional function that is called when the process has terminated.
-              The exit code from the process available via result.exitCode. If
-              stdout is not defined, then the output from stdout is available
-              via result.stdout. stderr data is in result.stderr
-
- mergeStderr: Optional boolean value. If true, stderr is merged with stdout;
-              no data will be provided to stderr.
-
-
-Description of object returned by subprocess.call(...)
-------------------------------------------------------
-The object returned by subprocess.call offers a few methods that can be
-executed:
-
- wait(): waits for the subprocess to terminate. It is not required to use
-              wait; done will be called in any case when the subprocess terminated.
-
- kill(): kill the subprocess. Any open pipes will be closed and
-              done will be called.
-
-
-Other methods exported by subprocess
-------------------------------------
-The following functions help debugging and provide logging facilities.
-
- registerDebugHandler(functionRef): register a handler that is called to get
-                                      debugging information
- registerLogHandler(functionRef): register a handler that is called to get error
-                                      messages
-
- example:
-    subprocess.registerLogHandler( function(s) { dump(s); } );
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess.js
+++ b/addon-sdk/source/lib/sdk/system/child_process/subprocess.js
@@ -1,1687 +1,185 @@
-// -*- coding: utf-8; indent-tabs-mode: nil -*-
-// vim: et:ts=4:sw=4:sts=4:ft=javascript
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Jan Gerber.
- * Portions created by Jan Gerber <j@mailb.org>
- * are Copyright (C) 2011 Jan Gerber.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Patrick Brunschwig <patrick@enigmail.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * This object allows to start a process, and read/write data to/from it
- * using stdin/stdout/stderr streams.
- * Usage example:
- *
- *  var p = subprocess.call({
- *    command:     '/bin/foo',
- *    arguments:   ['-v', 'foo'],
- *    environment: [ "XYZ=abc", "MYVAR=def" ],
- *    charset: 'UTF-8',
- *    workdir: '/home/foo',
- *    //stdin: "some value to write to stdin\nfoobar",
- *    stdin: function(stdin) {
- *      stdin.write("some value to write to stdin\nfoobar");
- *      stdin.close();
- *    },
- *    stdout: function(data) {
- *      dump("got data on stdout:" + data + "\n");
- *    },
- *    stderr: function(data) {
- *      dump("got data on stderr:" + data + "\n");
- *    },
- *    done: function(result) {
- *      dump("process terminated with " + result.exitCode + "\n");
- *    },
- *    mergeStderr: false
- *  });
- *  p.wait(); // wait for the subprocess to terminate
- *            // this will block the main thread,
- *            // only do if you can wait that long
- *
- *
- * Description of parameters:
- * --------------------------
- * Apart from <command>, all arguments are optional.
- *
- * command:     either a |nsIFile| object pointing to an executable file or a
- *              String containing the platform-dependent path to an executable
- *              file.
- *
- * arguments:   optional string array containing the arguments to the command.
- *
- * environment: optional string array containing environment variables to pass
- *              to the command. The array elements must have the form
- *              "VAR=data". Please note that if environment is defined, it
- *              replaces any existing environment variables for the subprocess.
- *
- * charset:     Output is decoded with given charset and a string is returned.
- *              If charset is undefined, "UTF-8" is used as default.
- *              To get binary data, set this to null and the returned string
- *              is not decoded in any way.
- *
- * workdir:     optional; String containing the platform-dependent path to a
- *              directory to become the current working directory of the subprocess.
- *
- * stdin:       optional input data for the process to be passed on standard
- *              input. stdin can either be a string or a function.
- *              A |string| gets written to stdin and stdin gets closed;
- *              A |function| gets passed an object with write and close function.
- *              Please note that the write() function will return almost immediately;
- *              data is always written asynchronously on a separate thread.
- *
- * stdout:      an optional function that can receive output data from the
- *              process. The stdout-function is called asynchronously; it can be
- *              called mutliple times during the execution of a process.
- *              At a minimum at each occurance of \n or \r.
- *              Please note that null-characters might need to be escaped
- *              with something like 'data.replace(/\0/g, "\\0");'.
- *
- * stderr:      an optional function that can receive stderr data from the
- *              process. The stderr-function is called asynchronously; it can be
- *              called mutliple times during the execution of a process. Please
- *              note that null-characters might need to be escaped with
- *              something like 'data.replace(/\0/g, "\\0");'.
- *              (on windows it only gets called once right now)
- *
- * done:        optional function that is called when the process has terminated.
- *              The exit code from the process available via result.exitCode. If
- *              stdout is not defined, then the output from stdout is available
- *              via result.stdout. stderr data is in result.stderr
- *
- * mergeStderr: optional boolean value. If true, stderr is merged with stdout;
- *              no data will be provided to stderr.
- *
- *
- * Description of object returned by subprocess.call(...)
- * ------------------------------------------------------
- * The object returned by subprocess.call offers a few methods that can be
- * executed:
- *
- * wait():         waits for the subprocess to terminate. It is not required to use
- *                 wait; done will be called in any case when the subprocess terminated.
- *
- * kill(hardKill): kill the subprocess. Any open pipes will be closed and
- *                 done will be called.
- *                 hardKill [ignored on Windows]:
- *                  - false: signal the process terminate (SIGTERM)
- *                  - true:  kill the process (SIGKILL)
- *
- *
- * Other methods in subprocess
- * ---------------------------
- *
- * registerDebugHandler(functionRef):   register a handler that is called to get
- *                                      debugging information
- * registerLogHandler(functionRef):     register a handler that is called to get error
- *                                      messages
- *
- * example:
- *    subprocess.registerLogHandler( function(s) { dump(s); } );
- */
+/* 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 { Cc, Ci, Cu, ChromeWorker } = require("chrome");
+const { Ci, Cu } = require("chrome");
 
-Cu.import("resource://gre/modules/ctypes.jsm");
-
-const NS_LOCAL_FILE = "@mozilla.org/file/local;1";
+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);
 }
 
-/*
-Fake require statements to ensure worker scripts are packaged:
-require("./subprocess_worker_win.js");
-require("./subprocess_worker_unix.js");
-*/
-const URL_PREFIX = module.uri.replace(/subprocess\.js/, "");
-const WORKER_URL_WIN = URL_PREFIX + "subprocess_worker_win.js";
-const WORKER_URL_UNIX = URL_PREFIX + "subprocess_worker_unix.js";
-
-//Windows API definitions
-if (ctypes.size_t.size == 8) {
-    var WinABI = ctypes.default_abi;
-} else {
-    var WinABI = ctypes.winapi_abi;
-}
-const WORD = ctypes.uint16_t;
-const DWORD = ctypes.uint32_t;
-const LPDWORD = DWORD.ptr;
-
-const UINT = ctypes.unsigned_int;
-const BOOL = ctypes.bool;
-const HANDLE = ctypes.size_t;
-const HWND = HANDLE;
-const HMODULE = HANDLE;
-const WPARAM = ctypes.size_t;
-const LPARAM = ctypes.size_t;
-const LRESULT = ctypes.size_t;
-const ULONG_PTR = ctypes.uintptr_t;
-const PVOID = ctypes.voidptr_t;
-const LPVOID = PVOID;
-const LPCTSTR = ctypes.char16_t.ptr;
-const LPCWSTR = ctypes.char16_t.ptr;
-const LPTSTR = ctypes.char16_t.ptr;
-const LPSTR = ctypes.char.ptr;
-const LPCSTR = ctypes.char.ptr;
-const LPBYTE = ctypes.char.ptr;
-
-const CREATE_NEW_CONSOLE = 0x00000010;
-const CREATE_NO_WINDOW = 0x08000000;
-const CREATE_UNICODE_ENVIRONMENT = 0x00000400;
-const STARTF_USESHOWWINDOW = 0x00000001;
-const STARTF_USESTDHANDLES = 0x00000100;
-const SW_HIDE = 0;
-const DUPLICATE_SAME_ACCESS = 0x00000002;
-const STILL_ACTIVE = 259;
-const INFINITE = DWORD(0xFFFFFFFF);
-const WAIT_TIMEOUT = 0x00000102;
-
-/*
-typedef struct _SECURITY_ATTRIBUTES {
- DWORD  nLength;
- LPVOID lpSecurityDescriptor;
- BOOL   bInheritHandle;
-} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
-*/
-const SECURITY_ATTRIBUTES = new ctypes.StructType("SECURITY_ATTRIBUTES", [
-    {"nLength": DWORD},
-    {"lpSecurityDescriptor": LPVOID},
-    {"bInheritHandle": BOOL},
-]);
+function awaitPromise(promise) {
+  let value;
+  let resolved = null;
+  promise.then(val => {
+    resolved = true;
+    value = val;
+  }, val => {
+    resolved = false;
+    value = val;
+  });
 
-/*
-typedef struct _STARTUPINFO {
-  DWORD  cb;
-  LPTSTR lpReserved;
-  LPTSTR lpDesktop;
-  LPTSTR lpTitle;
-  DWORD  dwX;
-  DWORD  dwY;
-  DWORD  dwXSize;
-  DWORD  dwYSize;
-  DWORD  dwXCountChars;
-  DWORD  dwYCountChars;
-  DWORD  dwFillAttribute;
-  DWORD  dwFlags;
-  WORD   wShowWindow;
-  WORD   cbReserved2;
-  LPBYTE lpReserved2;
-  HANDLE hStdInput;
-  HANDLE hStdOutput;
-  HANDLE hStdError;
-} STARTUPINFO, *LPSTARTUPINFO;
-*/
-const STARTUPINFO = new ctypes.StructType("STARTUPINFO", [
-    {"cb": DWORD},
-    {"lpReserved": LPTSTR},
-    {"lpDesktop": LPTSTR},
-    {"lpTitle": LPTSTR},
-    {"dwX": DWORD},
-    {"dwY": DWORD},
-    {"dwXSize": DWORD},
-    {"dwYSize": DWORD},
-    {"dwXCountChars": DWORD},
-    {"dwYCountChars": DWORD},
-    {"dwFillAttribute": DWORD},
-    {"dwFlags": DWORD},
-    {"wShowWindow": WORD},
-    {"cbReserved2": WORD},
-    {"lpReserved2": LPBYTE},
-    {"hStdInput": HANDLE},
-    {"hStdOutput": HANDLE},
-    {"hStdError": HANDLE},
-]);
+  while (resolved === null)
+    Services.tm.mainThread.processNextEvent(true);
 
-/*
-typedef struct _PROCESS_INFORMATION {
-  HANDLE hProcess;
-  HANDLE hThread;
-  DWORD  dwProcessId;
-  DWORD  dwThreadId;
-} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
-*/
-const PROCESS_INFORMATION = new ctypes.StructType("PROCESS_INFORMATION", [
-    {"hProcess": HANDLE},
-    {"hThread": HANDLE},
-    {"dwProcessId": DWORD},
-    {"dwThreadId": DWORD},
-]);
-
-/*
-typedef struct _OVERLAPPED {
-  ULONG_PTR Internal;
-  ULONG_PTR InternalHigh;
-  union {
-    struct {
-      DWORD Offset;
-      DWORD OffsetHigh;
-    };
-    PVOID  Pointer;
-  };
-  HANDLE    hEvent;
-} OVERLAPPED, *LPOVERLAPPED;
-*/
-const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
-
-//UNIX definitions
-const pid_t = ctypes.int32_t;
-const WNOHANG = 1;
-const F_GETFD = 1;
-const F_SETFL = 4;
-
-const LIBNAME       = 0;
-const O_NONBLOCK    = 1;
-const RLIM_T        = 2;
-const RLIMIT_NOFILE = 3;
-
-function getPlatformValue(valueType) {
-
-    if (! gXulRuntime)
-        gXulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
-
-    const platformDefaults = {
-        // Windows API:
-        'winnt':   [ 'kernel32.dll' ],
-
-        // Unix API:
-        //            library name   O_NONBLOCK RLIM_T                RLIMIT_NOFILE
-        'darwin':  [ 'libc.dylib',   0x04     , ctypes.uint64_t     , 8 ],
-        'linux':   [ 'libc.so.6',    2024     , ctypes.unsigned_long, 7 ],
-        'freebsd': [ 'libc.so.7',    0x04     , ctypes.int64_t      , 8 ],
-        'openbsd': [ 'libc.so.61.0', 0x04     , ctypes.int64_t      , 8 ],
-        'sunos':   [ 'libc.so',      0x80     , ctypes.unsigned_long, 5 ]
-    }
-
-    return platformDefaults[gXulRuntime.OS.toLowerCase()][valueType];
+  if (resolved === true)
+    return value;
+  throw value;
 }
 
-
-var gDebugFunc = null,
-    gLogFunc = null,
-    gXulRuntime = null;
+let readAllData = Task.async(function* (pipe, read, callback) {
+  let string;
+  while (string = yield read(pipe))
+    callback(string);
+});
 
-function LogError(s) {
-    if (gLogFunc)
-        gLogFunc(s);
-    else
-        dump(s);
-}
-
-function debugLog(s) {
-    if (gDebugFunc)
-        gDebugFunc(s);
-}
-
-function setTimeout(callback, timeout) {
-    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    timer.initWithCallback(callback, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
+let write = (pipe, data) => {
+  let buffer = new Uint8Array(Array.from(data, c => c.charCodeAt(0)));
+  return pipe.write(data);
 };
 
-function getBytes(data) {
-  var string = '';
-  data.forEach(function(x) { string += String.fromCharCode(x < 0 ? x + 256 : x) });
-  return string;
-}
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset !== null) // stop on NULL character for non-binary data
-           break
-        bytes.push(data[i]);
-    }
-    if (!bytes || bytes.length == 0)
-        return string;
-    if(charset === null) {
-        return bytes;
-    }
-    return convertBytes(bytes, charset);
-}
+var subprocess = {
+  call: function(options) {
+    var result;
 
-function convertBytes(bytes, charset) {
-    var string = '';
-    charset = charset || 'UTF-8';
-    var unicodeConv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                        .getService(Ci.nsIScriptableUnicodeConverter);
-    try {
-        unicodeConv.charset = charset;
-        string = unicodeConv.convertFromByteArray(bytes, bytes.length);
-    } catch (ex) {
-        LogError("String conversion failed: "+ex.toString()+"\n")
-        string = '';
-    }
-    string += unicodeConv.Finish();
-    return string;
-}
-
-
-// temporary solution for removal of nsILocalFile
-function getLocalFileApi() {
-  if ("nsILocalFile" in Ci) {
-    return Ci.nsILocalFile;
-  }
-  else
-    return Ci.nsIFile;
-}
+    let procPromise = Task.spawn(function*() {
+      let opts = {};
 
-function getCommandStr(command) {
-    let commandStr = null;
-    if (typeof(command) == "string") {
-        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
-        file.initWithPath(command);
-        if (! (file.isExecutable() && file.isFile()))
-            throw("File '"+command+"' is not an executable file");
-        commandStr = command;
-    }
-    else {
-        if (! (command.isExecutable() && command.isFile()))
-            throw("File '"+command.path+"' is not an executable file");
-        commandStr = command.path;
-    }
-
-    return commandStr;
-}
+      if (options.mergeStderr) {
+        opts.stderr = "stdout"
+      } else if (options.stderr) {
+        opts.stderr = "pipe";
+      }
 
-function getWorkDir(workdir) {
-    let workdirStr = null;
-    if (typeof(workdir) == "string") {
-        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
-        file.initWithPath(workdir);
-        if (! (file.isDirectory()))
-            throw("Directory '"+workdir+"' does not exist");
-        workdirStr = workdir;
-    }
-    else if (workdir) {
-        if (! workdir.isDirectory())
-            throw("Directory '"+workdir.path+"' does not exist");
-        workdirStr = workdir.path;
-    }
-    return workdirStr;
-}
-
+      if (options.command instanceof Ci.nsIFile) {
+        opts.command = options.command.path;
+      } else {
+        opts.command = yield Subprocess.pathSearch(options.command);
+      }
 
-var subprocess = {
-    call: function(options) {
-        options.mergeStderr = options.mergeStderr || false;
-        options.workdir = options.workdir ||  null;
-        options.environment = options.environment || DEFAULT_ENVIRONMENT;
-        if (options.arguments) {
-            var args = options.arguments;
-            options.arguments = [];
-            args.forEach(function(argument) {
-                options.arguments.push(argument);
-            });
-        } else {
-            options.arguments = [];
-        }
+      if (options.workdir) {
+        opts.workdir = options.workdir;
+      }
 
-        options.libc = getPlatformValue(LIBNAME);
-
-        if (gXulRuntime.OS.substring(0, 3) == "WIN") {
-            return subprocess_win32(options);
-        } else {
-            return subprocess_unix(options);
-        }
-
-    },
-    registerDebugHandler: function(func) {
-        gDebugFunc = func;
-    },
-    registerLogHandler: function(func) {
-        gLogFunc = func;
-    }
-};
-
+      opts.arguments = options.arguments || [];
 
 
-function subprocess_win32(options) {
-    var kernel32dll = ctypes.open(options.libc),
-        hChildProcess,
-        active = true,
-        done = false,
-        exitCode = -1,
-        child = {},
-        stdinWorker = null,
-        stdoutWorker = null,
-        stderrWorker = null,
-        pendingWriteCount = 0,
-        readers = options.mergeStderr ? 1 : 2,
-        stdinOpenState = 2,
-        error = '',
-        output = '';
-
-    // stdin pipe states
-    const OPEN = 2;
-    const CLOSEABLE = 1;
-    const CLOSED = 0;
-
-    //api declarations
-    /*
-    BOOL WINAPI CloseHandle(
-      __in  HANDLE hObject
-    );
-    */
-    var CloseHandle = kernel32dll.declare("CloseHandle",
-                                            WinABI,
-                                            BOOL,
-                                            HANDLE
-    );
-
-    /*
-    BOOL WINAPI CreateProcess(
-      __in_opt     LPCTSTR lpApplicationName,
-      __inout_opt  LPTSTR lpCommandLine,
-      __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
-      __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
-      __in         BOOL bInheritHandles,
-      __in         DWORD dwCreationFlags,
-      __in_opt     LPVOID lpEnvironment,
-      __in_opt     LPCTSTR lpCurrentDirectory,
-      __in         LPSTARTUPINFO lpStartupInfo,
-      __out        LPPROCESS_INFORMATION lpProcessInformation
-    );
-     */
-    var CreateProcessW = kernel32dll.declare("CreateProcessW",
-                                            WinABI,
-                                            BOOL,
-                                            LPCTSTR,
-                                            LPTSTR,
-                                            SECURITY_ATTRIBUTES.ptr,
-                                            SECURITY_ATTRIBUTES.ptr,
-                                            BOOL,
-                                            DWORD,
-                                            LPVOID,
-                                            LPCTSTR,
-                                            STARTUPINFO.ptr,
-                                            PROCESS_INFORMATION.ptr
-                                         );
+      // Set up environment
 
-//     /*
-//     BOOL WINAPI ReadFile(
-//       __in         HANDLE hFile,
-//       __out        LPVOID ReadFileBuffer,
-//       __in         DWORD nNumberOfBytesToRead,
-//       __out_opt    LPDWORD lpNumberOfBytesRead,
-//       __inout_opt  LPOVERLAPPED lpOverlapped
-//     );
-//     */
-//     var ReadFileBufferSize = 1024,
-//         ReadFileBuffer = ctypes.char.array(ReadFileBufferSize),
-//         ReadFile = kernel32dll.declare("ReadFile",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ReadFileBuffer,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         OVERLAPPED.ptr
-//     );
-//
-//     /*
-//     BOOL WINAPI PeekNamedPipe(
-//       __in       HANDLE hNamedPipe,
-//       __out_opt  LPVOID lpBuffer,
-//       __in       DWORD nBufferSize,
-//       __out_opt  LPDWORD lpBytesRead,
-//       __out_opt  LPDWORD lpTotalBytesAvail,
-//       __out_opt  LPDWORD lpBytesLeftThisMessage
-//     );
-//     */
-//     var PeekNamedPipe = kernel32dll.declare("PeekNamedPipe",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ReadFileBuffer,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         LPDWORD,
-//                                         LPDWORD
-//     );
-//
-//     /*
-//     BOOL WINAPI WriteFile(
-//       __in         HANDLE hFile,
-//       __in         LPCVOID lpBuffer,
-//       __in         DWORD nNumberOfBytesToWrite,
-//       __out_opt    LPDWORD lpNumberOfBytesWritten,
-//       __inout_opt  LPOVERLAPPED lpOverlapped
-//     );
-//     */
-//     var WriteFile = kernel32dll.declare("WriteFile",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ctypes.char.ptr,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         OVERLAPPED.ptr
-//     );
+      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);
+        }
 
-    /*
-    BOOL WINAPI CreatePipe(
-      __out     PHANDLE hReadPipe,
-      __out     PHANDLE hWritePipe,
-      __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
-      __in      DWORD nSize
-    );
-    */
-    var CreatePipe = kernel32dll.declare("CreatePipe",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE.ptr,
-                                        HANDLE.ptr,
-                                        SECURITY_ATTRIBUTES.ptr,
-                                        DWORD
-    );
-
-    /*
-    HANDLE WINAPI GetCurrentProcess(void);
-    */
-    var GetCurrentProcess = kernel32dll.declare("GetCurrentProcess",
-                                        WinABI,
-                                        HANDLE
-    );
-
-    /*
-    DWORD WINAPI GetLastError(void);
-    */
-    var GetLastError = kernel32dll.declare("GetLastError",
-                                        WinABI,
-                                        DWORD
-    );
-
-    /*
-    BOOL WINAPI DuplicateHandle(
-      __in   HANDLE hSourceProcessHandle,
-      __in   HANDLE hSourceHandle,
-      __in   HANDLE hTargetProcessHandle,
-      __out  LPHANDLE lpTargetHandle,
-      __in   DWORD dwDesiredAccess,
-      __in   BOOL bInheritHandle,
-      __in   DWORD dwOptions
-    );
-    */
-    var DuplicateHandle = kernel32dll.declare("DuplicateHandle",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        HANDLE,
-                                        HANDLE,
-                                        HANDLE.ptr,
-                                        DWORD,
-                                        BOOL,
-                                        DWORD
-    );
+        opts.environment = environment;
+      }
 
 
-    /*
-    BOOL WINAPI GetExitCodeProcess(
-      __in   HANDLE hProcess,
-      __out  LPDWORD lpExitCode
-    );
-    */
-    var GetExitCodeProcess = kernel32dll.declare("GetExitCodeProcess",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        LPDWORD
-    );
-
-    /*
-    DWORD WINAPI WaitForSingleObject(
-      __in  HANDLE hHandle,
-      __in  DWORD dwMilliseconds
-    );
-    */
-    var WaitForSingleObject = kernel32dll.declare("WaitForSingleObject",
-                                        WinABI,
-                                        DWORD,
-                                        HANDLE,
-                                        DWORD
-    );
-
-    /*
-    BOOL WINAPI TerminateProcess(
-      __in  HANDLE hProcess,
-      __in  UINT uExitCode
-    );
-    */
-    var TerminateProcess = kernel32dll.declare("TerminateProcess",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        UINT
-    );
-
-    //functions
-    function popen(command, workdir, args, environment, child) {
-        //escape arguments
-        args.unshift(command);
-        for (var i = 0; i < args.length; i++) {
-          if (typeof args[i] != "string") { args[i] = args[i].toString(); }
-          /* quote arguments with spaces */
-          if (args[i].match(/\s/)) {
-            args[i] = "\"" + args[i] + "\"";
-          }
-          /* If backslash is followed by a quote, double it */
-          args[i] = args[i].replace(/\\\"/g, "\\\\\"");
-        }
-        command = args.join(' ');
-
-        environment = environment || [];
-        if(environment.length) {
-            //An environment block consists of
-            //a null-terminated block of null-terminated strings.
-            //Using CREATE_UNICODE_ENVIRONMENT so needs to be char16_t
-            environment = ctypes.char16_t.array()(environment.join('\0') + '\0');
-        } else {
-            environment = null;
-        }
-
-        var hOutputReadTmp = new HANDLE(),
-            hOutputRead = new HANDLE(),
-            hOutputWrite = new HANDLE();
-
-        var hErrorRead = new HANDLE(),
-            hErrorReadTmp = new HANDLE(),
-            hErrorWrite = new HANDLE();
-
-        var hInputRead = new HANDLE(),
-            hInputWriteTmp = new HANDLE(),
-            hInputWrite = new HANDLE();
-
-        // Set up the security attributes struct.
-        var sa = new SECURITY_ATTRIBUTES();
-        sa.nLength = SECURITY_ATTRIBUTES.size;
-        sa.lpSecurityDescriptor = null;
-        sa.bInheritHandle = true;
-
-        // Create output pipe.
-
-        if(!CreatePipe(hOutputReadTmp.address(), hOutputWrite.address(), sa.address(), 0))
-            LogError('CreatePipe hOutputReadTmp failed');
-
-        if(options.mergeStderr) {
-          // Create a duplicate of the output write handle for the std error
-          // write handle. This is necessary in case the child application
-          // closes one of its std output handles.
-          if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
-                               GetCurrentProcess(), hErrorWrite.address(), 0,
-                               true, DUPLICATE_SAME_ACCESS))
-             LogError("DuplicateHandle hOutputWrite failed");
-        } else {
-            // Create error pipe.
-            if(!CreatePipe(hErrorReadTmp.address(), hErrorWrite.address(), sa.address(), 0))
-                LogError('CreatePipe hErrorReadTmp failed');
-        }
-
-        // Create input pipe.
-        if (!CreatePipe(hInputRead.address(),hInputWriteTmp.address(),sa.address(), 0))
-            LogError("CreatePipe hInputRead failed");
-
-        // Create new output/error read handle and the input write handles. Set
-        // the Properties to FALSE. Otherwise, the child inherits the
-        // properties and, as a result, non-closeable handles to the pipes
-        // are created.
-        if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
-                             GetCurrentProcess(),
-                             hOutputRead.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-             LogError("DupliateHandle hOutputReadTmp failed");
+      let proc = yield Subprocess.call(opts);
 
-        if(!options.mergeStderr) {
-            if (!DuplicateHandle(GetCurrentProcess(), hErrorReadTmp,
-                             GetCurrentProcess(),
-                             hErrorRead.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-             LogError("DupliateHandle hErrorReadTmp failed");
-        }
-        if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
-                             GetCurrentProcess(),
-                             hInputWrite.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-          LogError("DupliateHandle hInputWriteTmp failed");
-
-        // Close inheritable copies of the handles.
-        if (!CloseHandle(hOutputReadTmp)) LogError("CloseHandle hOutputReadTmp failed");
-        if(!options.mergeStderr)
-            if (!CloseHandle(hErrorReadTmp)) LogError("CloseHandle hErrorReadTmp failed");
-        if (!CloseHandle(hInputWriteTmp)) LogError("CloseHandle failed");
-
-        var pi = new PROCESS_INFORMATION();
-        var si = new STARTUPINFO();
-
-        si.cb = STARTUPINFO.size;
-        si.dwFlags = STARTF_USESTDHANDLES;
-        si.hStdInput  = hInputRead;
-        si.hStdOutput = hOutputWrite;
-        si.hStdError  = hErrorWrite;
-
-        // Launch the process
-        if(!CreateProcessW(null,            // executable name
-                           command,         // command buffer
-                           null,            // process security attribute
-                           null,            // thread security attribute
-                           true,            // inherits system handles
-                           CREATE_UNICODE_ENVIRONMENT|CREATE_NO_WINDOW, // process flags
-                           environment,     // envrionment block
-                           workdir,          // set as current directory
-                           si.address(),    // (in) startup information
-                           pi.address()     // (out) process information
-        ))
-            throw("Fatal - Could not launch subprocess '"+command+"'");
-
-        // Close any unnecessary handles.
-        if (!CloseHandle(pi.hThread))
-            LogError("CloseHandle pi.hThread failed");
-
-        // Close pipe handles (do not continue to modify the parent).
-        // You need to make sure that no handles to the write end of the
-        // output pipe are maintained in this process or else the pipe will
-        // not close when the child process exits and the ReadFile will hang.
-        if (!CloseHandle(hInputRead)) LogError("CloseHandle hInputRead failed");
-        if (!CloseHandle(hOutputWrite)) LogError("CloseHandle hOutputWrite failed");
-        if (!CloseHandle(hErrorWrite)) LogError("CloseHandle hErrorWrite failed");
-
-        //return values
-        child.stdin = hInputWrite;
-        child.stdout = hOutputRead;
-        child.stderr = options.mergeStderr ? undefined : hErrorRead;
-        child.process = pi.hProcess;
-        return pi.hProcess;
-    }
-
-    /*
-     * createStdinWriter ()
-     *
-     * Create a ChromeWorker object for writing data to the subprocess' stdin
-     * pipe. The ChromeWorker object lives on a separate thread; this avoids
-     * internal deadlocks.
-     */
-    function createStdinWriter() {
-        debugLog("Creating new stdin worker\n");
-        stdinWorker = new ChromeWorker(WORKER_URL_WIN);
-        stdinWorker.onmessage = function(event) {
-            switch(event.data) {
-            case "WriteOK":
-                pendingWriteCount--;
-                debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
-                break;
-            case "ClosedOK":
-                stdinOpenState = CLOSED;
-                debugLog("Stdin pipe closed\n");
-                break;
-            default:
-                debugLog("got msg from stdinWorker: "+event.data+"\n");
-            }
-        }
-        stdinWorker.onerror = function(error) {
-            pendingWriteCount--;
-            LogError("got error from stdinWorker: "+error.message+"\n");
-        }
-
-        stdinWorker.postMessage({msg: "init", libc: options.libc});
-    }
-
-    /*
-     * writeStdin()
-     * @data: String containing the data to write
-     *
-     * Write data to the subprocess' stdin (equals to sending a request to the
-     * ChromeWorker object to write the data).
-     */
-    function writeStdin(data) {
-        ++pendingWriteCount;
-        debugLog("sending "+data.length+" bytes to stdinWorker\n");
-        var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
-
-        stdinWorker.postMessage({
-                msg: 'write',
-                pipe: pipePtr,
-                data: data
-            });
-    }
-
-    /*
-     * closeStdinHandle()
-     *
-     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
-     * close the pipe. The ChromeWorker will only close the pipe after the last write
-     * request process is done.
-     */
-
-    function closeStdinHandle() {
-        debugLog("trying to close stdin\n");
-        if (stdinOpenState != OPEN) return;
-        stdinOpenState = CLOSEABLE;
-
-        if (stdinWorker) {
-            debugLog("sending close stdin to worker\n");
-            var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
-            stdinWorker.postMessage({
-                msg: 'close',
-                pipe: pipePtr
-            });
-        }
-        else {
-            stdinOpenState = CLOSED;
-            debugLog("Closing Stdin\n");
-            CloseHandle(child.stdin) || LogError("CloseHandle hInputWrite failed");
-        }
-    }
+      Object.defineProperty(result, "pid", {
+        value: proc.pid,
+        enumerable: true,
+        configurable: true,
+      });
 
 
-    /*
-     * createReader(pipe, name)
-     *
-     * @pipe: handle to the pipe
-     * @name: String containing the pipe name (stdout or stderr)
-     *
-     * Create a ChromeWorker object for reading data asynchronously from
-     * the pipe (i.e. on a separate thread), and passing the result back to
-     * the caller.
-     */
-    function createReader(pipe, name, callbackFunc) {
-        var worker = new ChromeWorker(WORKER_URL_WIN);
-        worker.onmessage = function(event) {
-            switch(event.data.msg) {
-            case "data":
-                debugLog("got "+event.data.count+" bytes from "+name+"\n");
-                var data = '';
-                if (options.charset === null) {
-                    data = getBytes(event.data.data);
-                }
-                else {
-                    try {
-                        data = convertBytes(event.data.data, options.charset);
-                    }
-                    catch(ex) {
-                        console.warn("error decoding output: " + ex);
-                        data = getBytes(event.data.data);
-                    }
-                }
+      let promises = [];
 
-                callbackFunc(data);
-                break;
-            case "done":
-                debugLog("Pipe "+name+" closed\n");
-                --readers;
-                if (readers == 0) cleanup();
-                break;
-            default:
-                debugLog("Got msg from "+name+": "+event.data.data+"\n");
-            }
-        }
+      // Set up IO handlers.
 
-        worker.onerror = function(errorMsg) {
-            LogError("Got error from "+name+": "+errorMsg.message);
-        }
-
-        var pipePtr = parseInt(ctypes.cast(pipe.address(), ctypes.uintptr_t).value);
-
-        worker.postMessage({
-                msg: 'read',
-                pipe: pipePtr,
-                libc: options.libc,
-                charset: options.charset === null ? "null" : options.charset,
-                name: name
-            });
-
-        return worker;
-    }
-
-    /*
-     * readPipes()
-     *
-     * Open the pipes for reading from stdout and stderr
-     */
-    function readPipes() {
-        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
-            if(options.stdout) {
-                setTimeout(function() {
-                    options.stdout(data);
-                }, 0);
-            } else {
-                output += data;
-            }
-        });
-
-
-        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
-            if(options.stderr) {
-                setTimeout(function() {
-                    options.stderr(data);
-                }, 0);
-            } else {
-                error += data;
-            }
-        });
-    }
+      let read = pipe => pipe.readString();
+      if (options.charset === null) {
+        read = pipe => {
+          return pipe.read().then(buffer => {
+            return String.fromCharCode(...buffer);
+          });
+        };
+      }
 
-    /*
-     * cleanup()
-     *
-     * close stdin if needed, get the exit code from the subprocess and invoke
-     * the caller's done() function.
-     *
-     * Note: because stdout() and stderr() are called using setTimeout, we need to
-     * do the same here in order to guarantee the message sequence.
-     */
-    function cleanup() {
-        debugLog("Cleanup called\n");
-        if(active) {
-            active = false;
-
-            closeStdinHandle(); // should only be required in case of errors
-
-            var exit = new DWORD();
-            GetExitCodeProcess(child.process, exit.address());
-            exitCode = exit.value;
-
-            if (stdinWorker)
-                stdinWorker.postMessage({msg: 'stop'})
+      if (options.stdout)
+        promises.push(readAllData(proc.stdout, read, options.stdout));
 
-            setTimeout(function _done() {
-                if (options.done) {
-                    try {
-                        options.done({
-                            exitCode: exitCode,
-                            stdout: output,
-                            stderr: error,
-                        });
-                    }
-                    catch (ex) {
-                        // prevent from blocking if options.done() throws an error
-                        done = true;
-                        throw ex;
-                    }
-                }
-                done = true;
-            }, 0);
-            kernel32dll.close();
-        }
-    }
-
-    var cmdStr = getCommandStr(options.command);
-    var workDir = getWorkDir(options.workdir);
+      if (options.stderr && proc.stderr)
+        promises.push(readAllData(proc.stderr, read, options.stderr));
 
-    //main
-    hChildProcess = popen(cmdStr, workDir, options.arguments, options.environment, child);
-
-    readPipes();
-
-    if (options.stdin) {
-       createStdinWriter();
+      // Process stdin
 
-        if(typeof(options.stdin) == 'function') {
-            try {
-                options.stdin({
-                    write: function(data) {
-                        writeStdin(data);
-                    },
-                    close: function() {
-                        closeStdinHandle();
-                    }
-                });
-            }
-            catch (ex) {
-                // prevent from failing if options.stdin() throws an exception
-                closeStdinHandle();
-                throw ex;
-            }
-        } else {
-            writeStdin(options.stdin);
-            closeStdinHandle();
-        }
-    }
-    else
-        closeStdinHandle();
-
-    return {
-        kill: function(hardKill) {
-            // hardKill is currently ignored on Windows
-            var r = !!TerminateProcess(child.process, 255);
-            cleanup(-1);
-            return r;
-        },
-        wait: function() {
-            // wait for async operations to complete
-            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
-            while (!done) thread.processNextEvent(true);
-
-            return exitCode;
-        }
-    }
-}
+      if (typeof options.stdin === "string") {
+        write(proc.stdin, options.stdin);
+        proc.stdin.close();
+      }
 
 
-function subprocess_unix(options) {
-    // stdin pipe states
-    const OPEN = 2;
-    const CLOSEABLE = 1;
-    const CLOSED = 0;
-
-    var libc = ctypes.open(options.libc),
-        active = true,
-        done = false,
-        exitCode = -1,
-        workerExitCode = 0,
-        child = {},
-        pid = -1,
-        stdinWorker = null,
-        stdoutWorker = null,
-        stderrWorker = null,
-        pendingWriteCount = 0,
-        readers = options.mergeStderr ? 1 : 2,
-        stdinOpenState = OPEN,
-        error = '',
-        output = '';
-
-    //api declarations
+      // Handle process completion
 
-    //pid_t fork(void);
-    var fork = libc.declare("fork",
-                         ctypes.default_abi,
-                         pid_t
-    );
-
-    //NULL terminated array of strings, argv[0] will be command >> + 2
-    var argv = ctypes.char.ptr.array(options.arguments.length + 2);
-    var envp = ctypes.char.ptr.array(options.environment.length + 1);
-
-    // posix_spawn_file_actions_t is a complex struct that may be different on
-    // each platform. We do not care about its attributes, we don't need to
-    // get access to them, but we do need to allocate the right amount
-    // of memory for it.
-    // At 2013/10/28, its size was 80 on linux, but better be safe (and larger),
-    // than crash when posix_spawn_file_actions_init fill `action` with zeros.
-    // Use `gcc sizeof_fileaction.c && ./a.out` to check that size.
-    var posix_spawn_file_actions_t = ctypes.uint8_t.array(100);
-
-    //int posix_spawn(pid_t *restrict pid, const char *restrict path,
-    //   const posix_spawn_file_actions_t *file_actions,
-    //   const posix_spawnattr_t *restrict attrp,
-    //   char *const argv[restrict], char *const envp[restrict]);
-    var posix_spawn = libc.declare("posix_spawn",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         pid_t.ptr,
-                         ctypes.char.ptr,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.voidptr_t,
-                         argv,
-                         envp
-    );
+      if (options.done)
+        Promise.all(promises)
+          .then(() => proc.wait())
+          .then(options.done);
 
-    //int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
-    var posix_spawn_file_actions_init = libc.declare("posix_spawn_file_actions_init",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr
-    );
-
-    //int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
-    var posix_spawn_file_actions_destroy = libc.declare("posix_spawn_file_actions_destroy",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr
-    );
-
-    // int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *
-    //                                      file_actions, int fildes, int newfildes);
-    var posix_spawn_file_actions_adddup2 = libc.declare("posix_spawn_file_actions_adddup2",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.int,
-                         ctypes.int
-    );
+      return proc;
+    });
 
-    // int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *
-    //                                       file_actions, int fildes);
-    var posix_spawn_file_actions_addclose = libc.declare("posix_spawn_file_actions_addclose",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.int
-    );
-
-    //int pipe(int pipefd[2]);
-    var pipefd = ctypes.int.array(2);
-    var pipe = libc.declare("pipe",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         pipefd
-    );
-
-    //int close(int fd);
-    var close = libc.declare("close",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int
-    );
-
-    //pid_t waitpid(pid_t pid, int *status, int options);
-    var waitpid = libc.declare("waitpid",
-                          ctypes.default_abi,
-                          pid_t,
-                          pid_t,
-                          ctypes.int.ptr,
-                          ctypes.int
-    );
-
-    //int kill(pid_t pid, int sig);
-    var kill = libc.declare("kill",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          pid_t,
-                          ctypes.int
-    );
+    procPromise.catch(e => {
+      Cu.reportError(e instanceof Error ? e : e.message || e);
+      if (options.done)
+        options.done({exitCode: -1});
+    });
 
-    //int read(int fd, void *buf, size_t count);
-    var bufferSize = 1024;
-    var buffer = ctypes.char.array(bufferSize);
-    var read = libc.declare("read",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          buffer,
-                          ctypes.int
-    );
-
-    //ssize_t write(int fd, const void *buf, size_t count);
-    var write = libc.declare("write",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.char.ptr,
-                          ctypes.int
-    );
-
-    //int chdir(const char *path);
-    var chdir = libc.declare("chdir",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.char.ptr
-    );
-
-    //int fcntl(int fd, int cmd, ... /* arg */ );
-    var fcntl = libc.declare("fcntl",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.int
-    );
-
-    function popen(command, workdir, args, environment, child) {
-        var _in,
-            _out,
-            _err,
-            pid,
-            rc;
-        _in = new pipefd();
-        _out = new pipefd();
-        if(!options.mergeStderr)
-            _err = new pipefd();
-
-        var _args = argv();
-        args.unshift(command);
-        for(var i=0;i<args.length;i++) {
-            _args[i] = ctypes.char.array()(args[i]);
-        }
-        var _envp = envp();
-        for(var i=0;i<environment.length;i++) {
-            _envp[i] = ctypes.char.array()(environment[i]);
-            // LogError(_envp);
-        }
+    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);
+          });
+        },
 
-        rc = pipe(_in);
-        if (rc < 0) {
-            return -1;
-        }
-        rc = pipe(_out);
-        fcntl(_out[0], F_SETFL, getPlatformValue(O_NONBLOCK));
-        if (rc < 0) {
-            close(_in[0]);
-            close(_in[1]);
-            return -1
-        }
-        if(!options.mergeStderr) {
-            rc = pipe(_err);
-            fcntl(_err[0], F_SETFL, getPlatformValue(O_NONBLOCK));
-            if (rc < 0) {
-                close(_in[0]);
-                close(_in[1]);
-                close(_out[0]);
-                close(_out[1]);
-                return -1
-            }
-        }
-
-        let STDIN_FILENO = 0;
-        let STDOUT_FILENO = 1;
-        let STDERR_FILENO = 2;
-
-        let action = posix_spawn_file_actions_t();
-        posix_spawn_file_actions_init(action.address());
-
-        posix_spawn_file_actions_adddup2(action.address(), _in[0], STDIN_FILENO);
-        posix_spawn_file_actions_addclose(action.address(), _in[1]);
-        posix_spawn_file_actions_addclose(action.address(), _in[0]);
-
-        posix_spawn_file_actions_adddup2(action.address(), _out[1], STDOUT_FILENO);
-        posix_spawn_file_actions_addclose(action.address(), _out[1]);
-        posix_spawn_file_actions_addclose(action.address(), _out[0]);
-
-        if (!options.mergeStderr) {
-          posix_spawn_file_actions_adddup2(action.address(), _err[1], STDERR_FILENO);
-          posix_spawn_file_actions_addclose(action.address(), _err[1]);
-          posix_spawn_file_actions_addclose(action.address(), _err[0]);
-        }
-
-        // posix_spawn doesn't support setting a custom workdir for the child,
-        // so change the cwd in the parent process before launching the child process.
-        if (workdir) {
-          if (chdir(workdir) < 0) {
-            throw new Error("Unable to change workdir before launching child process");
-          }
-        }
-
-        closeOtherFds(action, _in[1], _out[0], options.mergeStderr ? undefined : _err[0]);
-
-        let id = pid_t(0);
-        let rv = posix_spawn(id.address(), command, action.address(), null, _args, _envp);
-        posix_spawn_file_actions_destroy(action.address());
-        if (rv != 0) {
-          // we should not really end up here
-          if(!options.mergeStderr) {
-            close(_err[0]);
-            close(_err[1]);
-          }
-          close(_out[0]);
-          close(_out[1]);
-          close(_in[0]);
-          close(_in[1]);
-          throw new Error("Fatal - failed to create subprocess '"+command+"'");
-        }
-        pid = id.value;
-
-        close(_in[0]);
-        close(_out[1]);
-        if (!options.mergeStderr)
-          close(_err[1]);
-        child.stdin  = _in[1];
-        child.stdout = _out[0];
-        child.stderr = options.mergeStderr ? undefined : _err[0];
-        child.pid = pid;
-
-        return pid;
+        close() {
+          procPromise.then(proc => {
+            proc.stdin.close();
+          });
+        },
+      });
     }
 
-
-    // close any file descriptors that are not required for the process
-    function closeOtherFds(action, fdIn, fdOut, fdErr) {
-        // Unfortunately on mac, any fd registered in posix_spawn_file_actions_addclose
-        // that can't be closed correctly will make posix_spawn fail...
-        // Even if we ensure registering only still opened fds.
-        if (gXulRuntime.OS == "Darwin")
-            return;
-
-        var maxFD = 256; // arbitrary max
-
-
-        var rlim_t = getPlatformValue(RLIM_T);
-
-        const RLIMITS = new ctypes.StructType("RLIMITS", [
-            {"rlim_cur": rlim_t},
-            {"rlim_max": rlim_t}
-        ]);
-
-        try {
-            var getrlimit = libc.declare("getrlimit",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  RLIMITS.ptr
-            );
-
-            var rl = new RLIMITS();
-            if (getrlimit(getPlatformValue(RLIMIT_NOFILE), rl.address()) == 0) {
-                maxFD = rl.rlim_cur;
-            }
-            debugLog("getlimit: maxFD="+maxFD+"\n");
-
-        }
-        catch(ex) {
-            debugLog("getrlimit: no such function on this OS\n");
-            debugLog(ex.toString());
-        }
-
-        // close any file descriptors
-        // fd's 0-2 are already closed
-        for (var i = 3; i < maxFD; i++) {
-            if (i != fdIn && i != fdOut && i != fdErr && fcntl(i, F_GETFD, -1) >= 0) {
-                posix_spawn_file_actions_addclose(action.address(), i);
-            }
-        }
-    }
-
-    /*
-     * createStdinWriter ()
-     *
-     * Create a ChromeWorker object for writing data to the subprocess' stdin
-     * pipe. The ChromeWorker object lives on a separate thread; this avoids
-     * internal deadlocks.
-     */
-    function createStdinWriter() {
-        debugLog("Creating new stdin worker\n");
-        stdinWorker = new ChromeWorker(WORKER_URL_UNIX);
-        stdinWorker.onmessage = function(event) {
-            switch (event.data.msg) {
-            case "info":
-                switch(event.data.data) {
-                case "WriteOK":
-                    pendingWriteCount--;
-                    debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
-                    break;
-                case "ClosedOK":
-                    stdinOpenState = CLOSED;
-                    debugLog("Stdin pipe closed\n");
-                    break;
-                default:
-                    debugLog("got msg from stdinWorker: "+event.data.data+"\n");
-                }
-                break;
-            case "debug":
-                debugLog("stdinWorker: "+event.data.data+"\n");
-                break;
-            case "error":
-                LogError("got error from stdinWorker: "+event.data.data+"\n");
-                pendingWriteCount = 0;
-                stdinOpenState = CLOSED;
-            }
-        }
-        stdinWorker.onerror = function(error) {
-            pendingWriteCount = 0;
-            closeStdinHandle();
-            LogError("got error from stdinWorker: "+error.message+"\n");
-        }
-        stdinWorker.postMessage({msg: "init", libc: options.libc});
-    }
-
-    /*
-     * writeStdin()
-     * @data: String containing the data to write
-     *
-     * Write data to the subprocess' stdin (equals to sending a request to the
-     * ChromeWorker object to write the data).
-     */
-    function writeStdin(data) {
-        if (stdinOpenState == CLOSED) return; // do not write to closed pipes
-
-        ++pendingWriteCount;
-        debugLog("sending "+data.length+" bytes to stdinWorker\n");
-        var pipe = parseInt(child.stdin);
-
-        stdinWorker.postMessage({
-            msg: 'write',
-            pipe: pipe,
-            data: data
-        });
-    }
-
-
-    /*
-     * closeStdinHandle()
-     *
-     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
-     * close the pipe. The ChromeWorker will only close the pipe after the last write
-     * request process is done.
-     */
-
-    function closeStdinHandle() {
-        debugLog("trying to close stdin\n");
-        if (stdinOpenState != OPEN) return;
-        stdinOpenState = CLOSEABLE;
-
-        if (stdinWorker) {
-            debugLog("sending close stdin to worker\n");
-            var pipePtr = parseInt(child.stdin);
-
-            stdinWorker.postMessage({
-                msg: 'close',
-                pipe: pipePtr
-            });
-        }
-        else {
-            stdinOpenState = CLOSED;
-            debugLog("Closing Stdin\n");
-            close(child.stdin) && LogError("CloseHandle stdin failed");
-        }
-    }
-
+    result = {
+      get pid() {
+        return awaitPromise(procPromise.then(proc => {
+          return proc.pid;
+        }));
+      },
 
-    /*
-     * createReader(pipe, name)
-     *
-     * @pipe: handle to the pipe
-     * @name: String containing the pipe name (stdout or stderr)
-     * @callbackFunc: function to be called with the read data
-     *
-     * Create a ChromeWorker object for reading data asynchronously from
-     * the pipe (i.e. on a separate thread), and passing the result back to
-     * the caller.
-     *
-     */
-    function createReader(pipe, name, callbackFunc) {
-        var worker = new ChromeWorker(WORKER_URL_UNIX);
-        worker.onmessage = function(event) {
-            switch(event.data.msg) {
-            case "data":
-                debugLog("got "+event.data.count+" bytes from "+name+"\n");
-                var data = '';
-                if (options.charset === null) {
-                    data = getBytes(event.data.data);
-                }
-                else {
-                    try {
-                        data = convertBytes(event.data.data, options.charset);
-                    }
-                    catch(ex) {
-                        console.warn("error decoding output: " + ex);
-                        data = getBytes(event.data.data);
-                    }
-                }
-
-                callbackFunc(data);
-                break;
-            case "done":
-                debugLog("Pipe "+name+" closed\n");
-                if (event.data.data > 0) workerExitCode = event.data.data;
-                --readers;
-                if (readers == 0) cleanup();
-                break;
-            default:
-                debugLog("Got msg from "+name+": "+event.data.data+"\n");
-            }
-        }
-        worker.onerror = function(error) {
-            LogError("Got error from "+name+": "+error.message);
-        }
-
-        worker.postMessage({
-                msg: 'read',
-                pipe: pipe,
-                pid: pid,
-                libc: options.libc,
-                charset: options.charset === null ? "null" : options.charset,
-                name: name
-            });
-
-        return worker;
-    }
-
-    /*
-     * readPipes()
-     *
-     * Open the pipes for reading from stdout and stderr
-     */
-    function readPipes() {
-
-        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
-            if(options.stdout) {
-                setTimeout(function() {
-                    options.stdout(data);
-                }, 0);
-            } else {
-                output += data;
-            }
-        });
-
-        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
-            if(options.stderr) {
-                setTimeout(function() {
-                    options.stderr(data);
-                 }, 0);
-            } else {
-                error += data;
-            }
-        });
-    }
+      wait() {
+        return awaitPromise(procPromise.then(proc => {
+          return proc.wait().then(({exitCode}) => exitCode);
+        }));
+      },
 
-    function cleanup() {
-        debugLog("Cleanup called\n");
-        if(active) {
-            active = false;
-
-            closeStdinHandle(); // should only be required in case of errors
-
-            var result, status = ctypes.int();
-            result = waitpid(child.pid, status.address(), 0);
-            if (result > 0)
-                exitCode = status.value
-            else
-                if (workerExitCode >= 0)
-                    exitCode = workerExitCode
-                else
-                    exitCode = status.value;
-
-            if (stdinWorker)
-                stdinWorker.postMessage({msg: 'stop'})
-
-            setTimeout(function _done() {
-                if (options.done) {
-                    try {
-                        options.done({
-                            exitCode: exitCode,
-                            stdout: output,
-                            stderr: error,
-                        });
-                    }
-                    catch(ex) {
-                        // prevent from blocking if options.done() throws an error
-                        done = true;
-                        throw ex;
-                    }
-
-                }
-                done = true;
-            }, 0);
-
-            libc.close();
-        }
-    }
-
-    //main
-
-    var cmdStr = getCommandStr(options.command);
-    var workDir = getWorkDir(options.workdir);
+      kill(hard = false) {
+        procPromise.then(proc => {
+          proc.kill(hard ? 0 : undefined);
+        });
+      },
+    };
 
-    child = {};
-    pid = popen(cmdStr, workDir, options.arguments, options.environment, child);
-
-    debugLog("subprocess started; got PID "+pid+"\n");
-
-    readPipes();
-
-    if (options.stdin) {
-        createStdinWriter();
-        if(typeof(options.stdin) == 'function') {
-            try {
-                options.stdin({
-                    write: function(data) {
-                        writeStdin(data);
-                    },
-                    close: function() {
-                        closeStdinHandle();
-                    }
-                });
-            }
-            catch(ex) {
-                // prevent from failing if options.stdin() throws an exception
-                closeStdinHandle();
-                throw ex;
-            }
-        } else {
-            writeStdin(options.stdin);
-            closeStdinHandle();
-        }
-    }
-
-    return {
-        wait: function() {
-            // wait for async operations to complete
-            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
-            while (! done) thread.processNextEvent(true)
-            return exitCode;
-        },
-        kill: function(hardKill) {
-            var rv = kill(pid, (hardKill ? 9: 15));
-            cleanup(-1);
-            return rv;
-        },
-        pid: pid
-    }
-}
-
+    return result;
+  },
+};
 
 module.exports = subprocess;
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_unix.js
+++ /dev/null
@@ -1,278 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Patrick Brunschwig.
- * Portions created by Patrick Brunschwig <patrick@enigmail.net>
- * are Copyright (C) 2011 Patrick Brunschwig.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Jan Gerber <j@mailb.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...)
- * to process stdin/stdout/stderr on separate threads.
- *
- */
-
-// Being a ChromeWorker object, implicitly uses the following:
-// Cu.import("resource://gre/modules/ctypes.jsm");
-
-'use strict';
-
-const BufferSize = 1024;
-
-var libc = null;
-var libcFunc = {};
-
-
-/*
-    struct pollfd {
-         int    fd;       // file descriptor
-         short  events;   // events to look for
-         short  revents;  // events returned
-     };
-*/
-
-var pollfd = new ctypes.StructType("pollfd",
-                        [   {'fd': ctypes.int},
-                            {'events': ctypes.short},
-                            {'revents': ctypes.short}
-                        ]);
-
-var WriteBuffer = ctypes.uint8_t.array(BufferSize);
-var ReadBuffer = ctypes.char.array(BufferSize);
-
-
-const POLLIN     = 0x0001;
-const POLLOUT    = 0x0004;
-
-const POLLERR    = 0x0008;         // some poll error occurred
-const POLLHUP    = 0x0010;         // file descriptor was "hung up"
-const POLLNVAL   = 0x0020;         // requested events "invalid"
-
-const WNOHANG    = 0x01;
-
-const pid_t = ctypes.int32_t;
-
-const INDEFINITE = -1;
-const NOWAIT     = 0;
-const WAITTIME   = 200  // wait time for poll() in ms
-
-function initLibc(libName) {
-    postMessage({msg: "debug", data: "initialising library with "+ libName});
-
-    libc = ctypes.open(libName);
-
-    libcFunc.pollFds = pollfd.array(1);
-
-    // int poll(struct pollfd fds[], nfds_t nfds, int timeout);
-    libcFunc.poll = libc.declare("poll",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  libcFunc.pollFds,
-                                  ctypes.unsigned_int,
-                                  ctypes.int);
-
-    //ssize_t write(int fd, const void *buf, size_t count);
-    // NOTE: buf is declared as array of unsigned int8 instead of char to avoid
-    // implicit charset conversion
-    libcFunc.write = libc.declare("write",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  WriteBuffer,
-                                  ctypes.int);
-
-    //int read(int fd, void *buf, size_t count);
-    libcFunc.read = libc.declare("read",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  ReadBuffer,
-                                  ctypes.int);
-
-    //int pipe(int pipefd[2]);
-    libcFunc.pipefd = ctypes.int.array(2);
-
-    //int close(int fd);
-    libcFunc.close = libc.declare("close",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int);
-
-    //pid_t waitpid(pid_t pid, int *status, int options);
-    libcFunc.waitpid = libc.declare("waitpid",
-                                  ctypes.default_abi,
-                                  pid_t,
-                                  pid_t,
-                                  ctypes.int.ptr,
-                                  ctypes.int);
-}
-
-function closePipe(pipe) {
-    libcFunc.close(pipe);
-}
-
-function writePipe(pipe, data) {
-
-    postMessage({msg: "debug", data: "trying to write to "+pipe});
-
-    let numChunks = Math.floor(data.length / BufferSize);
-    let pData = new WriteBuffer();
-
-    for (var chunk = 0; chunk <= numChunks; chunk ++) {
-        let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
-        for (var i=0; i < numBytes; i++) {
-            pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
-        }
-
-        let bytesWritten = libcFunc.write(pipe, pData, numBytes);
-        if (bytesWritten != numBytes) {
-            closePipe(pipe);
-            postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"});
-            close();
-        }
-    }
-    postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"});
-}
-
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
-           break;
-
-        bytes.push(data[i]);
-    }
-
-    return bytes;
-}
-
-function readPipe(pipe, charset, pid) {
-    var p = new libcFunc.pollFds;
-    p[0].fd = pipe;
-    p[0].events = POLLIN | POLLERR | POLLHUP;
-    p[0].revents = 0;
-    var pollTimeout = WAITTIME;
-    var exitCode = -1;
-    var readCount = 0;
-    var result, status = ctypes.int();
-    result = 0;
-
-
-    const i=0;
-    while (true) {
-        if (result == 0) {
-            result = libcFunc.waitpid(pid, status.address(), WNOHANG);
-            if (result > 0) {
-                pollTimeout = NOWAIT;
-                exitCode = parseInt(status.value);
-                postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value });
-            }
-        }
-        var r = libcFunc.poll(p, 1, pollTimeout);
-        if (r > 0) {
-            if (p[i].revents & POLLIN) {
-                postMessage({msg: "debug", data: "reading next chunk"});
-                readCount = readPolledFd(p[i].fd, charset);
-                if (readCount == 0) break;
-            }
-
-            if (p[i].revents & POLLHUP) {
-                postMessage({msg: "debug", data: "poll returned HUP"});
-                break;
-            }
-            else if (p[i].revents & POLLERR) {
-                postMessage({msg: "error", data: "poll returned error"});
-                break;
-            }
-            else if (p[i].revents != POLLIN) {
-                postMessage({msg: "error", data: "poll returned "+p[i]});
-                break;
-            }
-        }
-        else
-            if (pollTimeout == 0 || r < 0) break;
-    }
-
-    // continue reading until the buffer is empty
-    while (readCount > 0) {
-      readCount = readPolledFd(pipe, charset);
-    }
-
-    libcFunc.close(pipe);
-    postMessage({msg: "done", data: exitCode });
-    libc.close();
-    close();
-}
-
-function readPolledFd(pipe, charset) {
-    var line = new ReadBuffer();
-    var r = libcFunc.read(pipe, line, BufferSize);
-
-    if (r > 0) {
-        var c = readString(line, r, charset);
-        postMessage({msg: "data", data: c, count: c.length});
-    }
-    return r;
-}
-
-onmessage = function (event) {
-    switch (event.data.msg) {
-    case "init":
-        initLibc(event.data.libc);
-        break;
-    case "read":
-        initLibc(event.data.libc);
-        readPipe(event.data.pipe, event.data.charset, event.data.pid);
-        break;
-    case "write":
-        // data contents:
-        //   msg: 'write'
-        //   data: the data (string) to write
-        //   pipe: ptr to pipe
-        writePipe(event.data.pipe, event.data.data);
-        postMessage({msg: "info", data: "WriteOK"});
-        break;
-    case "close":
-        postMessage({msg: "debug", data: "closing stdin\n"});
-
-        closePipe(event.data.pipe);
-        postMessage({msg: "info", data: "ClosedOK"});
-        break;
-    case "stop":
-        libc.close(); // do not use libc after this point
-        close();
-        break;
-    default:
-        throw("error: Unknown command"+event.data.msg+"\n");
-    }
-    return;
-};
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_win.js
+++ /dev/null
@@ -1,237 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Patrick Brunschwig.
- * Portions created by Patrick Brunschwig <patrick@enigmail.net>
- * are Copyright (C) 2011 Patrick Brunschwig.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Jan Gerber <j@mailb.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr
- * on separate threads.
- *
- */
-
-// Being a ChromeWorker object, implicitly uses the following:
-// Cu.import("resource://gre/modules/ctypes.jsm");
-
-'use strict';
-
-const BufferSize = 1024;
-
-const BOOL = ctypes.bool;
-const HANDLE = ctypes.size_t;
-const DWORD = ctypes.uint32_t;
-const LPDWORD = DWORD.ptr;
-const PVOID = ctypes.voidptr_t;
-const LPVOID = PVOID;
-
-/*
-typedef struct _OVERLAPPED {
-  ULONG_PTR Internal;
-  ULONG_PTR InternalHigh;
-  union {
-    struct {
-      DWORD Offset;
-      DWORD OffsetHigh;
-    };
-    PVOID  Pointer;
-  };
-  HANDLE    hEvent;
-} OVERLAPPED, *LPOVERLAPPED;
-*/
-const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
-
-var ReadFileBuffer = ctypes.char.array(BufferSize);
-var WriteFileBuffer = ctypes.uint8_t.array(BufferSize);
-
-var kernel32dll = null;
-var libFunc = {};
-
-function initLib(libName) {
-    if (ctypes.size_t.size == 8) {
-        var WinABI = ctypes.default_abi;
-    } else {
-        var WinABI = ctypes.winapi_abi;
-    }
-
-    kernel32dll = ctypes.open(libName);
-
-    /*
-    BOOL WINAPI WriteFile(
-      __in         HANDLE hFile,
-      __in         LPCVOID lpBuffer,
-      __in         DWORD nNumberOfBytesToWrite,
-      __out_opt    LPDWORD lpNumberOfBytesWritten,
-      __inout_opt  LPOVERLAPPED lpOverlapped
-    );
-
-    NOTE: lpBuffer is declared as array of unsigned int8 instead of char to avoid
-           implicit charset conversion
-    */
-    libFunc.WriteFile = kernel32dll.declare("WriteFile",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        WriteFileBuffer,
-                                        DWORD,
-                                        LPDWORD,
-                                        OVERLAPPED.ptr
-    );
-
-    /*
-    BOOL WINAPI ReadFile(
-      __in         HANDLE hFile,
-      __out        LPVOID ReadFileBuffer,
-      __in         DWORD nNumberOfBytesToRead,
-      __out_opt    LPDWORD lpNumberOfBytesRead,
-      __inout_opt  LPOVERLAPPED lpOverlapped
-    );
-    */
-    libFunc.ReadFile = kernel32dll.declare("ReadFile",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        ReadFileBuffer,
-                                        DWORD,
-                                        LPDWORD,
-                                        OVERLAPPED.ptr
-    );
-
-    /*
-    BOOL WINAPI CloseHandle(
-      __in  HANDLE hObject
-    );
-    */
-    libFunc.CloseHandle = kernel32dll.declare("CloseHandle",
-                                            WinABI,
-                                            BOOL,
-                                            HANDLE
-    );
-}
-
-
-function writePipe(pipe, data) {
-    var bytesWritten = DWORD(0);
-
-    var pData = new WriteFileBuffer();
-
-    var numChunks = Math.floor(data.length / BufferSize);
-    for (var chunk = 0; chunk <= numChunks; chunk ++) {
-        var numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
-        for (var i=0; i < numBytes; i++) {
-            pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
-        }
-
-      var r = libFunc.WriteFile(pipe, pData, numBytes, bytesWritten.address(), null);
-      if (bytesWritten.value != numBytes)
-          throw("error: wrote "+bytesWritten.value+" instead of "+numBytes+" bytes");
-    }
-    postMessage("wrote "+data.length+" bytes of data");
-}
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
-           break;
-
-        bytes.push(data[i]);
-    }
-
-    return bytes;
-}
-
-function readPipe(pipe, charset) {
-    while (true) {
-        var bytesRead = DWORD(0);
-        var line = new ReadFileBuffer();
-        var r = libFunc.ReadFile(pipe, line, BufferSize, bytesRead.address(), null);
-
-        if (!r) {
-            // stop if we get an error (such as EOF reached)
-            postMessage({msg: "info", data: "ReadFile failed"});
-            break;
-        }
-
-        if (bytesRead.value > 0) {
-            var c = readString(line, bytesRead.value, charset);
-            postMessage({msg: "data", data: c, count: c.length});
-        }
-        else {
-            break;
-        }
-    }
-    libFunc.CloseHandle(pipe);
-    postMessage({msg: "done"});
-    kernel32dll.close();
-    close();
-}
-
-onmessage = function (event) {
-    let pipePtr;
-    switch (event.data.msg) {
-    case "init":
-        initLib(event.data.libc);
-        break;
-    case "write":
-        // data contents:
-        //   msg: 'write'
-        //   data: the data (string) to write
-        //   pipe: ptr to pipe
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        writePipe(pipePtr.contents, event.data.data);
-        postMessage("WriteOK");
-        break;
-    case "read":
-        initLib(event.data.libc);
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        readPipe(pipePtr.contents, event.data.charset);
-        break;
-    case "close":
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        postMessage("closing stdin\n");
-
-        if (libFunc.CloseHandle(pipePtr.contents)) {
-            postMessage("ClosedOK");
-        }
-        else
-            postMessage("Could not close stdin handle");
-        break;
-    case "stop":
-        kernel32dll.close();
-        close();
-        break;
-    default:
-        throw("error: Unknown command"+event.data.msg+"\n");
-    }
-    return;
-};
--- a/addon-sdk/source/test/addons/child_process/index.js
+++ b/addon-sdk/source/test/addons/child_process/index.js
@@ -17,17 +17,17 @@ const app = require("sdk/system/xul-app"
 
 // Once Bug 903018 is resolved, just move the application testing to
 // module.metadata.engines
 if (app.is("Firefox")) {
   exports["test child_process in an addon"] = (assert, done) => {
     exec(isWindows ? "DIR /A-D" : "ls -al", {
       cwd: PROFILE_DIR
     }, (err, stdout, stderr) => {
-      assert.ok(!err, "no errors");
+      assert.equal(err, null, "no errors");
       assert.equal(stderr, "", "stderr is empty");
       assert.ok(/extensions\.ini/.test(stdout), "stdout output of `ls -al` finds files");
 
       if (isWindows)
         assert.ok(!/<DIR>/.test(stdout), "passing args works");
       else
         assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), "passing args works");
       done();