1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // System headers (alphabetical)
14 // Mozilla headers (alphabetical)
16 #include "nsINIParser.h"
17 #include "nsWindowsWMain.cpp" // we want a wmain entry point
18 #include "nsXPCOMGlue.h"
19 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
20 #include "nsXULAppAPI.h"
21 #include "mozilla/AppData.h"
23 using namespace mozilla
;
25 XRE_GetFileFromPathType XRE_GetFileFromPath
;
26 XRE_CreateAppDataType XRE_CreateAppData
;
27 XRE_FreeAppDataType XRE_FreeAppData
;
28 XRE_mainType XRE_main
;
31 const char kAPP_INI
[] = "application.ini";
32 const char kWEBAPP_INI
[] = "webapp.ini";
33 const char kWEBAPPRT_INI
[] = "webapprt.ini";
34 const char kWEBAPPRT_PATH
[] = "webapprt";
35 const char kAPP_ENV_PREFIX
[] = "XUL_APP_FILE=";
36 const char kAPP_RT
[] = "webapprt-stub.exe";
38 const wchar_t kAPP_RT_BACKUP
[] = L
"webapprt.old";
40 wchar_t curExePath
[MAXPATHLEN
];
41 wchar_t backupFilePath
[MAXPATHLEN
];
42 wchar_t iconPath
[MAXPATHLEN
];
43 char profile
[MAXPATHLEN
];
44 bool isProfileOverridden
= false;
49 joinPath(char* const dest
,
50 char const* const dir
,
51 char const* const leaf
,
54 size_t dirLen
= strlen(dir
);
55 size_t leafLen
= strlen(leaf
);
56 bool needsSeparator
= (dirLen
!= 0
57 && dir
[dirLen
-1] != '\\'
61 if (dirLen
+ (needsSeparator
? 1 : 0) + leafLen
>= bufferSize
) {
62 return NS_ERROR_FAILURE
;
65 strncpy(dest
, dir
, bufferSize
);
66 char* destEnd
= dest
+ dirLen
;
71 strncpy(destEnd
, leaf
, leafLen
);
76 * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
81 ScopedLogging() { NS_LogInit(); }
82 ~ScopedLogging() { NS_LogTerm(); }
86 * A helper class for scope-guarding nsXREAppData.
88 class ScopedXREAppData
92 : mAppData(nullptr) { }
95 create(nsIFile
* aINIFile
)
97 return XRE_CreateAppData(aINIFile
, &mAppData
);
102 if (nullptr != mAppData
) {
103 XRE_FreeAppData(mAppData
);
125 nsXREAppData
* mAppData
;
126 nsXREAppData
* const get() { return mAppData
; }
130 Output(const wchar_t *fmt
, ... )
136 _vsnwprintf_s(msg
, _countof(msg
), _countof(msg
), fmt
, ap
);
138 MessageBoxW(nullptr, msg
, L
"Web Runtime", MB_OK
);
144 Output(const char *fmt
, ... )
150 vsnprintf(msg
, sizeof(msg
), fmt
, ap
);
152 wchar_t wide_msg
[1024];
153 MultiByteToWideChar(CP_UTF8
,
164 const nsDynamicFunctionLoad kXULFuncs
[] = {
165 { "XRE_GetFileFromPath", (NSFuncPtr
*) &XRE_GetFileFromPath
},
166 { "XRE_CreateAppData", (NSFuncPtr
*) &XRE_CreateAppData
},
167 { "XRE_FreeAppData", (NSFuncPtr
*) &XRE_FreeAppData
},
168 { "XRE_main", (NSFuncPtr
*) &XRE_main
},
173 AttemptCopyAndLaunch(wchar_t* src
)
175 // Rename the old app executable
176 if (FALSE
== ::MoveFileExW(curExePath
,
178 MOVEFILE_REPLACE_EXISTING
)) {
182 // Copy webapprt-stub.exe from the Firefox dir to the app's dir
183 if (FALSE
== ::CopyFileW(src
,
186 // Try to move the old file back to its original location
187 ::MoveFileW(backupFilePath
,
192 // XXX: We will soon embed the app's icon in the EXE here
195 PROCESS_INFORMATION pi
;
197 ::ZeroMemory(&si
, sizeof(si
));
199 ::ZeroMemory(&pi
, sizeof(pi
));
201 if (!CreateProcessW(curExePath
, // Module name
202 nullptr, // Command line
203 nullptr, // Process handle not inheritable
204 nullptr, // Thread handle not inheritable
205 FALSE
, // Set handle inheritance to FALSE
206 0, // No creation flags
207 nullptr, // Use parent's environment block
208 nullptr, // Use parent's starting directory
214 // Close process and thread handles.
215 CloseHandle( pi
.hProcess
);
216 CloseHandle( pi
.hThread
);
222 AttemptCopyAndLaunch(char* srcUtf8
)
224 wchar_t src
[MAXPATHLEN
];
225 if (0 == MultiByteToWideChar(CP_UTF8
,
234 return AttemptCopyAndLaunch(src
);
238 AttemptGRELoadAndLaunch(char* greDir
)
242 char xpcomDllPath
[MAXPATHLEN
];
243 rv
= joinPath(xpcomDllPath
, greDir
, XPCOM_DLL
, MAXPATHLEN
);
244 NS_ENSURE_SUCCESS(rv
, false);
246 rv
= XPCOMGlueStartup(xpcomDllPath
);
247 NS_ENSURE_SUCCESS(rv
, false);
249 rv
= XPCOMGlueLoadXULFunctions(kXULFuncs
);
250 NS_ENSURE_SUCCESS(rv
, false);
252 // NOTE: The GRE has successfully loaded, so we can use XPCOM now
253 { // Scope for any XPCOM stuff we create
257 // Get the path to the runtime.
258 char rtPath
[MAXPATHLEN
];
259 rv
= joinPath(rtPath
, greDir
, kWEBAPPRT_PATH
, MAXPATHLEN
);
260 NS_ENSURE_SUCCESS(rv
, false);
262 // Get the path to the runtime's INI file.
263 char rtIniPath
[MAXPATHLEN
];
264 rv
= joinPath(rtIniPath
, rtPath
, kWEBAPPRT_INI
, MAXPATHLEN
);
265 NS_ENSURE_SUCCESS(rv
, false);
267 // Load the runtime's INI from its path.
268 nsCOMPtr
<nsIFile
> rtINI
;
269 rv
= XRE_GetFileFromPath(rtIniPath
, getter_AddRefs(rtINI
));
270 NS_ENSURE_SUCCESS(rv
, false);
273 rv
= rtINI
->Exists(&exists
);
274 if (NS_FAILED(rv
) || !exists
)
277 ScopedXREAppData webShellAppData
;
278 rv
= webShellAppData
.create(rtINI
);
279 NS_ENSURE_SUCCESS(rv
, false);
281 if (!isProfileOverridden
) {
282 SetAllocatedString(webShellAppData
->profile
, profile
);
283 SetAllocatedString(webShellAppData
->name
, profile
);
286 nsCOMPtr
<nsIFile
> directory
;
287 rv
= XRE_GetFileFromPath(rtPath
, getter_AddRefs(directory
));
288 NS_ENSURE_SUCCESS(rv
, false);
290 nsCOMPtr
<nsIFile
> xreDir
;
291 rv
= XRE_GetFileFromPath(greDir
, getter_AddRefs(xreDir
));
292 NS_ENSURE_SUCCESS(rv
, false);
294 xreDir
.forget(&webShellAppData
->xreDirectory
);
295 NS_IF_RELEASE(webShellAppData
->directory
);
296 directory
.forget(&webShellAppData
->directory
);
298 // There is only XUL.
299 XRE_main(*pargc
, *pargv
, webShellAppData
, 0);
306 AttemptLoadFromDir(char* firefoxDir
)
310 // Here we're going to open Firefox's application.ini
311 char appIniPath
[MAXPATHLEN
];
312 rv
= joinPath(appIniPath
, firefoxDir
, kAPP_INI
, MAXPATHLEN
);
313 NS_ENSURE_SUCCESS(rv
, false);
316 rv
= parser
.Init(appIniPath
);
317 NS_ENSURE_SUCCESS(rv
, false);
319 // Get buildid of FF we're trying to load
320 char buildid
[MAXPATHLEN
]; // This isn't a path, so MAXPATHLEN doesn't
321 // necessarily make sense, but it's a
322 // convenient number to use.
323 rv
= parser
.GetString("App",
327 NS_ENSURE_SUCCESS(rv
, false);
329 if (0 == strcmp(buildid
, NS_STRINGIFY(GRE_BUILDID
))) {
330 return AttemptGRELoadAndLaunch(firefoxDir
);
333 char webAppRTExe
[MAXPATHLEN
];
334 rv
= joinPath(webAppRTExe
, firefoxDir
, kAPP_RT
, MAXPATHLEN
);
335 NS_ENSURE_SUCCESS(rv
, false);
337 return AttemptCopyAndLaunch(webAppRTExe
);
341 GetFirefoxDirFromRegistry(char* firefoxDir
)
344 wchar_t wideGreDir
[MAXPATHLEN
];
347 RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
348 L
"SOFTWARE\\Microsoft\\Windows"
349 L
"\\CurrentVersion\\App paths\\firefox.exe",
356 DWORD length
= MAXPATHLEN
* sizeof(wchar_t);
357 // XXX: When Vista/XP64 become our minimum supported client, we can use
358 // RegGetValue instead
359 if (ERROR_SUCCESS
!= RegQueryValueExW(key
,
363 reinterpret_cast<BYTE
*>(wideGreDir
),
370 // According to this article, we need to write our own null terminator:
371 // http://msdn.microsoft.com/en-us/library/ms724911%28v=vs.85%29.aspx
372 length
= length
/ sizeof(wchar_t);
373 if (wideGreDir
[length
] != L
'\0') {
374 if (length
>= MAXPATHLEN
) {
377 wideGreDir
[length
] = L
'\0';
380 if (0 == WideCharToMultiByte(CP_UTF8
,
397 //////////////////////////////////////////////////////////////////////////////
400 // Note: XPCOM cannot be used until AttemptGRELoad has returned successfully.
402 main(int argc
, char* argv
[])
407 char buffer
[MAXPATHLEN
];
408 wchar_t wbuffer
[MAXPATHLEN
];
411 if (!GetModuleFileNameW(0, wbuffer
, MAXPATHLEN
)) {
412 Output("Couldn't calculate the application directory.");
415 wcsncpy(curExePath
, wbuffer
, MAXPATHLEN
);
417 // Get the current directory into wbuffer
418 wchar_t* lastSlash
= wcsrchr(wbuffer
, L
'\\');
420 Output("Application directory format not understood.");
423 *(++lastSlash
) = L
'\0';
425 // Set up backup file path
426 if (wcslen(wbuffer
) + _countof(kAPP_RT_BACKUP
) >= MAXPATHLEN
) {
427 Output("Application directory path is too long (couldn't set up backup file path).");
429 wcsncpy(lastSlash
, kAPP_RT_BACKUP
, _countof(kAPP_RT_BACKUP
));
430 wcsncpy(backupFilePath
, wbuffer
, MAXPATHLEN
);
434 // Convert current directory to utf8 and stuff it in buffer
435 if (0 == WideCharToMultiByte(CP_UTF8
,
443 Output("Application directory could not be processed.");
447 // Check if the runtime was executed with the "-profile" argument
448 for (int i
= 1; i
< argc
; i
++) {
449 if (!strcmp(argv
[i
], "-profile")) {
450 isProfileOverridden
= true;
455 // First attempt at loading Firefox binaries:
456 // Check if the webapprt is in the same directory as the Firefox binary.
457 // This is the case during WebappRT chrome and content tests.
458 if (AttemptLoadFromDir(buffer
)) {
462 // Set up appIniPath with path to webapp.ini.
463 // This should be in the same directory as the running executable.
464 char appIniPath
[MAXPATHLEN
];
465 if (NS_FAILED(joinPath(appIniPath
, buffer
, kWEBAPP_INI
, MAXPATHLEN
))) {
466 Output("Path to webapp.ini could not be processed.");
470 // Open webapp.ini as an INI file (as opposed to using the
471 // XRE webapp.ini-specific processing we do later)
473 if (NS_FAILED(parser
.Init(appIniPath
))) {
474 Output("Could not open webapp.ini");
478 // Set up our environment to know where webapp.ini was loaded from.
479 char appEnv
[MAXPATHLEN
+ _countof(kAPP_ENV_PREFIX
)];
480 strcpy(appEnv
, kAPP_ENV_PREFIX
);
481 strcpy(appEnv
+ _countof(kAPP_ENV_PREFIX
) - 1, appIniPath
);
482 if (putenv(appEnv
)) {
483 Output("Couldn't set up app environment");
487 if (!isProfileOverridden
) {
488 // Get profile dir from webapp.ini
489 if (NS_FAILED(parser
.GetString("Webapp",
493 Output("Unable to retrieve profile from web app INI file");
498 char firefoxDir
[MAXPATHLEN
];
500 // Second attempt at loading Firefox binaries:
501 // Get the location of Firefox from our webapp.ini
503 // XXX: This string better be UTF-8...
504 rv
= parser
.GetString("WebappRT",
508 if (NS_SUCCEEDED(rv
)) {
509 if (AttemptLoadFromDir(firefoxDir
)) {
514 // Third attempt at loading Firefox binaries:
515 // Get the location of Firefox from the registry
516 if (GetFirefoxDirFromRegistry(firefoxDir
)) {
517 if (AttemptLoadFromDir(firefoxDir
)) {
518 // XXX: Write gre dir location to webapp.ini
523 // We've done all we know how to do to try to find and launch FF
524 Output("This app requires that Firefox version 16 or above is installed."
525 " Firefox 16+ has not been detected.");