Bug 944451 - Land simulator addon into mozilla-central. r=vingtetun, r=gps, r=paul
authorAlexandre Poirot <poirot.alex@gmail.com>
Mon, 03 Mar 2014 14:50:40 -0500
changeset 182559 1e85f3f0b525ef04f18ed4e2caff5f1efb71f24f
parent 182558 48c8b39f410f261338ee030168ed25f7a8bba8d2
child 182560 93443ede815261bba923b8b91e9c1dbe6fc86ccd
push id5439
push userffxbld
push dateMon, 17 Mar 2014 23:08:15 +0000
treeherdermozilla-aurora@c0befb3c8038 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvingtetun, gps, paul
bugs944451
milestone30.0a1
Bug 944451 - Land simulator addon into mozilla-central. r=vingtetun, r=gps, r=paul
b2g/build.mk
b2g/simulator/build_xpi.py
b2g/simulator/custom-prefs.js
b2g/simulator/custom-settings.json
b2g/simulator/icon.png
b2g/simulator/icon64.png
b2g/simulator/lib/main.js
b2g/simulator/lib/simulator-process.js
b2g/simulator/package-overload.json.in
b2g/simulator/package.json
b2g/simulator/packages/subprocess/README.md
b2g/simulator/packages/subprocess/lib/subprocess.js
b2g/simulator/packages/subprocess/lib/subprocess_worker_unix.js
b2g/simulator/packages/subprocess/lib/subprocess_worker_win.js
b2g/simulator/packages/subprocess/package.json
b2g/simulator/packages/subprocess/tests/test-subprocess.js
configure.in
--- a/b2g/build.mk
+++ b/b2g/build.mk
@@ -1,17 +1,22 @@
 # 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/.
 
+include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
+
 installer: 
 	@$(MAKE) -C b2g/installer installer
 
 package:
 	@$(MAKE) -C b2g/installer
