Bug 1056136 - Get rid of SDK dependency for simulator addon. r=jryans, r=gps

This commit is contained in:
Alexandre Poirot 2014-08-28 04:58:00 -04:00
Родитель 8ed74d7d51
Коммит dc17cf4f2f
15 изменённых файлов: 200 добавлений и 2392 удалений

67
b2g/simulator/bootstrap.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,67 @@
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
// Useful piece of code from :bent
// http://mxr.mozilla.org/mozilla-central/source/dom/workers/test/extensions/bootstrap/bootstrap.js
function registerAddonResourceHandler(data) {
let file = data.installPath;
let fileuri = file.isDirectory() ?
Services.io.newFileURI(file) :
Services.io.newURI("jar:" + file.path + "!/", null, null);
let resourceName = encodeURIComponent(data.id.replace("@", "at"));
Services.io.getProtocolHandler("resource").
QueryInterface(Ci.nsIResProtocolHandler).
setSubstitution(resourceName, fileuri);
return "resource://" + resourceName + "/";
}
let mainModule;
function install(data, reason) {}
function uninstall(data, reason) {}
function startup(data, reason) {
let uri = registerAddonResourceHandler(data);
let loaderModule =
Cu.import('resource://gre/modules/commonjs/toolkit/loader.js').Loader;
let { Loader, Require, Main } = loaderModule;
const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
let loader = Loader({
paths: {
"./": uri + "lib/",
"": "resource://gre/modules/commonjs/"
},
globals: {
console: new ConsoleAPI({
prefix: data.id
})
},
modules: {
"toolkit/loader": loaderModule,
addon: {
id: data.id,
version: data.version,
uri: uri
}
}
});
let require_ = Require(loader, { id: "./addon" });
mainModule = require_("./main");
}
function shutdown(data, reason) {
if (mainModule && mainModule.shutdown) {
mainModule.shutdown();
}
}
function uninstall(data, reason) {}

Просмотреть файл

@ -18,11 +18,11 @@ import sys, os, re, subprocess
from mozbuild.preprocessor import Preprocessor
from mozbuild.base import MozbuildObject
from mozbuild.util import ensureParentDir
from mozpack.mozjar import JarWriter
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"
@ -43,17 +43,17 @@ class GaiaBuilder(object):
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):
def preprocess_file(src, dst, version, app_buildid, update_url):
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
# (reduce the app build id to only the build date
# as addon manager doesn't handle big ints in addon versions)
defines = {
"NUM_VERSION": version,
"SLASH_VERSION": version.replace(".", "_"),
"FULL_VERSION": ("%s.%s" % (version, app_buildid[:8]))
"ADDON_ID": "fxos_" + version.replace(".", "_") + "_simulator@mozilla.org",
# (reduce the app build id to only the build date
# as addon manager doesn't handle big ints in addon versions)
"ADDON_VERSION": ("%s.%s" % (version, app_buildid[:8])),
"ADDON_NAME": "Firefox OS " + version + " Simulator",
"ADDON_DESCRIPTION": "a Firefox OS " + version + " simulator",
"ADDON_UPDATE_URL": update_url
}
pp = Preprocessor(defines=defines)
pp.do_filter("substitution")
@ -61,20 +61,24 @@ def process_package_overload(src, dst, version, app_buildid):
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")
def add_dir_to_zip(jar, top, pathInZip, blacklist=()):
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()
path = os.path.normpath(os.path.join(pathInZip, relpath))
file = open(os.path.join(dirpath, filename), "rb")
mode = os.stat(os.path.join(dirpath, filename)).st_mode
jar.add(path.encode("ascii"), file, mode=mode)
def add_file_to_zip(jar, path, pathInZip):
file = open(path, "rb")
mode = os.stat(path).st_mode
jar.add(pathInZip.encode("ascii"), file, mode=mode)
def main(platform):
build = MozbuildObject.from_environment()
@ -107,38 +111,41 @@ def main(platform):
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", "B2G.app/Contents/MacOS/gaia"))
# Then ship our own gaia profile
add_dir_to_zip(xpi_path, os.path.join(gaia_path, "profile"), "profile")
# Add "defaults" directory (required by add-on runner in Firefox 31 and
# earlier)
add_dir_to_zip(xpi_path, os.path.join(srcdir, "defaults"), "defaults")
# Preprocess some files...
manifest = os.path.join(build.topobjdir, "b2g", "simulator", "install.rdf")
preprocess_file(os.path.join(srcdir, "install.rdf.in"),
manifest,
version,
app_buildid,
update_url)
options_file = os.path.join(build.topobjdir, "b2g", "simulator", "options.xul")
preprocess_file(os.path.join(srcdir, "options.xul.in"),
options_file,
version,
app_buildid,
update_url)
with JarWriter(xpi_path, optimize=False) as zip:
# Ship addon files into the .xpi
add_dir_to_zip(zip, os.path.join(srcdir, "lib"), "lib")
add_file_to_zip(zip, manifest, "install.rdf")
add_file_to_zip(zip, os.path.join(srcdir, "bootstrap.js"), "bootstrap.js")
add_file_to_zip(zip, options_file, "options.xul")
add_file_to_zip(zip, os.path.join(srcdir, "icon.png"), "icon.png")
add_file_to_zip(zip, os.path.join(srcdir, "icon64.png"), "icon64.png")
# Ship b2g-desktop, but prevent its gaia profile to be shipped in the xpi
add_dir_to_zip(zip, os.path.join(distdir, "b2g"), "b2g", ("gaia", "B2G.app/Contents/MacOS/gaia"))
# Then ship our own gaia profile
add_dir_to_zip(zip, os.path.join(gaia_path, "profile"), "profile")
if __name__ == '__main__':
if 2 != len(sys.argv):

