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 189778 1e85f3f0b525ef04f18ed4e2caff5f1efb71f24f
parent 189777 48c8b39f410f261338ee030168ed25f7a8bba8d2
child 189779 93443ede815261bba923b8b91e9c1dbe6fc86ccd
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvingtetun, gps, paul
bugs944451
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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