+ifdef FXOS_SIMULATOR
+	$(PYTHON) $(srcdir)/b2g/simulator/build_xpi.py $(MOZ_PKG_PLATFORM)
+endif
 
 install::
 	@echo 'B2G can't be installed directly.'
 	@exit 1
 
 upload::
 	@$(MAKE) -C b2g/installer upload
 
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/build_xpi.py
@@ -0,0 +1,145 @@
+# 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/.
+
+# Generate xpi for the simulator addon by:
+# - building a special gaia profile for it, as we need:
+#     * more languages, and,
+#     * less apps
+#   than b2g desktop's one
+# - retrieve usefull app version metadata from the build system
+# - finally, use addon sdk's cfx tool to build the addon xpi
+#   that ships:
+#     * a small firefox addon registering to the app manager
+#     * b2g desktop runtime
+#     * gaia profile
+
+import sys, os, re, subprocess
+from mozbuild.preprocessor import Preprocessor
+from mozbuild.base import MozbuildObject
+from mozbuild.util import ensureParentDir
+from zipfile import ZipFile
+from distutils.version import LooseVersion
+
+ftp_root_path = "/pub/mozilla.org/labs/fxos-simulator"
+UPDATE_LINK = "https://ftp.mozilla.org" + ftp_root_path + "/%(update_path)s/%(xpi_name)s"
+UPDATE_URL = "https://ftp.mozilla.org" + ftp_root_path + "/%(update_path)s/update.rdf"
+XPI_NAME = "fxos-simulator-%(version)s-%(platform)s.xpi"
+
+class GaiaBuilder(object):
+    def __init__(self, build, gaia_path):
+        self.build = build
+        self.gaia_path = gaia_path
+
+    def clean(self):
+        self.build._run_make(target="clean", directory=self.gaia_path)
+
+    def profile(self, env):
+        self.build._run_make(target="profile", directory=self.gaia_path, num_jobs=1, silent=False, append_env=env)
+
+    def override_prefs(self, srcfile):
+        # Note that each time we call `make profile` in gaia, a fresh new pref file is created
+        # cat srcfile >> profile/user.js
+        with open(os.path.join(self.gaia_path, "profile", "user.js"), "a") as userJs:
+            userJs.write(open(srcfile).read())
+
+def process_package_overload(src, dst, version, app_buildid):
+    ensureParentDir(dst)
+    # First replace numeric version like '1.3'
+    # Then replace with 'slashed' version like '1_4'
+    # Finally set the full length addon version like 1.3.20131230
+    defines = {
+        "NUM_VERSION": version,
+        "SLASH_VERSION": version.replace(".", "_"),
+        "FULL_VERSION": ("%s.%s" % (version, app_buildid))
+    }
+    pp = Preprocessor(defines=defines)
+    pp.do_filter("substitution")
+    with open(dst, "w") as output:
+        with open(src, "r") as input:
+            pp.processFile(input=input, output=output)
+
+def add_dir_to_zip(zip, top, pathInZip, blacklist=()):
+    zf = ZipFile(zip, "a")
+    for dirpath, subdirs, files in os.walk(top):
+        dir_relpath = os.path.relpath(dirpath, top)
+        if dir_relpath.startswith(blacklist):
+            continue
+        zf.write(dirpath, os.path.join(pathInZip, dir_relpath))
+        for filename in files:
+            relpath = os.path.join(dir_relpath, filename)
+            if relpath in blacklist:
+                continue
+            zf.write(os.path.join(dirpath, filename),
+                     os.path.join(pathInZip, relpath))
+    zf.close()
+
+def main(platform):
+    build = MozbuildObject.from_environment()
+    topsrcdir = build.topsrcdir
+    distdir = build.distdir
+
+    srcdir = os.path.join(topsrcdir, "b2g", "simulator")
+
+    app_buildid = open(os.path.join(build.topobjdir, "config", "buildid")).read().strip()
+
+    # The simulator uses a shorter version string,
+    # it only keeps the major version digits A.B
+    # whereas MOZ_B2G_VERSION is A.B.C.D
+    b2g_version = build.config_environment.defines["MOZ_B2G_VERSION"].replace('"', '')
+    version = ".".join(str(n) for n in LooseVersion(b2g_version).version[0:2])
+
+    # Build a gaia profile specific to the simulator in order to:
+    # - disable the FTU
+    # - set custom prefs to enable devtools debugger server
+    # - set custom settings to disable lockscreen and screen timeout
+    # - only ship production apps
+    gaia_path = build.config_environment.substs["GAIADIR"]
+    builder = GaiaBuilder(build, gaia_path)
+    builder.clean()
+    env = {
+      "NOFTU": "1",
+      "GAIA_APP_TARGET": "production",
+      "SETTINGS_PATH": os.path.join(srcdir, "custom-settings.json")
+    }
+    builder.profile(env)
+    builder.override_prefs(os.path.join(srcdir, "custom-prefs.js"))
+
+    # Substitute version strings in the package manifest overload file
+    manifest_overload = os.path.join(build.topobjdir, "b2g", "simulator", "package-overload.json")
+    process_package_overload(os.path.join(srcdir, "package-overload.json.in"),
+                             manifest_overload,
+                             version,
+                             app_buildid)
+
+    # Build the simulator addon xpi
+    xpi_name = XPI_NAME % {"version": version, "platform": platform}
+    xpi_path = os.path.join(distdir, xpi_name)
+
+    update_path = "%s/%s" % (version, platform)
+    update_link = UPDATE_LINK % {"update_path": update_path, "xpi_name": xpi_name}
+    update_url = UPDATE_URL % {"update_path": update_path}
+    subprocess.check_call([
+      build.virtualenv_manager.python_path, os.path.join(topsrcdir, "addon-sdk", "source", "bin", "cfx"), "xpi", \
+      "--pkgdir", srcdir, \
+      "--manifest-overload", manifest_overload, \
+      "--strip-sdk", \
+      "--update-link", update_link, \
+      "--update-url", update_url, \
+      "--static-args", "{\"label\": \"Firefox OS %s\"}" % version, \
+      "--output-file", xpi_path \
+    ])
+
+    # Ship b2g-desktop, but prevent its gaia profile to be shipped in the xpi
+    add_dir_to_zip(xpi_path, os.path.join(distdir, "b2g"), "b2g", ("gaia"))
+    # Then ship our own gaia profile
+    add_dir_to_zip(xpi_path, os.path.join(gaia_path, "profile"), "profile")
+
+if __name__ == '__main__':
+    if 2 != len(sys.argv):
+        print("""Usage:
+  python {0} MOZ_PKG_PLATFORM
+""".format(sys.argv[0]))
+        sys.exit(1)
+    main(*sys.argv[1:])
+
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/custom-prefs.js
@@ -0,0 +1,2 @@
+user_pref("devtools.debugger.prompt-connection", false);
+user_pref("devtools.debugger.forbid-certified-apps", false);
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/custom-settings.json
@@ -0,0 +1,6 @@
+{
+  "debugger.remote-mode": "adb-devtools",
+  "screen.timeout": 0,
+  "lockscreen.enabled": false,
+  "lockscreen.locked": false
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c4307fc8418436bb6b2fd3a6afc702c2db28aa77
GIT binary patch
literal 4762
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..275cd04a5eae3f727c1272cb71d3090dbcecd732
GIT binary patch
literal 7858
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
Q`2YX_07*qoM6N<$g0DUvVgLXD
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/lib/main.js
@@ -0,0 +1,52 @@
+/* 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/.
+ */
+
+const { Cc, Ci, Cu } = require("chrome");
+
+const { SimulatorProcess } = require("./simulator-process");
+const Promise = require("sdk/core/promise");
+const Self = require("sdk/self");
+const System = require("sdk/system");
+const { Simulator } = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
+
+let process;
+
+function launch({ port }) {
+  // Close already opened simulation
+  if (process) {
+    return close().then(launch.bind(null, { port: port }));
+  }
+
+  process = SimulatorProcess();
+  process.remoteDebuggerPort = port;
+  process.run();
+
+  return Promise.resolve();
+}
+
+function close() {
+  if (!process) {
+    return Promise.resolve();
+  }
+  let p = process;
+  process = null;
+  return p.kill();
+}
+
+
+// Load data generated at build time that
+// expose various information about the runtime we ship
+let appinfo = System.staticArgs;
+
+Simulator.register(appinfo.label, {
+  appinfo: appinfo,
+  launch: launch,
+  close: close
+});
+
+require("sdk/system/unload").when(function () {
+  Simulator.unregister(appinfo.label);
+  close();
+});
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/lib/simulator-process.js
@@ -0,0 +1,182 @@
+/* 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");
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const { EventTarget } = require("sdk/event/target");
+const { emit, off } = require("sdk/event/core");
+const { Class } = require("sdk/core/heritage");
+const Environment = require("sdk/system/environment").env;
+const Runtime = require("sdk/system/runtime");
+const Self = require("sdk/self");
+const URL = require("sdk/url");
+const Subprocess = require("subprocess");
+const Promise = require("sdk/core/promise");
+
+const { rootURI: ROOT_URI } = require('@loader/options');
+const PROFILE_URL = ROOT_URI + "profile/";
+const BIN_URL = ROOT_URI + "b2g/";
+
+// Log subprocess error and debug messages to the console.  This logs messages
+// for all consumers of the API.  We trim the messages because they sometimes
+// have trailing newlines.  And note that registerLogHandler actually registers
+// an error handler, despite its name.
+Subprocess.registerLogHandler(
+  function(s) console.error("subprocess: " + s.trim())
+);
+Subprocess.registerDebugHandler(
+  function(s) console.debug("subprocess: " + s.trim())
+);
+
+exports.SimulatorProcess = Class({
+  extends: EventTarget,
+  initialize: function initialize(options) {
+    EventTarget.prototype.initialize.call(this, options);
+
+    this.on("stdout", function onStdout(data) console.log(data.trim()));
+    this.on("stderr", function onStderr(data) console.error(data.trim()));
+  },
+
+  // check if b2g is running
+  get isRunning() !!this.process,
+
+  /**
+   * Start the process and connect the debugger client.
+   */
+  run: function() {
+    // kill before start if already running
+    if (this.process != null) {
+      this.process
+          .kill()
+          .then(this.run.bind(this));
+      return;
+    }
+
+    // resolve b2g binaries path (raise exception if not found)
+    let b2gExecutable = this.b2gExecutable;
+
+    this.once("stdout", function () {
+      if (Runtime.OS == "Darwin") {
+          console.debug("WORKAROUND run osascript to show b2g-desktop window"+
+                        " on Runtime.OS=='Darwin'");
+        // Escape double quotes and escape characters for use in AppleScript.
+        let path = b2gExecutable.path
+          .replace(/\\/g, "\\\\").replace(/\"/g, '\\"');
+
+        Subprocess.call({
+          command: "/usr/bin/osascript",
+          arguments: ["-e", 'tell application "' + path + '" to activate'],
+        });
+      }
+    });
+
+    let environment;
+    if (Runtime.OS == "Linux") {
+      environment = ["TMPDIR=" + Services.dirsvc.get("TmpD",Ci.nsIFile).path];
+      if ("DISPLAY" in Environment) {
+        environment.push("DISPLAY=" + Environment.DISPLAY);
+      }
+    }
+
+    // spawn a b2g instance
+    this.process = Subprocess.call({
+      command: b2gExecutable,
+      arguments: this.b2gArguments,
+      environment: environment,
+
+      // emit stdout event
+      stdout: (function(data) {
+        emit(this, "stdout", data);
+      }).bind(this),
+
+      // emit stderr event
+      stderr: (function(data) {
+        emit(this, "stderr", data);
+      }).bind(this),
+
+      // on b2g instance exit, reset tracked process, remoteDebuggerPort and
+      // shuttingDown flag, then finally emit an exit event
+      done: (function(result) {
+        console.log(this.b2gFilename + " terminated with " + result.exitCode);
+        this.process = null;
+        emit(this, "exit", result.exitCode);
+      }).bind(this)
+    });
+  },
+
+  // request a b2g instance kill
+  kill: function() {
+    let deferred = Promise.defer();
+    if (this.process) {
+      this.once("exit", (exitCode) => {
+        this.shuttingDown = false;
+        deferred.resolve(exitCode);
+      });
+      if (!this.shuttingDown) {
+        this.shuttingDown = true;
+        emit(this, "kill", null);
+        this.process.kill();
+      }
+      return deferred.promise;
+    } else {
+      return Promise.resolve(undefined);
+    }
+  },
+
+  // compute current b2g filename
+  get b2gFilename() {
+    return this._executable ? this._executableFilename : "B2G";
+  },
+
+  // compute current b2g file handle
+  get b2gExecutable() {
+    if (this._executable) return this._executable;
+
+    let bin = URL.toFilename(BIN_URL);
+    let executables = {
+      WINNT: "b2g-bin.exe",
+      Darwin: "Contents/MacOS/b2g-bin",
+      Linux: "b2g-bin",
+    };
+
+    console.log("bin url: "+bin+"/"+executables[Runtime.OS]);
+    let path = bin + "/" + executables[Runtime.OS];
+
+    let executable = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    executable.initWithPath(path);
+
+    if (!executable.exists()) {
+      // B2G binaries not found
+      throw Error("b2g-desktop Executable not found.");
+    }
+
+    this._executable = executable;
+    this._executableFilename = "b2g-bin";
+
+    return executable;
+  },
+
+  // compute b2g CLI arguments
+  get b2gArguments() {
+    let args = [];
+
+    let profile = URL.toFilename(PROFILE_URL);
+    args.push("-profile", profile);
+    Cu.reportError(profile);
+
+    // NOTE: push dbgport option on the b2g-desktop commandline
+    args.push("-dbgport", "" + this.remoteDebuggerPort);
+
+    // Ignore eventual zombie instances of b2g that are left over
+    args.push("-no-remote");
+
+    return args;
+  },
+});
+
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/package-overload.json.in
@@ -0,0 +1,7 @@
+{
+  "id": "fxos_@SLASH_VERSION@_simulator@mozilla.org",
+  "name": "fxos_@SLASH_VERSION@_simulator",
+  "version": "@FULL_VERSION@",
+  "fullName": "Firefox OS @NUM_VERSION@ Simulator",
+  "description": "a Firefox OS @NUM_VERSION@ simulator"
+}
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/package.json
@@ -0,0 +1,37 @@
+{
+  "id": "fxos_simulator@mozilla.org",
+  "name": "fxos_simulator",
+  "version": "1.0.dev",
+  "fullName": "Firefox OS Simulator",
+  "label": "Firefox OS",
+  "description": "a Firefox OS simulator",
+  "author": "Myk Melez (https://github.com/mykmelez)",
+  "contributors": [
+    "Alexandre Poirot (https://github.com/ochameau)",
+    "Anant Narayanan (https://github.com/anantn)",
+    "Brandon Kase (https://github.com/bkase)",
+    "Breck Yunits (https://github.com/breck7)",
+    "César Carruitero (https://github.com/ccarruitero)",
+    "David Gomes (https://github.com/davidgomes)",
+    "Fabrice Desré (https://github.com/fabricedesre)",
+    "Fraser Tweedale (https://github.com/frasertweedale)",
+    "Harald Kirschner (https://github.com/digitarald)",
+    "Jérémie Patonnier (https://github.com/JeremiePat)",
+    "J. Ryan Stinnett (https://github.com/jryans)",
+    "Kan-Ru Chen (陳侃如) (https://github.com/kanru)",
+    "Louis Stowasser (https://github.com/louisstow)",
+    "Luca Greco (https://github.com/rpl)",
+    "Matthew Claypotch (https://github.com/potch)",
+    "Matthew Riley MacPherson (https://github.com/tofumatt)",
+    "Nick Desaulniers (https://github.com/nickdesaulniers)",
+    "Soumen Ganguly (https://github.com/SoumenG)",
+    "Sudheesh Singanamalla (https://github.com/sudheesh001)",
+    "Victor Bjelkholm (https://github.com/VictorBjelkholm)"
+  ],
+  "permissions": {
+    "private-browsing": true
+  },
+  "license": "MPL 2.0",
+  "unpack": true,
+  "dependencies": ["subprocess"]
+}
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/README.md
@@ -0,0 +1,124 @@
+<h2>What's that?</h2>
+Simply package enigmail hard work on providing IPC feature in mozilla platform.
+So we are able to launch child proccesses from javascript,
+and in our case, from addon-sdk libraries :)
+
+<h2>Sample of code:</h2>
+  This object allows to start a process, and read/write data to/from it
+  using stdin/stdout/stderr streams.
+  Usage example:
+
+   const subprocess = require("subprocess");
+   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); } );
+
+
+<h2>Credits:</h2>
+All enigmail team working on IPC component.
+  The Initial Developer of this code is Jan Gerber.
+  Portions created by Jan Gerber <j@mailb.org>,
+  Patrick Brunschwig (author of almost all code) <patrick@mozilla-enigmail.org>,
+  Ramalingam Saravanan (from enigmail team) <svn@xmlterm.org>
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/lib/subprocess.js
@@ -0,0 +1,1543 @@
+/* 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");
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+
+const NS_LOCAL_FILE = "@mozilla.org/file/local;1";
+
+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.jschar.ptr;
+const LPCWSTR = ctypes.jschar.ptr;
+const LPTSTR = ctypes.jschar.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},
+]);
+
+/*
+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},
+]);
+
+/*
+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];
+}
+
+
+var gDebugFunc = null,
+    gLogFunc = null,
+    gXulRuntime = null;
+
+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);
+};
+
+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);
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+
+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 = [];
+        }
+
+        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;
+    }
+};
+
+
+
+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
+                                         );
+
+//     /*
+//     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
+//     );
+
+    /*
+    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
+    );
+
+
+    /*
+    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 jschar
+            environment = ctypes.jschar.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");
+
+        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");
+        }
+    }
+
+
+    /*
+     * 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);
+                    }
+                }
+
+                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");
+            }
+        }
+
+        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;
+            }
+        });
+    }
+
+    /*
+     * 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'})
+
+            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);
+
+    //main
+    hChildProcess = popen(cmdStr, workDir, options.arguments, options.environment, child);
+
+    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();
+        }
+    }
+    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;
+        }
+    }
+}
+
+
+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
+
+    //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.
+    // Bug 936297 - Use OS.File internals to fetch
+    // sizeof(posix_spawn_file_actions_t)
+    var { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
+    var sizeof_file_actions_t = OS.Constants.libc.OSFILE_SIZEOF_DIRENT;
+    var posix_spawn_file_actions_t = ctypes.uint8_t.array(sizeof_file_actions_t);
+
+    //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
+    );
+
+    //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
+    );
+
+    // 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
+    );
+
+    //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);
+        }
+
+        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 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");
+        }
+    }
+
+
+    /*
+     * 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;
+            }
+        });
+    }
+
+    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);
+
+    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;
+        }
+    }
+}
+
+
+module.exports = subprocess;
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/lib/subprocess_worker_unix.js
@@ -0,0 +1,248 @@
+/* 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/.
+ */
+
+/*
+ * 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:
+// Components.utils.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();
+            libc.close();
+            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;
+};
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/lib/subprocess_worker_win.js
@@ -0,0 +1,206 @@
+/* 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/.
+ */
+
+/*
+ * ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr
+ * on separate threads.
+ *
+ */
+
+// Being a ChromeWorker object, implicitly uses the following:
+// Components.utils.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;
+};
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "subprocess",
+    "license": "MPL 1.1/GPL 2.0/LGPL 2.1",
+    "author": "Alexandre Poirot",
+    "contributors": [
+      "Jan Gerber (original creator) <j@mailb.org>",
+      "Patrick Brunschwig (author of almost all code) <patrick@mozilla-enigmail.org>",
+      "Ramalingam Saravanan (from enigmail team) <svn@xmlterm.org>"
+    ],
+    "version": "0.1.1",
+    "dependencies": [
+      "api-utils"
+    ],
+    "description": "Addon-sdk package for subprocess xpcom components from enigmail. Allow to run process, manipulate stdin/out and kill it."
+}
new file mode 100644
--- /dev/null
+++ b/b2g/simulator/packages/subprocess/tests/test-subprocess.js
@@ -0,0 +1,144 @@
+/* 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/.
+ */
+
+const { Cc, Ci } = require("chrome");
+const subprocess = require("subprocess");
+const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+
+// For now, only test on windows
+if (env.get('OS') && env.get('OS').match(/Windows/)) {
+
+exports.testWindows = function (test) {
+  test.waitUntilDone();
+  let envTestValue = "OK";
+  let gotStdout = false;
+
+  var p = subprocess.call({
+    // Retrieve windows cmd.exe path from env
+    command:     env.get('ComSpec'),
+    // In order to execute a simple "echo" function
+    arguments:   ['/C', 'echo %ENV_TEST%'], // ' & type CON' should display stdin, but doesn't work
+    // Printing an environnement variable set here by the parent process
+    environment: ['ENV_TEST='+envTestValue],
+
+    stdin: function(stdin) {
+      // Win32 command line is not really made for stdin
+      // So it doesn't seems to work as it's hard to retrieve stdin
+      stdin.write("stdin");
+      stdin.close();
+    },
+    stdout: function(data) {
+      test.assert(!gotStdout,"don't get stdout twice");
+      test.assertEqual(data,envTestValue+"\r\n","stdout contains the environment variable");
+      gotStdout = true;
+    },
+    stderr: function(data) {
+      test.fail("shouldn't get stderr");
+    },
+    done: function() {
+      test.assert(gotStdout, "got stdout before finished");
+      test.done();
+    },
+    mergeStderr: false
+  });
+
+}
+
+exports.testWindowsStderr = function (test) {
+  test.waitUntilDone();
+  let gotStderr = false;
+
+  var p = subprocess.call({
+    command:     env.get('ComSpec'),
+    arguments:   ['/C', 'nonexistent'],
+
+    stdout: function(data) {
+      test.fail("shouldn't get stdout");
+    },
+    stderr: function(data) {
+      test.assert(!gotStderr,"don't get stderr twice");
+      test.assertEqual(
+        data,
+        "'nonexistent' is not recognized as an internal or external command,\r\n" +
+        "operable program or batch file.\r\n",
+        "stderr contains the error message"
+      );
+      gotStderr = true;
+    },
+    done: function() {
+      test.assert(gotStderr, "got stderr before finished");
+      test.done();
+    },
+    mergeStderr: false
+  });
+
+}
+
+}
+
+if (env.get('USER') && env.get('SHELL')) {
+
+exports.testUnix = function (test) {
+  test.waitUntilDone();
+  let envTestValue = "OK";
+  let gotStdout = false;
+
+  var p = subprocess.call({
+    command:     '/bin/sh',
+    // Print stdin and our env variable
+    //arguments:   ['-c', 'echo $@ $ENV_TEST'],
+    environment: ['ENV_TEST='+envTestValue],
+
+    stdin: function(stdin) {
+      stdin.write("echo $ENV_TEST");
+      stdin.close();
+    },
+    stdout: function(data) {
+      test.assert(!gotStdout,"don't get stdout twice");
+      test.assertEqual(data,envTestValue+"\n","stdout contains the environment variable");
+      gotStdout = true;
+    },
+    stderr: function(data) {
+      test.fail("shouldn't get stderr");
+    },
+    done: function() {
+      test.assert(gotStdout, "got stdout before finished");
+      test.done();
+    },
+    mergeStderr: false
+  });
+}
+
+exports.testUnixStderr = function (test) {
+  test.waitUntilDone();
+  let gotStderr = false;
+
+  var p = subprocess.call({
+    // Hope that we don't have to give absolute path on linux ...
+    command:     '/bin/sh',
+    arguments:   ['nonexistent'],
+
+    stdout: function(data) {
+      test.fail("shouldn't get stdout");
+    },
+    stderr: function(data) {
+      test.assert(!gotStderr,"don't get stderr twice");
+      // There is two variant of error message
+      if (data == "/bin/sh: 0: Can't open nonexistent\n")
+        test.pass("stderr containes the expected error message");
+      else
+        test.assertEqual(data, "/bin/sh: nonexistent: No such file or directory\n",
+                         "stderr contains the error message");
+      gotStderr = true;
+    },
+    done: function() {
+      test.assert(gotStderr, "got stderr before finished");
+      test.done();
+    },
+    mergeStderr: false
+  });
+}
+
+}
--- a/configure.in
+++ b/configure.in
@@ -159,21 +159,30 @@ fi
 AC_SUBST(L10NBASEDIR)
 
 dnl Check for Perl first -- needed for win32 SDK checks
 MOZ_PATH_PROGS(PERL, $PERL perl5 perl )
 if test -z "$PERL" -o "$PERL" = ":"; then
     AC_MSG_ERROR([perl not found in \$PATH])
 fi
 
+if test -n "$GAIADIR" -a ! -d "$GAIADIR" ; then
+    AC_MSG_ERROR([GAIADIR '$GAIADIR' isn't a valid directory])
+fi
+
 AC_SUBST(GAIADIR)
 if test -n "$GAIADIR" ; then
     AC_DEFINE(PACKAGE_GAIA)
 fi
 
+if test -n "$FXOS_SIMULATOR" -a -z "$GAIADIR" ; then
+    AC_MSG_ERROR([FXOS_SIMULATOR=1 requires GAIADIR to be defined])
+fi
+AC_SUBST(FXOS_SIMULATOR)
+
 MOZ_ARG_WITH_STRING(gonk,
 [  --with-gonk=DIR
                location of gonk dir],
     gonkdir=$withval)
 
 MOZ_ARG_WITH_STRING(gonk-toolchain-prefix,
 [  --with-gonk-toolchain-prefix=DIR
                           prefix to gonk toolchain commands],
@@ -4155,16 +4164,17 @@ b2g)
   AC_DEFINE(MOZ_B2G)
   ;;
 esac
 
 AC_SUBST(MOZ_BUILD_APP)
 AC_SUBST(MOZ_PHOENIX)
 AC_SUBST(MOZ_XULRUNNER)
 AC_SUBST(MOZ_B2G)
+AC_SUBST(MOZ_B2G_VERSION)
 
 AC_DEFINE_UNQUOTED(MOZ_BUILD_APP,$MOZ_BUILD_APP)
 
 dnl ========================================================
 dnl Check android sdk version depending on mobile target
 dnl ========================================================
 
 if test -z "$gonkdir" ; then