Просмотреть файл

Просмотреть файл

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>@ADDON_ID@</em:id>
<em:version>@ADDON_VERSION@</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>true</em:unpack>
<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>26.0</em:minVersion>
<em:maxVersion>30.0</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>@ADDON_NAME@</em:name>
<em:description>@ADDON_DESCRIPTION@</em:description>
<em:creator>Myk Melez (https://github.com/mykmelez)</em:creator>
<em:optionsType>2</em:optionsType>
<em:updateURL>@ADDON_UPDATE_URL@</em:updateURL>
<em:contributor>Alexandre Poirot (https://github.com/ochameau)</em:contributor>
<em:contributor>Anant Narayanan (https://github.com/anantn)</em:contributor>
<em:contributor>Brandon Kase (https://github.com/bkase)</em:contributor>
<em:contributor>Breck Yunits (https://github.com/breck7)</em:contributor>
<em:contributor>César Carruitero (https://github.com/ccarruitero)</em:contributor>
<em:contributor>David Gomes (https://github.com/davidgomes)</em:contributor>
<em:contributor>Fabrice Desré (https://github.com/fabricedesre)</em:contributor>
<em:contributor>Fraser Tweedale (https://github.com/frasertweedale)</em:contributor>
<em:contributor>Harald Kirschner (https://github.com/digitarald)</em:contributor>
<em:contributor>Jérémie Patonnier (https://github.com/JeremiePat)</em:contributor>
<em:contributor>J. Ryan Stinnett (https://github.com/jryans)</em:contributor>
<em:contributor>Kan-Ru Chen (陳侃如) (https://github.com/kanru)</em:contributor>
<em:contributor>Louis Stowasser (https://github.com/louisstow)</em:contributor>
<em:contributor>Luca Greco (https://github.com/rpl)</em:contributor>
<em:contributor>Matthew Claypotch (https://github.com/potch)</em:contributor>
<em:contributor>Matthew Riley MacPherson (https://github.com/tofumatt)</em:contributor>
<em:contributor>Nick Desaulniers (https://github.com/nickdesaulniers)</em:contributor>
<em:contributor>Soumen Ganguly (https://github.com/SoumenG)</em:contributor>
<em:contributor>Sudheesh Singanamalla (https://github.com/sudheesh001)</em:contributor>
<em:contributor>Victor Bjelkholm (https://github.com/VictorBjelkholm)</em:contributor>
</Description>
</RDF>

Просмотреть файл

@ -7,9 +7,8 @@ const { Cc, Ci, Cu } = require("chrome");
const { SimulatorProcess } = require("./simulator-process");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
const Self = require("sdk/self");
const System = require("sdk/system");
const { Simulator } = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
let process;
@ -35,18 +34,20 @@ function close() {
return p.kill();
}
let appinfo = {};
// Load data generated at build time that
// expose various information about the runtime we ship
let appinfo = System.staticArgs;
AddonManager.getAddonByID(require("addon").id, function (addon) {
appinfo.label = addon.name.replace(" Simulator", "");
Simulator.register(appinfo.label, {
appinfo: appinfo,
launch: launch,
close: close
Simulator.register(appinfo.label, {
appinfo: appinfo,
launch: launch,
close: close
});
});
require("sdk/system/unload").when(function () {
exports.shutdown = function () {
Simulator.unregister(appinfo.label);
close();
});
}

Просмотреть файл

@ -14,13 +14,11 @@ 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 Subprocess = require("sdk/system/child_process/subprocess");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
const Prefs = require("sdk/simple-prefs").prefs;
const { rootURI: ROOT_URI } = require('@loader/options');
const ROOT_URI = require("addon").uri;
const PROFILE_URL = ROOT_URI + "profile/";
const BIN_URL = ROOT_URI + "b2g/";
@ -140,9 +138,14 @@ exports.SimulatorProcess = Class({
if (this._executable) {
return this._executable;
}
let customRuntime;
try {
let pref = "extensions." + require("addon").id + ".customRuntime";
customRuntime = Services.prefs.getComplexValue(pref, Ci.nsIFile);
} catch(e) {}
if (Prefs.customRuntime) {
this._executable = Prefs.customRuntime;
if (customRuntime) {
this._executable = customRuntime;
this._executableFilename = "Custom runtime";
return this._executable;
}
@ -177,7 +180,13 @@ exports.SimulatorProcess = Class({
get b2gArguments() {
let args = [];
let profile = Prefs.gaiaProfile || URL.toFilename(PROFILE_URL);
let gaiaProfile;
try {
let pref = "extensions." + require("addon").id + ".gaiaProfile";
gaiaProfile = Services.prefs.getComplexValue(pref, Ci.nsIFile).path;
} catch(e) {}
let profile = gaiaProfile || URL.toFilename(PROFILE_URL);
args.push("-profile", profile);
console.log("profile", profile);

Просмотреть файл

@ -0,0 +1,5 @@
<?xml version="1.0" ?>
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<setting pref="extensions.@ADDON_ID@.gaiaProfile" type="directory" title="Select a custom Gaia profile directory"/>
<setting pref="extensions.@ADDON_ID@.customRuntime" type="file" title="Select a custom runtime executable"/>
</vbox>

Просмотреть файл

@ -1,7 +0,0 @@
{
"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"
}

Просмотреть файл

@ -1,46 +0,0 @@
{
"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
},
"preferences": [{
"type": "directory",
"name": "gaiaProfile",
"title": "Select a custom Gaia profile directory"
}, {
"type": "file",
"name": "customRuntime",
"title": "Select a custom runtime executable"
}],
"license": "MPL 2.0",
"unpack": true,
"dependencies": ["subprocess"]
}

Просмотреть файл

@ -1,124 +0,0 @@
<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>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,248 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* 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;
};

Просмотреть файл

@ -1,206 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* 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;
};

Просмотреть файл

@ -1,15 +0,0 @@
{
"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."
}

Просмотреть файл

@ -1,144 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
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
});
}
}