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
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.
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"
47 #include "nsILocalFileMac.h"
49 #include "nsNetUtil.h"
50 #include "nsIAppStartup.h"
51 #include "nsIServiceManager.h"
53 #include "nsIIOService.h"
55 #include "nsIServiceManager.h"
57 #include "nsIDOMWindow.h"
59 #include "nsISupportsPrimitives.h"
60 #include "nsIWindowWatcher.h"
62 #include "nsReadableUtils.h"
63 #include "nsIObserverService.h"
64 #include "nsIPrefService.h"
66 #include "nsAEEventHandling.h"
67 #include "nsXPFEComponentsCID.h"
74 // the static instance
75 nsMacCommandLine
nsMacCommandLine::sMacCommandLine
;
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
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;
94 while (charsRead
< (bufSize
-1)) {
96 if (c
== EOF
|| c
== '\n' || c
== '\r')
100 buf
[charsRead
] = '\0';
102 return (c
== EOF
&& !charsRead
) ? -1 : charsRead
;
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
),
119 kEventDurationNoWait
,
121 &carbonEvent
) == noErr
) {
122 EventRecord eventRecord
;
123 ::ConvertEventRefToEventRecord(carbonEvent
, &eventRecord
);
124 ::AEProcessAppleEvent(&eventRecord
);
125 ::ReleaseEvent(carbonEvent
);
132 //----------------------------------------------------------------------------------------
133 nsMacCommandLine::nsMacCommandLine()
137 , mStartedUp(PR_FALSE
)
138 //----------------------------------------------------------------------------------------
143 //----------------------------------------------------------------------------------------
144 nsMacCommandLine::~nsMacCommandLine()
145 //----------------------------------------------------------------------------------------
147 ShutdownAEHandlerClasses();
149 for (PRUint32 i
= 0; i
< mArgsUsed
; i
++)
156 //----------------------------------------------------------------------------------------
157 nsresult
nsMacCommandLine::Initialize(int& argc
, char**& argv
)
158 //----------------------------------------------------------------------------------------
160 mArgs
= static_cast<char **>(malloc(kArgsGrowSize
* sizeof(char *)));
162 return NS_ERROR_FAILURE
;
164 mArgsAllocated
= kArgsGrowSize
;
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
190 ProcessAppleEvents();
192 // we've started up now
193 mStartedUp
= PR_TRUE
;
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
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
&&
228 // The process is currently in the foreground. The relaunched
229 // process should come to the front, too.
230 AddToCommandLine("-foreground");
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 *)));
245 return NS_ERROR_OUT_OF_MEMORY
;
247 mArgsAllocated
+= kArgsGrowSize
;
249 char *temp2
= strdup(inArgText
);
251 return NS_ERROR_OUT_OF_MEMORY
;
252 mArgs
[mArgsUsed
++] = temp2
;
253 mArgs
[mArgsUsed
] = nsnull
;
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.
265 if (::FSpMakeFSRef(&inFileSpec
, &fsRef
) != noErr
)
266 return NS_ERROR_FAILURE
;
268 CFURLRef url
= ::CFURLCreateFromFSRef(nsnull
, &fsRef
);
270 return NS_ERROR_FAILURE
;
272 CFStringRef string
= ::CFURLGetString(url
);
275 return NS_ERROR_FAILURE
;
278 CFIndex length
= ::CFStringGetLength(string
);
280 ::CFStringGetBytes(string
, CFRangeMake(0, length
), kCFStringEncodingUTF8
,
281 0, PR_FALSE
, nsnull
, 0, &bufLen
);
283 UInt8 buffer
[bufLen
+ 1];
286 return NS_ERROR_FAILURE
;
289 ::CFStringGetBytes(string
, CFRangeMake(0, length
), kCFStringEncodingUTF8
,
290 0, PR_FALSE
, buffer
, bufLen
, nsnull
);
295 AddToCommandLine(inOptionString
);
296 AddToCommandLine((char*)buffer
);
301 //----------------------------------------------------------------------------------------
302 nsresult
nsMacCommandLine::AddToEnvironmentVars(const char* inArgText
)
303 //----------------------------------------------------------------------------------------
305 (void)PR_SetEnv(inArgText
);
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
));
317 return errAEEventNotHandled
;
321 // Is it the right type to be a command-line file?
322 if (inFileType
== 'TEXT' || inFileType
== 'CMDL')
324 // Can we open the file?
326 rv
= inFile
->OpenANSIFileDesc("r", &fp
);
327 if (NS_SUCCEEDED(rv
))
329 Boolean foundArgs
= false;
330 Boolean foundEnv
= false;
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);
342 else if (PL_strstr(chars
, kEnvVarLinePrefix
) == chars
)
344 (void)AddToEnvironmentVars(chars
+ sizeof(kEnvVarLinePrefix
) - 1);
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
)
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
);
367 return errAEEventNotHandled
;
369 return OpenURL(specBuf
.get());
372 OSErr
nsMacCommandLine::OpenURL(const char* aURL
)
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
));
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());
389 return errAEEventNotHandled
;
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.
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
;
427 rv
= wwatch
->OpenWindow(0, chrome
, "_blank",
428 "chrome,dialog=no,all", urlWrapper
,
429 getter_AddRefs(newWindow
));
434 //----------------------------------------------------------------------------------------
435 OSErr
nsMacCommandLine::DispatchURLToNewBrowser(const char* url
)
436 //----------------------------------------------------------------------------------------
438 OSErr err
= errAEEventNotHandled
;
442 err
= AddToCommandLine("-url");
444 err
= AddToCommandLine(url
);
450 //----------------------------------------------------------------------------------------
451 OSErr
nsMacCommandLine::Quit(TAskSave askSave
)
452 //----------------------------------------------------------------------------------------
456 nsCOMPtr
<nsIObserverService
> obsServ
=
457 do_GetService("@mozilla.org/observer-service;1", &rv
);
459 return errAEEventNotHandled
;
461 nsCOMPtr
<nsISupportsPRBool
> cancelQuit
=
462 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
, &rv
);
464 return errAEEventNotHandled
;
466 cancelQuit
->SetData(PR_FALSE
);
467 if (askSave
!= eSaveNo
) {
468 rv
= obsServ
->NotifyObservers(cancelQuit
, "quit-application-requested", nsnull
);
470 return errAEEventNotHandled
;
474 cancelQuit
->GetData(&abortQuit
);
476 return userCanceledErr
;
478 nsCOMPtr
<nsIAppStartup
> appStartup
=
479 do_GetService(NS_APPSTARTUP_CONTRACTID
, &rv
);
481 return errAEEventNotHandled
;
483 appStartup
->Quit(nsIAppStartup::eAttemptQuit
);
489 //----------------------------------------------------------------------------------------
490 void SetupMacCommandLine(int& argc
, char**& argv
)
491 //----------------------------------------------------------------------------------------
493 nsMacCommandLine
& cmdLine
= nsMacCommandLine::GetMacCommandLine();
494 return cmdLine
.SetupCommandLine(argc
, argv
);