Bug 448909 - Need more controls WHATWG Video tag. r=mconnor, r=bz
[wine-gecko.git] / xpcom / threads / nsProcessCommon.cpp
blob9448cc68d06bbb98b35f9a417b470e590f07407e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Don Bragg <dbragg@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /*****************************************************************************
42 * nsProcess is used to execute new processes and specify if you want to
43 * wait (blocking) or continue (non-blocking).
45 *****************************************************************************
48 #include "nsCOMPtr.h"
49 #include "nsMemory.h"
50 #include "nsProcess.h"
51 #include "prtypes.h"
52 #include "prio.h"
53 #include "prenv.h"
54 #include "nsCRT.h"
56 #include <stdlib.h>
58 #if defined(XP_WIN)
59 #include "prmem.h"
60 #include "nsString.h"
61 #include "nsLiteralString.h"
62 #include "nsReadableUtils.h"
63 #include <windows.h>
64 #endif
66 #if defined(XP_MACOSX)
67 #include <Processes.h>
68 #include "nsILocalFileMac.h"
69 #endif
71 //-------------------------------------------------------------------//
72 // nsIProcess implementation
73 //-------------------------------------------------------------------//
74 NS_IMPL_ISUPPORTS1(nsProcess, nsIProcess)
76 //Constructor
77 nsProcess::nsProcess()
78 : mExitValue(-1),
79 mProcess(nsnull)
83 NS_IMETHODIMP
84 nsProcess::Init(nsIFile* executable)
86 NS_ENSURE_ARG_POINTER(executable);
87 PRBool isFile;
89 //First make sure the file exists
90 nsresult rv = executable->IsFile(&isFile);
91 if (NS_FAILED(rv)) return rv;
92 if (!isFile)
93 return NS_ERROR_FAILURE;
95 //Store the nsIFile in mExecutable
96 mExecutable = executable;
97 //Get the path because it is needed by the NSPR process creation
98 #ifdef XP_WIN
99 rv = mExecutable->GetNativeTarget(mTargetPath);
100 if (NS_FAILED(rv) || mTargetPath.IsEmpty() )
101 #endif
102 rv = mExecutable->GetNativePath(mTargetPath);
104 return rv;
108 #if defined(XP_WIN)
109 static int assembleCmdLine(char *const *argv, char **cmdLine)
111 char *const *arg;
112 char *p, *q;
113 int cmdLineSize;
114 int numBackslashes;
115 int i;
116 int argNeedQuotes;
119 * Find out how large the command line buffer should be.
121 cmdLineSize = 0;
122 for (arg = argv; *arg; arg++) {
124 * \ and " need to be escaped by a \. In the worst case,
125 * every character is a \ or ", so the string of length
126 * may double. If we quote an argument, that needs two ".
127 * Finally, we need a space between arguments, and
128 * a null byte at the end of command line.
130 cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
131 + 2 /* we quote every argument */
132 + 1; /* space in between, or final null */
134 p = *cmdLine = (char *) PR_MALLOC(cmdLineSize);
135 if (p == NULL) {
136 return -1;
139 for (arg = argv; *arg; arg++) {
140 /* Add a space to separates the arguments */
141 if (arg != argv) {
142 *p++ = ' ';
144 q = *arg;
145 numBackslashes = 0;
146 argNeedQuotes = 0;
148 /* If the argument contains white space, it needs to be quoted. */
149 if (strpbrk(*arg, " \f\n\r\t\v")) {
150 argNeedQuotes = 1;
153 if (argNeedQuotes) {
154 *p++ = '"';
156 while (*q) {
157 if (*q == '\\') {
158 numBackslashes++;
159 q++;
160 } else if (*q == '"') {
161 if (numBackslashes) {
163 * Double the backslashes since they are followed
164 * by a quote
166 for (i = 0; i < 2 * numBackslashes; i++) {
167 *p++ = '\\';
169 numBackslashes = 0;
171 /* To escape the quote */
172 *p++ = '\\';
173 *p++ = *q++;
174 } else {
175 if (numBackslashes) {
177 * Backslashes are not followed by a quote, so
178 * don't need to double the backslashes.
180 for (i = 0; i < numBackslashes; i++) {
181 *p++ = '\\';
183 numBackslashes = 0;
185 *p++ = *q++;
189 /* Now we are at the end of this argument */
190 if (numBackslashes) {
192 * Double the backslashes if we have a quote string
193 * delimiter at the end.
195 if (argNeedQuotes) {
196 numBackslashes *= 2;
198 for (i = 0; i < numBackslashes; i++) {
199 *p++ = '\\';
202 if (argNeedQuotes) {
203 *p++ = '"';
207 *p = '\0';
208 return 0;
210 #endif
212 // XXXldb |args| has the wrong const-ness
213 NS_IMETHODIMP
214 nsProcess::Run(PRBool blocking, const char **args, PRUint32 count,
215 PRUint32 *pid)
217 NS_ENSURE_TRUE(mExecutable, NS_ERROR_NOT_INITIALIZED);
218 PRStatus status = PR_SUCCESS;
220 // make sure that when we allocate we have 1 greater than the
221 // count since we need to null terminate the list for the argv to
222 // pass into PR_CreateProcess
223 char **my_argv = NULL;
224 my_argv = (char **)nsMemory::Alloc(sizeof(char *) * (count + 2) );
225 if (!my_argv) {
226 return NS_ERROR_OUT_OF_MEMORY;
229 // copy the args
230 PRUint32 i;
231 for (i=0; i < count; i++) {
232 my_argv[i+1] = const_cast<char*>(args[i]);
234 // we need to set argv[0] to the program name.
235 my_argv[0] = mTargetPath.BeginWriting();
236 // null terminate the array
237 my_argv[count+1] = NULL;
239 #if defined(XP_WIN) && !defined (WINCE) /* wince uses nspr */
240 STARTUPINFO startupInfo;
241 PROCESS_INFORMATION procInfo;
242 BOOL retVal;
243 char *cmdLine;
245 if (assembleCmdLine(my_argv, &cmdLine) == -1) {
246 nsMemory::Free(my_argv);
247 return NS_ERROR_FILE_EXECUTION_FAILED;
250 ZeroMemory(&startupInfo, sizeof(startupInfo));
251 startupInfo.cb = sizeof(startupInfo);
253 /* The CREATE_NO_WINDOW flag is important to prevent console
254 * windows from appearing. This makes behavior the same on all
255 * platforms. This won't work on win9x, however. The flag will
256 * not have any effect on non-console applications.
259 retVal = CreateProcess(NULL,
260 // const_cast<char*>(mTargetPath.get()),
261 cmdLine,
262 NULL, /* security attributes for the new
263 * process */
264 NULL, /* security attributes for the primary
265 * thread in the new process */
266 FALSE, /* inherit handles */
267 CREATE_NO_WINDOW, /* creation flags */
268 NULL, /* env */
269 NULL, /* current drive and directory */
270 &startupInfo,
271 &procInfo
273 PR_Free( cmdLine );
274 if (blocking) {
276 // if success, wait for process termination. the early returns and such
277 // are a bit ugly but preserving the logic of the nspr code I copied to
278 // minimize our risk abit.
280 if ( retVal == TRUE ) {
281 DWORD dwRetVal;
282 unsigned long exitCode;
284 dwRetVal = WaitForSingleObject(procInfo.hProcess, INFINITE);
285 if (dwRetVal == WAIT_FAILED) {
286 nsMemory::Free(my_argv);
287 return NS_ERROR_FAILURE;
289 if (GetExitCodeProcess(procInfo.hProcess, &exitCode) == FALSE) {
290 mExitValue = exitCode;
291 nsMemory::Free(my_argv);
292 return NS_ERROR_FAILURE;
294 mExitValue = exitCode;
295 CloseHandle(procInfo.hProcess);
296 CloseHandle(procInfo.hThread);
298 else
299 status = PR_FAILURE;
301 else {
303 // map return value into success code
305 if (retVal == TRUE)
306 status = PR_SUCCESS;
307 else
308 status = PR_FAILURE;
311 #else // Note, this must not be an #elif ...!
313 #if defined(XP_MACOSX)
314 if (count == 0) {
315 FSSpec resolvedSpec;
316 OSErr err = noErr;
318 nsCOMPtr<nsILocalFileMac> macExecutable =
319 do_QueryInterface(mExecutable);
320 macExecutable->GetFSSpec(&resolvedSpec);
322 LaunchParamBlockRec launchPB;
323 launchPB.launchAppSpec = &resolvedSpec;
324 launchPB.launchAppParameters = NULL;
325 launchPB.launchBlockID = extendedBlock;
326 launchPB.launchEPBLength = extendedBlockLen;
327 launchPB.launchFileFlags = NULL;
328 launchPB.launchControlFlags =
329 launchContinue + launchNoFileFlags + launchUseMinimum;
330 if (!blocking)
331 launchPB.launchControlFlags += launchDontSwitch;
333 err = LaunchApplication(&launchPB);
335 // NOTE: blocking mode assumes you are running on a thread
336 // other than the UI thread that has the main event loop
337 if (blocking && err == noErr) {
338 while (1) {
339 ProcessInfoRec info;
340 info.processInfoLength = sizeof(ProcessInfoRec);
341 info.processName = NULL;
342 info.processAppSpec = NULL;
344 err = GetProcessInformation(&launchPB.launchProcessSN, &info);
346 if (err != noErr) {
347 // The process is no longer in the process
348 // manager's internal list, assume the process is
349 // done.
350 err = noErr;
352 break;
355 // still running so sleep some more (200 msecs)
356 PR_Sleep(200);
360 if (err != noErr) {
361 status = PR_FAILURE;
364 if (blocking) {
365 mExitValue = err;
367 } else
368 #endif
370 if (blocking) {
371 mProcess = PR_CreateProcess(mTargetPath.get(), my_argv, NULL,
372 NULL);
373 if (mProcess)
374 status = PR_WaitProcess(mProcess, &mExitValue);
375 } else {
376 status = PR_CreateProcessDetached(mTargetPath.get(), my_argv, NULL,
377 NULL);
380 #endif
382 // free up our argv
383 nsMemory::Free(my_argv);
385 if (status != PR_SUCCESS)
386 return NS_ERROR_FILE_EXECUTION_FAILED;
388 return NS_OK;
391 NS_IMETHODIMP nsProcess::InitWithPid(PRUint32 pid)
393 return NS_ERROR_NOT_IMPLEMENTED;
396 NS_IMETHODIMP
397 nsProcess::GetLocation(nsIFile** aLocation)
399 return NS_ERROR_NOT_IMPLEMENTED;
402 NS_IMETHODIMP
403 nsProcess::GetPid(PRUint32 *aPid)
405 return NS_ERROR_NOT_IMPLEMENTED;
408 NS_IMETHODIMP
409 nsProcess::GetProcessName(char** aProcessName)
411 return NS_ERROR_NOT_IMPLEMENTED;
414 NS_IMETHODIMP
415 nsProcess::GetProcessSignature(PRUint32 *aProcessSignature)
417 return NS_ERROR_NOT_IMPLEMENTED;
420 NS_IMETHODIMP
421 nsProcess::Kill()
423 nsresult rv = NS_OK;
424 if (mProcess)
425 rv = PR_KillProcess(mProcess) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
427 return rv;
430 NS_IMETHODIMP
431 nsProcess::GetExitValue(PRInt32 *aExitValue)
433 *aExitValue = mExitValue;
435 return NS_OK;