|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 */ |
|
5 |
|
6 /* |
|
7 * ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr |
|
8 * on separate threads. |
|
9 * |
|
10 */ |
|
11 |
|
12 // Being a ChromeWorker object, implicitly uses the following: |
|
13 // Components.utils.import("resource://gre/modules/ctypes.jsm"); |
|
14 |
|
15 'use strict'; |
|
16 |
|
17 const BufferSize = 1024; |
|
18 |
|
19 const BOOL = ctypes.bool; |
|
20 const HANDLE = ctypes.size_t; |
|
21 const DWORD = ctypes.uint32_t; |
|
22 const LPDWORD = DWORD.ptr; |
|
23 const PVOID = ctypes.voidptr_t; |
|
24 const LPVOID = PVOID; |
|
25 |
|
26 /* |
|
27 typedef struct _OVERLAPPED { |
|
28 ULONG_PTR Internal; |
|
29 ULONG_PTR InternalHigh; |
|
30 union { |
|
31 struct { |
|
32 DWORD Offset; |
|
33 DWORD OffsetHigh; |
|
34 }; |
|
35 PVOID Pointer; |
|
36 }; |
|
37 HANDLE hEvent; |
|
38 } OVERLAPPED, *LPOVERLAPPED; |
|
39 */ |
|
40 const OVERLAPPED = new ctypes.StructType("OVERLAPPED"); |
|
41 |
|
42 var ReadFileBuffer = ctypes.char.array(BufferSize); |
|
43 var WriteFileBuffer = ctypes.uint8_t.array(BufferSize); |
|
44 |
|
45 var kernel32dll = null; |
|
46 var libFunc = {}; |
|
47 |
|
48 function initLib(libName) { |
|
49 if (ctypes.size_t.size == 8) { |
|
50 var WinABI = ctypes.default_abi; |
|
51 } else { |
|
52 var WinABI = ctypes.winapi_abi; |
|
53 } |
|
54 |
|
55 kernel32dll = ctypes.open(libName); |
|
56 |
|
57 /* |
|
58 BOOL WINAPI WriteFile( |
|
59 __in HANDLE hFile, |
|
60 __in LPCVOID lpBuffer, |
|
61 __in DWORD nNumberOfBytesToWrite, |
|
62 __out_opt LPDWORD lpNumberOfBytesWritten, |
|
63 __inout_opt LPOVERLAPPED lpOverlapped |
|
64 ); |
|
65 |
|
66 NOTE: lpBuffer is declared as array of unsigned int8 instead of char to avoid |
|
67 implicit charset conversion |
|
68 */ |
|
69 libFunc.WriteFile = kernel32dll.declare("WriteFile", |
|
70 WinABI, |
|
71 BOOL, |
|
72 HANDLE, |
|
73 WriteFileBuffer, |
|
74 DWORD, |
|
75 LPDWORD, |
|
76 OVERLAPPED.ptr |
|
77 ); |
|
78 |
|
79 /* |
|
80 BOOL WINAPI ReadFile( |
|
81 __in HANDLE hFile, |
|
82 __out LPVOID ReadFileBuffer, |
|
83 __in DWORD nNumberOfBytesToRead, |
|
84 __out_opt LPDWORD lpNumberOfBytesRead, |
|
85 __inout_opt LPOVERLAPPED lpOverlapped |
|
86 ); |
|
87 */ |
|
88 libFunc.ReadFile = kernel32dll.declare("ReadFile", |
|
89 WinABI, |
|
90 BOOL, |
|
91 HANDLE, |
|
92 ReadFileBuffer, |
|
93 DWORD, |
|
94 LPDWORD, |
|
95 OVERLAPPED.ptr |
|
96 ); |
|
97 |
|
98 /* |
|
99 BOOL WINAPI CloseHandle( |
|
100 __in HANDLE hObject |
|
101 ); |
|
102 */ |
|
103 libFunc.CloseHandle = kernel32dll.declare("CloseHandle", |
|
104 WinABI, |
|
105 BOOL, |
|
106 HANDLE |
|
107 ); |
|
108 } |
|
109 |
|
110 |
|
111 function writePipe(pipe, data) { |
|
112 var bytesWritten = DWORD(0); |
|
113 |
|
114 var pData = new WriteFileBuffer(); |
|
115 |
|
116 var numChunks = Math.floor(data.length / BufferSize); |
|
117 for (var chunk = 0; chunk <= numChunks; chunk ++) { |
|
118 var numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize; |
|
119 for (var i=0; i < numBytes; i++) { |
|
120 pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256; |
|
121 } |
|
122 |
|
123 var r = libFunc.WriteFile(pipe, pData, numBytes, bytesWritten.address(), null); |
|
124 if (bytesWritten.value != numBytes) |
|
125 throw("error: wrote "+bytesWritten.value+" instead of "+numBytes+" bytes"); |
|
126 } |
|
127 postMessage("wrote "+data.length+" bytes of data"); |
|
128 } |
|
129 |
|
130 function readString(data, length, charset) { |
|
131 var string = '', bytes = []; |
|
132 for(var i = 0;i < length; i++) { |
|
133 if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data |
|
134 break; |
|
135 |
|
136 bytes.push(data[i]); |
|
137 } |
|
138 |
|
139 return bytes; |
|
140 } |
|
141 |
|
142 function readPipe(pipe, charset) { |
|
143 while (true) { |
|
144 var bytesRead = DWORD(0); |
|
145 var line = new ReadFileBuffer(); |
|
146 var r = libFunc.ReadFile(pipe, line, BufferSize, bytesRead.address(), null); |
|
147 |
|
148 if (!r) { |
|
149 // stop if we get an error (such as EOF reached) |
|
150 postMessage({msg: "info", data: "ReadFile failed"}); |
|
151 break; |
|
152 } |
|
153 |
|
154 if (bytesRead.value > 0) { |
|
155 var c = readString(line, bytesRead.value, charset); |
|
156 postMessage({msg: "data", data: c, count: c.length}); |
|
157 } |
|
158 else { |
|
159 break; |
|
160 } |
|
161 } |
|
162 libFunc.CloseHandle(pipe); |
|
163 postMessage({msg: "done"}); |
|
164 kernel32dll.close(); |
|
165 close(); |
|
166 } |
|
167 |
|
168 onmessage = function (event) { |
|
169 let pipePtr; |
|
170 switch (event.data.msg) { |
|
171 case "init": |
|
172 initLib(event.data.libc); |
|
173 break; |
|
174 case "write": |
|
175 // data contents: |
|
176 // msg: 'write' |
|
177 // data: the data (string) to write |
|
178 // pipe: ptr to pipe |
|
179 pipePtr = HANDLE.ptr(event.data.pipe); |
|
180 writePipe(pipePtr.contents, event.data.data); |
|
181 postMessage("WriteOK"); |
|
182 break; |
|
183 case "read": |
|
184 initLib(event.data.libc); |
|
185 pipePtr = HANDLE.ptr(event.data.pipe); |
|
186 readPipe(pipePtr.contents, event.data.charset); |
|
187 break; |
|
188 case "close": |
|
189 pipePtr = HANDLE.ptr(event.data.pipe); |
|
190 postMessage("closing stdin\n"); |
|
191 |
|
192 if (libFunc.CloseHandle(pipePtr.contents)) { |
|
193 postMessage("ClosedOK"); |
|
194 } |
|
195 else |
|
196 postMessage("Could not close stdin handle"); |
|
197 break; |
|
198 case "stop": |
|
199 kernel32dll.close(); |
|
200 close(); |
|
201 break; |
|
202 default: |
|
203 throw("error: Unknown command"+event.data.msg+"\n"); |
|
204 } |
|
205 return; |
|
206 }; |