Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / toolkit / xre / nsCommandLineServiceMac.cpp
blobadef0a91fd19c8dd7e8dc0551e45ff276f777dbc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Simon Fraser <sfraser@netscape.com>
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Mark Mentovai <mark@moxienet.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 // Special stuff for the Macintosh implementation of command-line service.
43 #include "nsCommandLineServiceMac.h"
45 // Mozilla
46 #include "nsDebug.h"
47 #include "nsILocalFileMac.h"
48 #include "nsDebug.h"
49 #include "nsNetUtil.h"
50 #include "nsIAppStartup.h"
51 #include "nsIServiceManager.h"
52 #include "nsIURL.h"
53 #include "nsIIOService.h"
54 #include "nsIURL.h"
55 #include "nsIServiceManager.h"
56 #include "nsNetCID.h"
57 #include "nsIDOMWindow.h"
58 #include "nsXPCOM.h"
59 #include "nsISupportsPrimitives.h"
60 #include "nsIWindowWatcher.h"
61 #include "jsapi.h"
62 #include "nsReadableUtils.h"
63 #include "nsIObserverService.h"
64 #include "nsIPrefService.h"
66 #include "nsAEEventHandling.h"
67 #include "nsXPFEComponentsCID.h"
69 // NSPR
70 #include "prmem.h"
71 #include "plstr.h"
72 #include "prenv.h"
74 // the static instance
75 nsMacCommandLine nsMacCommandLine::sMacCommandLine;
78 * ReadLine --
80 * Read in a line of text, terminated by CR or LF, from inStream into buf.
81 * The terminating CR or LF is not included. The text in buf is terminated
82 * by a null byte.
83 * Returns the number of bytes in buf. If EOF and zero bytes were read, returns -1.
86 static PRInt32 ReadLine(FILE* inStream, char* buf, PRInt32 bufSize)
88 PRInt32 charsRead = 0;
89 int c;
91 if (bufSize < 2)
92 return -1;
94 while (charsRead < (bufSize-1)) {
95 c = getc(inStream);
96 if (c == EOF || c == '\n' || c == '\r')
97 break;
98 buf[charsRead++] = c;
100 buf[charsRead] = '\0';
102 return (c == EOF && !charsRead) ? -1 : charsRead;
105 static PRUint32
106 ProcessAppleEvents()
108 // Dispatch all of the Apple Events waiting in the event queue.
110 PRUint32 processed = 0;
112 const EventTypeSpec kAppleEventList[] = {
113 { kEventClassAppleEvent, kEventAppleEvent },
116 EventRef carbonEvent;
117 while (::ReceiveNextEvent(GetEventTypeCount(kAppleEventList),
118 kAppleEventList,
119 kEventDurationNoWait,
120 PR_TRUE,
121 &carbonEvent) == noErr) {
122 EventRecord eventRecord;
123 ::ConvertEventRefToEventRecord(carbonEvent, &eventRecord);
124 ::AEProcessAppleEvent(&eventRecord);
125 ::ReleaseEvent(carbonEvent);
126 processed++;
129 return processed;
132 //----------------------------------------------------------------------------------------
133 nsMacCommandLine::nsMacCommandLine()
134 : mArgs(NULL)
135 , mArgsAllocated(0)
136 , mArgsUsed(0)
137 , mStartedUp(PR_FALSE)
138 //----------------------------------------------------------------------------------------
143 //----------------------------------------------------------------------------------------
144 nsMacCommandLine::~nsMacCommandLine()
145 //----------------------------------------------------------------------------------------
147 ShutdownAEHandlerClasses();
148 if (mArgs) {
149 for (PRUint32 i = 0; i < mArgsUsed; i++)
150 free(mArgs[i]);
151 free(mArgs);
156 //----------------------------------------------------------------------------------------
157 nsresult nsMacCommandLine::Initialize(int& argc, char**& argv)
158 //----------------------------------------------------------------------------------------
160 mArgs = static_cast<char **>(malloc(kArgsGrowSize * sizeof(char *)));
161 if (!mArgs)
162 return NS_ERROR_FAILURE;
163 mArgs[0] = nsnull;
164 mArgsAllocated = kArgsGrowSize;
165 mArgsUsed = 0;
167 // Here, we may actually get useful args.
168 // Copy them first to mArgv.
169 for (int arg = 0; arg < argc; arg++) {
170 char* flag = argv[arg];
171 // don't pass on the psn (Process Serial Number) flag from the OS
172 if (strncmp(flag, "-psn_", 5) != 0)
173 AddToCommandLine(flag);
176 // Set up AppleEvent handling.
177 OSErr err = CreateAEHandlerClasses(false);
178 if (err != noErr) return NS_ERROR_FAILURE;
180 // Snarf all the odoc and pdoc apple-events.
182 // 1. If they are odoc for 'CMDL' documents, read them into the buffer ready for
183 // parsing (concatenating multiple files).
185 // 2. If they are any other kind of document, convert them into -url command-line
186 // parameters or -print parameters, with file URLs.
188 // Spin a native event loop to allow AE handlers for waiting events to be
189 // called
190 ProcessAppleEvents();
192 // we've started up now
193 mStartedUp = PR_TRUE;
195 argc = mArgsUsed;
196 argv = mArgs;
198 return NS_OK;
201 //----------------------------------------------------------------------------------------
202 void nsMacCommandLine::SetupCommandLine(int& argc, char**& argv)
203 //----------------------------------------------------------------------------------------
205 // Initializes the command line from Apple Events and other sources,
206 // as appropriate for OS X.
208 // IMPORTANT: This must be done before XPCOM shutdown if the app is to
209 // relaunch (i.e. before the ScopedXPCOMStartup object goes out of scope).
210 // XPCOM shutdown can cause other things to process native events, and
211 // native event processing can cause the waiting Apple Events to be
212 // discarded.
214 // Process Apple Events and put them into the arguments.
215 Initialize(argc, argv);
217 Boolean isForeground = PR_FALSE;
218 ProcessSerialNumber psnSelf, psnFront;
220 // If the process will be relaunched, the child should be in the foreground
221 // if the parent is in the foreground. This will be communicated in a
222 // command-line argument to the child. Adding this argument is harmless
223 // if not relaunching.
224 if (::GetCurrentProcess(&psnSelf) == noErr &&
225 ::GetFrontProcess(&psnFront) == noErr &&
226 ::SameProcess(&psnSelf, &psnFront, &isForeground) == noErr &&
227 isForeground) {
228 // The process is currently in the foreground. The relaunched
229 // process should come to the front, too.
230 AddToCommandLine("-foreground");
233 argc = mArgsUsed;
234 argv = mArgs;
237 //----------------------------------------------------------------------------------------
238 nsresult nsMacCommandLine::AddToCommandLine(const char* inArgText)
239 //----------------------------------------------------------------------------------------
241 if (mArgsUsed >= mArgsAllocated - 1) {
242 // realloc does not free the given pointer if allocation fails.
243 char **temp = static_cast<char **>(realloc(mArgs, (mArgsAllocated + kArgsGrowSize) * sizeof(char *)));
244 if (!temp)
245 return NS_ERROR_OUT_OF_MEMORY;
246 mArgs = temp;
247 mArgsAllocated += kArgsGrowSize;
249 char *temp2 = strdup(inArgText);
250 if (!temp2)
251 return NS_ERROR_OUT_OF_MEMORY;
252 mArgs[mArgsUsed++] = temp2;
253 mArgs[mArgsUsed] = nsnull;
254 return NS_OK;
258 //----------------------------------------------------------------------------------------
259 nsresult nsMacCommandLine::AddToCommandLine(const char* inOptionString, const FSSpec& inFileSpec)
260 //----------------------------------------------------------------------------------------
262 // Convert the filespec to a URL. Avoid using xpcom because this may be
263 // called before xpcom startup.
264 FSRef fsRef;
265 if (::FSpMakeFSRef(&inFileSpec, &fsRef) != noErr)
266 return NS_ERROR_FAILURE;
268 CFURLRef url = ::CFURLCreateFromFSRef(nsnull, &fsRef);
269 if (!url)
270 return NS_ERROR_FAILURE;
272 CFStringRef string = ::CFURLGetString(url);
273 if (!string) {
274 ::CFRelease(url);
275 return NS_ERROR_FAILURE;
278 CFIndex length = ::CFStringGetLength(string);
279 CFIndex bufLen = 0;
280 ::CFStringGetBytes(string, CFRangeMake(0, length), kCFStringEncodingUTF8,
281 0, PR_FALSE, nsnull, 0, &bufLen);
283 UInt8 buffer[bufLen + 1];
284 if (!buffer) {
285 ::CFRelease(url);
286 return NS_ERROR_FAILURE;
289 ::CFStringGetBytes(string, CFRangeMake(0, length), kCFStringEncodingUTF8,
290 0, PR_FALSE, buffer, bufLen, nsnull);
291 buffer[bufLen] = 0;
293 ::CFRelease(url);
295 AddToCommandLine(inOptionString);
296 AddToCommandLine((char*)buffer);
298 return NS_OK;
301 //----------------------------------------------------------------------------------------
302 nsresult nsMacCommandLine::AddToEnvironmentVars(const char* inArgText)
303 //----------------------------------------------------------------------------------------
305 (void)PR_SetEnv(inArgText);
306 return NS_OK;
310 //----------------------------------------------------------------------------------------
311 OSErr nsMacCommandLine::HandleOpenOneDoc(const FSSpec& inFileSpec, OSType inFileType)
312 //----------------------------------------------------------------------------------------
314 nsCOMPtr<nsILocalFileMac> inFile;
315 nsresult rv = NS_NewLocalFileWithFSSpec(&inFileSpec, PR_TRUE, getter_AddRefs(inFile));
316 if (NS_FAILED(rv))
317 return errAEEventNotHandled;
319 if (!mStartedUp)
321 // Is it the right type to be a command-line file?
322 if (inFileType == 'TEXT' || inFileType == 'CMDL')
324 // Can we open the file?
325 FILE *fp = 0;
326 rv = inFile->OpenANSIFileDesc("r", &fp);
327 if (NS_SUCCEEDED(rv))
329 Boolean foundArgs = false;
330 Boolean foundEnv = false;
331 char chars[1024];
332 static const char kCommandLinePrefix[] = "ARGS:";
333 static const char kEnvVarLinePrefix[] = "ENV:";
335 while (ReadLine(fp, chars, sizeof(chars)) != -1)
336 { // See if there are any command line or environment var settings
337 if (PL_strstr(chars, kCommandLinePrefix) == chars)
339 (void)AddToCommandLine(chars + sizeof(kCommandLinePrefix) - 1);
340 foundArgs = true;
342 else if (PL_strstr(chars, kEnvVarLinePrefix) == chars)
344 (void)AddToEnvironmentVars(chars + sizeof(kEnvVarLinePrefix) - 1);
345 foundEnv = true;
349 fclose(fp);
350 // If we found a command line or environment vars we want to return now
351 // raather than trying to open the file as a URL
352 if (foundArgs || foundEnv)
353 return noErr;
356 // If it's not a command-line argument, and we are starting up the application,
357 // add a command-line "-url" argument to the global list. This means that if
358 // the app is opened with documents on the mac, they'll be handled the same
359 // way as if they had been typed on the command line in Unix or DOS.
360 return AddToCommandLine("-url", inFileSpec);
363 // Final case: we're not just starting up. How do we handle this?
364 nsCAutoString specBuf;
365 rv = NS_GetURLSpecFromFile(inFile, specBuf);
366 if (NS_FAILED(rv))
367 return errAEEventNotHandled;
369 return OpenURL(specBuf.get());
372 OSErr nsMacCommandLine::OpenURL(const char* aURL)
374 nsresult rv;
376 nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
378 nsXPIDLCString browserURL;
379 if (NS_SUCCEEDED(rv))
380 rv = prefBranch->GetCharPref("browser.chromeURL", getter_Copies(browserURL));
382 if (NS_FAILED(rv)) {
383 NS_WARNING("browser.chromeURL not supplied! How is the app supposed to know what the main window is?");
384 browserURL.Assign("chrome://navigator/content/navigator.xul");
387 rv = OpenWindow(browserURL.get(), NS_ConvertASCIItoUTF16(aURL).get());
388 if (NS_FAILED(rv))
389 return errAEEventNotHandled;
391 return noErr;
396 //----------------------------------------------------------------------------------------
397 OSErr nsMacCommandLine::HandlePrintOneDoc(const FSSpec& inFileSpec, OSType fileType)
398 //----------------------------------------------------------------------------------------
400 // If we are starting up the application,
401 // add a command-line "-print" argument to the global list. This means that if
402 // the app is opened with documents on the mac, they'll be handled the same
403 // way as if they had been typed on the command line in Unix or DOS.
404 if (!mStartedUp)
405 return AddToCommandLine("-print", inFileSpec);
407 // Final case: we're not just starting up. How do we handle this?
408 NS_NOTYETIMPLEMENTED("Write Me");
409 return errAEEventNotHandled;
414 //----------------------------------------------------------------------------------------
415 nsresult nsMacCommandLine::OpenWindow(const char *chrome, const PRUnichar *url)
416 //----------------------------------------------------------------------------------------
418 nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
419 nsCOMPtr<nsISupportsString> urlWrapper(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
420 if (!wwatch || !urlWrapper)
421 return NS_ERROR_FAILURE;
423 urlWrapper->SetData(nsDependentString(url));
425 nsCOMPtr<nsIDOMWindow> newWindow;
426 nsresult rv;
427 rv = wwatch->OpenWindow(0, chrome, "_blank",
428 "chrome,dialog=no,all", urlWrapper,
429 getter_AddRefs(newWindow));
431 return rv;
434 //----------------------------------------------------------------------------------------
435 OSErr nsMacCommandLine::DispatchURLToNewBrowser(const char* url)
436 //----------------------------------------------------------------------------------------
438 OSErr err = errAEEventNotHandled;
439 if (mStartedUp)
440 return OpenURL(url);
441 else {
442 err = AddToCommandLine("-url");
443 if (err == noErr)
444 err = AddToCommandLine(url);
447 return err;
450 //----------------------------------------------------------------------------------------
451 OSErr nsMacCommandLine::Quit(TAskSave askSave)
452 //----------------------------------------------------------------------------------------
454 nsresult rv;
456 nsCOMPtr<nsIObserverService> obsServ =
457 do_GetService("@mozilla.org/observer-service;1", &rv);
458 if (NS_FAILED(rv))
459 return errAEEventNotHandled;
461 nsCOMPtr<nsISupportsPRBool> cancelQuit =
462 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv);
463 if (NS_FAILED(rv))
464 return errAEEventNotHandled;
466 cancelQuit->SetData(PR_FALSE);
467 if (askSave != eSaveNo) {
468 rv = obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nsnull);
469 if (NS_FAILED(rv))
470 return errAEEventNotHandled;
473 PRBool abortQuit;
474 cancelQuit->GetData(&abortQuit);
475 if (abortQuit)
476 return userCanceledErr;
478 nsCOMPtr<nsIAppStartup> appStartup =
479 do_GetService(NS_APPSTARTUP_CONTRACTID, &rv);
480 if (NS_FAILED(rv))
481 return errAEEventNotHandled;
483 appStartup->Quit(nsIAppStartup::eAttemptQuit);
484 return noErr;
487 #pragma mark -
489 //----------------------------------------------------------------------------------------
490 void SetupMacCommandLine(int& argc, char**& argv)
491 //----------------------------------------------------------------------------------------
493 nsMacCommandLine& cmdLine = nsMacCommandLine::GetMacCommandLine();
494 return cmdLine.SetupCommandLine(argc, argv);