1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Mozilla XULRunner.
16 * The Initial Developer of the Original Code is
17 * Benjamin Smedberg <benjamin@smedbergs.us>
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsXPCOMGlue.h"
39 #include "nsINIParser.h"
41 #include "nsXPCOMPrivate.h" // for XP MAXPATHLEN
42 #include "nsMemory.h" // for NS_ARRAY_LENGTH
43 #include "nsXULAppAPI.h"
44 #include "nsILocalFile.h"
51 #define snprintf _snprintf
52 #define vsnprintf _vsnprintf
53 #define strcasecmp _stricmp
54 #define PATH_SEPARATOR_CHAR '\\'
56 #elif defined(XP_MACOSX)
59 #include <CoreFoundation/CoreFoundation.h>
60 #define PATH_SEPARATOR_CHAR '/'
61 #elif defined (XP_OS2)
64 #define INCL_DOSERRORS
67 #include <sys/types.h>
69 #define PATH_SEPARATOR_CHAR '\\'
72 #include <sys/types.h>
74 #define PATH_SEPARATOR_CHAR '/'
78 #include "nsWindowsWMain.cpp"
86 #define VERSION_MAXLEN 128
88 static void Output(PRBool isError
, const char *fmt
, ... )
93 #if (defined(XP_WIN) && !MOZ_WINCONSOLE) || defined(WINCE)
96 vsnprintf(msg
, sizeof(msg
), fmt
, ap
);
100 flags
|= MB_ICONERROR
;
102 flags
|= MB_ICONINFORMATION
;
104 wchar_t wide_msg
[1024];
105 MultiByteToWideChar(CP_ACP
,
110 sizeof(wide_msg
) / sizeof(wchar_t));
112 MessageBoxW(NULL
, wide_msg
, L
"XULRunner", flags
);
114 vfprintf(stderr
, fmt
, ap
);
121 * Return true if |arg| matches the given argument name.
123 static PRBool
IsArg(const char* arg
, const char* s
)
129 return !strcasecmp(arg
, s
);
132 #if defined(XP_WIN) || defined(XP_OS2)
134 return !strcasecmp(++arg
, s
);
141 * Return true if |aDir| is a valid file/directory.
143 static PRBool
FolderExists(const char* aDir
)
146 wchar_t wideDir
[MAX_PATH
];
147 MultiByteToWideChar(CP_UTF8
, 0, aDir
, -1, wideDir
, MAX_PATH
);
148 DWORD fileAttrs
= GetFileAttributesW(wideDir
);
149 return fileAttrs
!= INVALID_FILE_ATTRIBUTES
;
151 return access(aDir
, R_OK
) == 0;
155 static nsresult
GetRealPath(const char* appDataFile
, char* *aResult
)
158 wchar_t wAppDataFile
[MAX_PATH
];
159 wchar_t wIniPath
[MAX_PATH
];
160 MultiByteToWideChar(CP_UTF8
, 0, appDataFile
, -1, wAppDataFile
, MAX_PATH
);
161 _wfullpath(wIniPath
, wAppDataFile
, MAX_PATH
);
162 WideCharToMultiByte(CP_UTF8
, 0, wIniPath
, -1, *aResult
, MAX_PATH
, 0, 0);
164 struct stat fileStat
;
165 if (!realpath(appDataFile
, *aResult
) || stat(*aResult
, &fileStat
))
166 return NS_ERROR_FAILURE
;
168 if (!*aResult
|| !**aResult
)
169 return NS_ERROR_FAILURE
;
177 AutoAppData(nsILocalFile
* aINIFile
) : mAppData(nsnull
) {
178 nsresult rv
= XRE_CreateAppData(aINIFile
, &mAppData
);
184 XRE_FreeAppData(mAppData
);
187 operator nsXREAppData
*() const { return mAppData
; }
188 nsXREAppData
* operator -> () const { return mAppData
; }
191 nsXREAppData
* mAppData
;
194 XRE_CreateAppDataType XRE_CreateAppData
;
195 XRE_FreeAppDataType XRE_FreeAppData
;
196 XRE_mainType XRE_main
;
201 ForwardToWindow(HWND wnd
) {
202 // For WinCE, we're stuck with providing our own argv[0] for the remote
204 WCHAR wPath
[MAX_PATH
] = L
"dummy ";
205 WCHAR
*wCmd
= ::GetCommandLineW();
206 WCHAR wCwd
[MAX_PATH
];
207 _wgetcwd(wCwd
, MAX_PATH
);
209 // Construct a narrow UTF8 buffer <path> <commandline>\0<workingdir>\0
210 size_t len
= wcslen(wPath
) + wcslen(wCmd
) + wcslen(wCwd
) + 2;
211 WCHAR
*wMsg
= (WCHAR
*)malloc(len
* sizeof(*wMsg
));
213 wcscpy(wMsg
+ wcslen(wPath
), wCmd
); // The command line
214 wcscpy(wMsg
+ wcslen(wPath
) + wcslen(wCmd
) + 1, wCwd
); // Working dir
216 // Then convert to UTF-8, assuming worst-case explosion of characters
217 char *msg
= (char *)malloc(len
* 4);
218 WideCharToMultiByte(CP_UTF8
, 0, wMsg
, len
, msg
, len
* 4, NULL
, NULL
);
220 // We used to set dwData to zero, when we didn't send the working dir.
221 // Now we're using it as a version number.
222 COPYDATASTRUCT cds
= { 1, len
, (void *)msg
};
224 // Bring the already running Mozilla process to the foreground.
225 // nsWindow will restore the window (if minimized) and raise it.
226 // for activating the existing window on wince we need "| 0x01"
227 // see http://msdn.microsoft.com/en-us/library/ms940024.aspx for details
228 ::SetForegroundWindow((HWND
)(((ULONG
) wnd
) | 0x01));
229 ::SendMessage(wnd
, WM_COPYDATA
, 0, (LPARAM
)&cds
);
236 main(int argc
, char **argv
)
241 char iniPath
[MAXPATHLEN
];
242 char tmpPath
[MAXPATHLEN
];
243 char greDir
[MAXPATHLEN
];
244 PRBool greFound
= PR_FALSE
;
246 #if defined(XP_MACOSX)
247 CFBundleRef appBundle
= CFBundleGetMainBundle();
251 CFURLRef resourcesURL
= CFBundleCopyResourcesDirectoryURL(appBundle
);
255 CFURLRef absResourcesURL
= CFURLCopyAbsoluteURL(resourcesURL
);
256 CFRelease(resourcesURL
);
257 if (!absResourcesURL
)
260 CFURLRef iniFileURL
=
261 CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault
,
263 CFSTR("application.ini"),
265 CFRelease(absResourcesURL
);
269 CFStringRef iniPathStr
=
270 CFURLCopyFileSystemPath(iniFileURL
, kCFURLPOSIXPathStyle
);
271 CFRelease(iniFileURL
);
275 CFStringGetCString(iniPathStr
, iniPath
, sizeof(iniPath
),
276 kCFStringEncodingUTF8
);
277 CFRelease(iniPathStr
);
282 wchar_t wide_path
[MAX_PATH
];
283 if (!::GetModuleFileNameW(NULL
, wide_path
, MAX_PATH
))
286 WideCharToMultiByte(CP_UTF8
, 0, wide_path
,-1,
287 iniPath
, MAX_PATH
, NULL
, NULL
);
289 #elif defined(XP_OS2)
293 DosGetInfoBlocks(&ptib
, &ppib
);
294 DosQueryModuleName(ppib
->pib_hmte
, sizeof(iniPath
), iniPath
);
296 #elif defined(XP_BEOS)
297 BEntry
e((const char *)argv
[0], true); // traverse symlink
301 NS_ASSERTION(err
== B_OK
, "realpath failed");
304 // p.Path returns a pointer, so use strcpy to store path in iniPath
305 strcpy(iniPath
, p
.Path());
308 // on unix, there is no official way to get the path of the current binary.
309 // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to
310 // multiple applications, we will try a series of techniques:
312 // 1) use realpath() on argv[0], which works unless we're loaded from the
314 // 2) manually walk through the PATH and look for ourself
317 struct stat fileStat
;
319 if (!realpath(argv
[0], iniPath
) || stat(iniPath
, &fileStat
)) {
320 const char *path
= getenv("PATH");
324 char *pathdup
= strdup(path
);
328 PRBool found
= PR_FALSE
;
329 char *token
= strtok(pathdup
, ":");
331 sprintf(tmpPath
, "%s/%s", token
, argv
[0]);
332 if (realpath(tmpPath
, iniPath
) && stat(iniPath
, &fileStat
) == 0) {
336 token
= strtok(NULL
, ":");
344 lastSlash
= strrchr(iniPath
, PATH_SEPARATOR_CHAR
);
348 *(++lastSlash
) = '\0';
350 // On Linux/Win, look for XULRunner in appdir/xulrunner
352 snprintf(greDir
, sizeof(greDir
),
353 "%sxulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
,
356 greFound
= FolderExists(greDir
);
358 strncpy(lastSlash
, "application.ini", sizeof(iniPath
) - (lastSlash
- iniPath
));
362 // If -app parameter was passed in, it is now time to take it under
364 const char *appDataFile
;
365 appDataFile
= getenv("XUL_APP_FILE");
366 if (!appDataFile
|| !*appDataFile
)
367 if (argc
> 1 && IsArg(argv
[1], "app")) {
369 Output(PR_FALSE
, "specify APP-FILE (optional)\n");
376 appDataFile
= argv
[1];
381 char kAppEnv
[MAXPATHLEN
];
382 snprintf(kAppEnv
, MAXPATHLEN
, "XUL_APP_FILE=%s", appDataFile
);
384 Output(PR_FALSE
, "Couldn't set %s.\n", kAppEnv
);
386 char *result
= (char*) calloc(sizeof(char), MAXPATHLEN
);
387 if (NS_FAILED(GetRealPath(appDataFile
, &result
))) {
388 Output(PR_TRUE
, "Invalid application.ini path.\n");
392 // We have a valid application.ini path passed in to the -app parameter
393 // but not yet a valid greDir, so lets look for it also on the same folder
396 lastSlash
= strrchr(iniPath
, PATH_SEPARATOR_CHAR
);
400 *(++lastSlash
) = '\0';
402 snprintf(greDir
, sizeof(greDir
), "%s" XPCOM_DLL
, iniPath
);
403 greFound
= FolderExists(greDir
);
407 strcpy(iniPath
, result
);
411 rv
= parser
.Init(iniPath
);
413 fprintf(stderr
, "Could not read application.ini\n");
418 // On Windows Mobile and WinCE, we can save a lot of time by not
419 // waiting for XUL and XPCOM to load up. Let's see if we can find
420 // an existing app window to forward our command-line to now.
422 // Shouldn't attempt this if the -no-remote parameter has been provided.
423 bool noRemote
= false;
424 for (int i
= 1; i
< argc
; i
++) {
425 if (IsArg(argv
[i
], "no-remote")) {
432 char windowName
[512]; // Is there a const for appname like VERSION_MAXLEN?
433 rv
= parser
.GetString("App", "Name", windowName
, sizeof(windowName
));
435 fprintf(stderr
, "Couldn't figure out the application name\n");
439 // Lookup the hidden message window created by nsNativeAppSupport
440 strncat(windowName
, "MessageWindow", sizeof(windowName
) - strlen(windowName
));
441 WCHAR wWindowName
[512];
442 MultiByteToWideChar(CP_UTF8
, 0, windowName
, -1, wWindowName
, sizeof(wWindowName
));
443 HWND wnd
= ::FindWindowW(wWindowName
, NULL
);
445 // Forward the command-line and bail out
446 ForwardToWindow(wnd
);
453 char minVersion
[VERSION_MAXLEN
];
455 // If a gecko maxVersion is not specified, we assume that the app uses only
456 // frozen APIs, and is therefore compatible with any xulrunner 1.x.
457 char maxVersion
[VERSION_MAXLEN
] = "1.*";
459 GREVersionRange range
= {
466 rv
= parser
.GetString("Gecko", "MinVersion", minVersion
, sizeof(minVersion
));
469 "The application.ini does not specify a [Gecko] MinVersion\n");
473 rv
= parser
.GetString("Gecko", "MaxVersion", maxVersion
, sizeof(maxVersion
));
474 if (NS_SUCCEEDED(rv
))
475 range
.upperInclusive
= PR_TRUE
;
477 static const GREProperty kProperties
[] = {
478 { "xulrunner", "true" }
481 rv
= GRE_GetGREPathWithProperties(&range
, 1,
482 kProperties
, NS_ARRAY_LENGTH(kProperties
),
483 greDir
, sizeof(greDir
));
485 // XXXbsmedberg: Do something much smarter here: notify the
486 // user/offer to download/?
489 "Could not find compatible GRE between version %s and %s.\n",
490 range
.lower
, range
.upper
);
494 // Using a symlinked greDir will fail during startup. Not sure why, but if
495 // we resolve the symlink, everything works as expected.
496 char resolved_greDir
[MAXPATHLEN
] = "";
497 if (realpath(greDir
, resolved_greDir
) && *resolved_greDir
) {
498 strncpy(greDir
, resolved_greDir
, MAXPATHLEN
);
504 // On OS/2 we need to set BEGINLIBPATH to be able to find XULRunner DLLs
505 strcpy(tmpPath
, greDir
);
506 lastSlash
= strrchr(tmpPath
, PATH_SEPARATOR_CHAR
);
510 DosSetExtLIBPATH(tmpPath
, BEGIN_LIBPATH
);
513 rv
= XPCOMGlueStartup(greDir
);
515 if (rv
== NS_ERROR_OUT_OF_MEMORY
) {
516 char applicationName
[2000] = "this application";
517 parser
.GetString("App", "Name", applicationName
, sizeof(applicationName
));
518 Output(PR_TRUE
, "Not enough memory available to start %s.\n",
521 Output(PR_TRUE
, "Couldn't load XPCOM.\n");
526 static const nsDynamicFunctionLoad kXULFuncs
[] = {
527 { "XRE_CreateAppData", (NSFuncPtr
*) &XRE_CreateAppData
},
528 { "XRE_FreeAppData", (NSFuncPtr
*) &XRE_FreeAppData
},
529 { "XRE_main", (NSFuncPtr
*) &XRE_main
},
533 rv
= XPCOMGlueLoadXULFunctions(kXULFuncs
);
535 Output(PR_TRUE
, "Couldn't load XRE functions.\n");
543 { // Scope COMPtr and AutoAppData
544 nsCOMPtr
<nsILocalFile
> iniFile
;
546 // On Windows and Windows CE, iniPath is UTF-8 encoded,
547 // so we need to convert it.
548 rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(iniPath
), PR_FALSE
,
549 getter_AddRefs(iniFile
));
551 rv
= NS_NewNativeLocalFile(nsDependentCString(iniPath
), PR_FALSE
,
552 getter_AddRefs(iniFile
));
555 Output(PR_TRUE
, "Couldn't find application.ini file.\n");
559 AutoAppData
appData(iniFile
);
561 Output(PR_TRUE
, "Error: couldn't parse application.ini.\n");
565 NS_ASSERTION(appData
->directory
, "Failed to get app directory.");
567 if (!appData
->xreDirectory
) {
568 // chop "libxul.so" off the GRE path
569 lastSlash
= strrchr(greDir
, PATH_SEPARATOR_CHAR
);
575 NS_NewLocalFile(NS_ConvertUTF8toUTF16(greDir
), PR_FALSE
,
576 &appData
->xreDirectory
);
578 NS_NewNativeLocalFile(nsDependentCString(greDir
), PR_FALSE
,
579 &appData
->xreDirectory
);
583 retval
= XRE_main(argc
, argv
, appData
);