michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: michael@0: /* michael@0: * ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr michael@0: * on separate threads. michael@0: * michael@0: */ michael@0: michael@0: // Being a ChromeWorker object, implicitly uses the following: michael@0: // Components.utils.import("resource://gre/modules/ctypes.jsm"); michael@0: michael@0: 'use strict'; michael@0: michael@0: const BufferSize = 1024; michael@0: michael@0: const BOOL = ctypes.bool; michael@0: const HANDLE = ctypes.size_t; michael@0: const DWORD = ctypes.uint32_t; michael@0: const LPDWORD = DWORD.ptr; michael@0: const PVOID = ctypes.voidptr_t; michael@0: const LPVOID = PVOID; michael@0: michael@0: /* michael@0: typedef struct _OVERLAPPED { michael@0: ULONG_PTR Internal; michael@0: ULONG_PTR InternalHigh; michael@0: union { michael@0: struct { michael@0: DWORD Offset; michael@0: DWORD OffsetHigh; michael@0: }; michael@0: PVOID Pointer; michael@0: }; michael@0: HANDLE hEvent; michael@0: } OVERLAPPED, *LPOVERLAPPED; michael@0: */ michael@0: const OVERLAPPED = new ctypes.StructType("OVERLAPPED"); michael@0: michael@0: var ReadFileBuffer = ctypes.char.array(BufferSize); michael@0: var WriteFileBuffer = ctypes.uint8_t.array(BufferSize); michael@0: michael@0: var kernel32dll = null; michael@0: var libFunc = {}; michael@0: michael@0: function initLib(libName) { michael@0: if (ctypes.size_t.size == 8) { michael@0: var WinABI = ctypes.default_abi; michael@0: } else { michael@0: var WinABI = ctypes.winapi_abi; michael@0: } michael@0: michael@0: kernel32dll = ctypes.open(libName); michael@0: michael@0: /* michael@0: BOOL WINAPI WriteFile( michael@0: __in HANDLE hFile, michael@0: __in LPCVOID lpBuffer, michael@0: __in DWORD nNumberOfBytesToWrite, michael@0: __out_opt LPDWORD lpNumberOfBytesWritten, michael@0: __inout_opt LPOVERLAPPED lpOverlapped michael@0: ); michael@0: michael@0: NOTE: lpBuffer is declared as array of unsigned int8 instead of char to avoid michael@0: implicit charset conversion michael@0: */ michael@0: libFunc.WriteFile = kernel32dll.declare("WriteFile", michael@0: WinABI, michael@0: BOOL, michael@0: HANDLE, michael@0: WriteFileBuffer, michael@0: DWORD, michael@0: LPDWORD, michael@0: OVERLAPPED.ptr michael@0: ); michael@0: michael@0: /* michael@0: BOOL WINAPI ReadFile( michael@0: __in HANDLE hFile, michael@0: __out LPVOID ReadFileBuffer, michael@0: __in DWORD nNumberOfBytesToRead, michael@0: __out_opt LPDWORD lpNumberOfBytesRead, michael@0: __inout_opt LPOVERLAPPED lpOverlapped michael@0: ); michael@0: */ michael@0: libFunc.ReadFile = kernel32dll.declare("ReadFile", michael@0: WinABI, michael@0: BOOL, michael@0: HANDLE, michael@0: ReadFileBuffer, michael@0: DWORD, michael@0: LPDWORD, michael@0: OVERLAPPED.ptr michael@0: ); michael@0: michael@0: /* michael@0: BOOL WINAPI CloseHandle( michael@0: __in HANDLE hObject michael@0: ); michael@0: */ michael@0: libFunc.CloseHandle = kernel32dll.declare("CloseHandle", michael@0: WinABI, michael@0: BOOL, michael@0: HANDLE michael@0: ); michael@0: } michael@0: michael@0: michael@0: function writePipe(pipe, data) { michael@0: var bytesWritten = DWORD(0); michael@0: michael@0: var pData = new WriteFileBuffer(); michael@0: michael@0: var numChunks = Math.floor(data.length / BufferSize); michael@0: for (var chunk = 0; chunk <= numChunks; chunk ++) { michael@0: var numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize; michael@0: for (var i=0; i < numBytes; i++) { michael@0: pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256; michael@0: } michael@0: michael@0: var r = libFunc.WriteFile(pipe, pData, numBytes, bytesWritten.address(), null); michael@0: if (bytesWritten.value != numBytes) michael@0: throw("error: wrote "+bytesWritten.value+" instead of "+numBytes+" bytes"); michael@0: } michael@0: postMessage("wrote "+data.length+" bytes of data"); michael@0: } michael@0: michael@0: function readString(data, length, charset) { michael@0: var string = '', bytes = []; michael@0: for(var i = 0;i < length; i++) { michael@0: if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data michael@0: break; michael@0: michael@0: bytes.push(data[i]); michael@0: } michael@0: michael@0: return bytes; michael@0: } michael@0: michael@0: function readPipe(pipe, charset) { michael@0: while (true) { michael@0: var bytesRead = DWORD(0); michael@0: var line = new ReadFileBuffer(); michael@0: var r = libFunc.ReadFile(pipe, line, BufferSize, bytesRead.address(), null); michael@0: michael@0: if (!r) { michael@0: // stop if we get an error (such as EOF reached) michael@0: postMessage({msg: "info", data: "ReadFile failed"}); michael@0: break; michael@0: } michael@0: michael@0: if (bytesRead.value > 0) { michael@0: var c = readString(line, bytesRead.value, charset); michael@0: postMessage({msg: "data", data: c, count: c.length}); michael@0: } michael@0: else { michael@0: break; michael@0: } michael@0: } michael@0: libFunc.CloseHandle(pipe); michael@0: postMessage({msg: "done"}); michael@0: kernel32dll.close(); michael@0: close(); michael@0: } michael@0: michael@0: onmessage = function (event) { michael@0: let pipePtr; michael@0: switch (event.data.msg) { michael@0: case "init": michael@0: initLib(event.data.libc); michael@0: break; michael@0: case "write": michael@0: // data contents: michael@0: // msg: 'write' michael@0: // data: the data (string) to write michael@0: // pipe: ptr to pipe michael@0: pipePtr = HANDLE.ptr(event.data.pipe); michael@0: writePipe(pipePtr.contents, event.data.data); michael@0: postMessage("WriteOK"); michael@0: break; michael@0: case "read": michael@0: initLib(event.data.libc); michael@0: pipePtr = HANDLE.ptr(event.data.pipe); michael@0: readPipe(pipePtr.contents, event.data.charset); michael@0: break; michael@0: case "close": michael@0: pipePtr = HANDLE.ptr(event.data.pipe); michael@0: postMessage("closing stdin\n"); michael@0: michael@0: if (libFunc.CloseHandle(pipePtr.contents)) { michael@0: postMessage("ClosedOK"); michael@0: } michael@0: else michael@0: postMessage("Could not close stdin handle"); michael@0: break; michael@0: case "stop": michael@0: kernel32dll.close(); michael@0: close(); michael@0: break; michael@0: default: michael@0: throw("error: Unknown command"+event.data.msg+"\n"); michael@0: } michael@0: return; michael@0: };