1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsXULAppAPI.h"
7 #include "mozilla/AppData.h"
8 #include "application.ini.h"
9 #include "nsXPCOMGlue.h"
15 #elif defined(XP_UNIX)
16 #include <sys/resource.h>
22 #include <mach/mach_time.h>
23 #include "MacQuirks.h"
32 #include "nsStringGlue.h"
34 // Easy access to a five second startup delay used to get
35 // a debugger attached in the metro environment.
36 // #define DEBUG_delay_start_metro
39 // we want a wmain entry point
41 // ASAN requires firefox.exe to be built with -MD, and it's OK if we don't
42 // support Windows XP SP2 in ASAN builds.
43 #define XRE_DONT_SUPPORT_XPSP2
45 #include "nsWindowsWMain.cpp"
46 #define snprintf _snprintf
47 #define strcasecmp _stricmp
49 #include "BinaryPath.h"
51 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
53 #include "mozilla/Telemetry.h"
54 #include "mozilla/WindowsDllBlocklist.h"
56 using namespace mozilla
;
59 #define kOSXResourcesFolder "Resources"
61 #define kDesktopFolder "browser"
62 #define kMetroFolder "metro"
63 #define kMetroAppIniFilename "metroapp.ini"
65 #define kMetroTestFile "tests.ini"
66 const char* kMetroConsoleIdParam
= "testconsoleid=";
69 static void Output(const char *fmt
, ... )
75 vfprintf(stderr
, fmt
, ap
);
78 vsnprintf_s(msg
, _countof(msg
), _TRUNCATE
, fmt
, ap
);
80 wchar_t wide_msg
[2048];
81 MultiByteToWideChar(CP_UTF8
,
88 fwprintf_s(stderr
, wide_msg
);
90 // Linking user32 at load-time interferes with the DLL blocklist (bug 932100).
91 // This is a rare codepath, so we can load user32 at run-time instead.
92 HMODULE user32
= LoadLibraryW(L
"user32.dll");
94 decltype(MessageBoxW
)* messageBoxW
=
95 (decltype(MessageBoxW
)*) GetProcAddress(user32
, "MessageBoxW");
97 messageBoxW(nullptr, wide_msg
, L
"Firefox", MB_OK
110 * Return true if |arg| matches the given argument name.
112 static bool IsArg(const char* arg
, const char* s
)
118 return !strcasecmp(arg
, s
);
123 return !strcasecmp(++arg
, s
);
129 #if defined(XP_WIN) && defined(MOZ_METRO)
131 * AttachToTestHarness - Windows helper for when we are running
132 * in the immersive environment. Firefox is launched by Windows in
133 * response to a request by metrotestharness, which is launched by
134 * runtests.py. As such stdout in fx doesn't point to the right
135 * stream. This helper touches up stdout such that test output gets
136 * routed to a named pipe metrotestharness creates and dumps to its
139 static void AttachToTestHarness()
141 // attach to the metrotestharness named logging pipe
142 HANDLE winOut
= CreateFileA("\\\\.\\pipe\\metrotestharness",
145 OPEN_EXISTING
, 0, 0);
147 if (winOut
== INVALID_HANDLE_VALUE
) {
148 OutputDebugStringW(L
"Could not create named logging pipe.\n");
152 // Set the c runtime handle
153 int stdOut
= _open_osfhandle((intptr_t)winOut
, _O_APPEND
);
155 OutputDebugStringW(L
"Could not open c-runtime handle.\n");
158 FILE *fp
= _fdopen(stdOut
, "a");
163 XRE_GetFileFromPathType XRE_GetFileFromPath
;
164 XRE_CreateAppDataType XRE_CreateAppData
;
165 XRE_FreeAppDataType XRE_FreeAppData
;
166 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate
;
167 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord
;
168 XRE_mainType XRE_main
;
169 XRE_StopLateWriteChecksType XRE_StopLateWriteChecks
;
171 static const nsDynamicFunctionLoad kXULFuncs
[] = {
172 { "XRE_GetFileFromPath", (NSFuncPtr
*) &XRE_GetFileFromPath
},
173 { "XRE_CreateAppData", (NSFuncPtr
*) &XRE_CreateAppData
},
174 { "XRE_FreeAppData", (NSFuncPtr
*) &XRE_FreeAppData
},
175 { "XRE_TelemetryAccumulate", (NSFuncPtr
*) &XRE_TelemetryAccumulate
},
176 { "XRE_StartupTimelineRecord", (NSFuncPtr
*) &XRE_StartupTimelineRecord
},
177 { "XRE_main", (NSFuncPtr
*) &XRE_main
},
178 { "XRE_StopLateWriteChecks", (NSFuncPtr
*) &XRE_StopLateWriteChecks
},
182 static int do_main(int argc
, char* argv
[], nsIFile
*xreDirectory
)
184 nsCOMPtr
<nsIFile
> appini
;
186 uint32_t mainFlags
= 0;
188 // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
189 // Note that -app must be the *first* argument.
190 const char *appDataFile
= getenv("XUL_APP_FILE");
191 if (appDataFile
&& *appDataFile
) {
192 rv
= XRE_GetFileFromPath(appDataFile
, getter_AddRefs(appini
));
194 Output("Invalid path found: '%s'", appDataFile
);
198 else if (argc
> 1 && IsArg(argv
[1], "app")) {
200 Output("Incorrect number of arguments passed to -app");
204 rv
= XRE_GetFileFromPath(argv
[2], getter_AddRefs(appini
));
206 Output("application.ini path not recognized: '%s'", argv
[2]);
210 char appEnv
[MAXPATHLEN
];
211 snprintf(appEnv
, MAXPATHLEN
, "XUL_APP_FILE=%s", argv
[2]);
212 if (putenv(appEnv
)) {
213 Output("Couldn't set %s.\n", appEnv
);
222 nsXREAppData
*appData
;
223 rv
= XRE_CreateAppData(appini
, &appData
);
225 Output("Couldn't read application.ini");
228 // xreDirectory already has a refcount from NS_NewLocalFile
229 appData
->xreDirectory
= xreDirectory
;
230 int result
= XRE_main(argc
, argv
, appData
, mainFlags
);
231 XRE_FreeAppData(appData
);
235 bool metroOnDesktop
= false;
239 // This command-line flag is passed to our executable when it is to be
240 // launched in metro mode (i.e. our EXE is registered as the default
241 // browser and the user has tapped our EXE's tile)
242 if (IsArg(argv
[1], "ServerName:DefaultBrowserServer")) {
243 mainFlags
= XRE_MAIN_FLAG_USE_METRO
;
247 } else if (IsArg(argv
[1], "BackgroundSessionClosed")) {
248 // This command line flag is used for indirect shutdowns, the OS
249 // relaunches Metro Firefox with this command line arg.
250 mainFlags
= XRE_MAIN_FLAG_USE_METRO
;
252 #ifndef RELEASE_BUILD
253 // This command-line flag is used to test the metro browser in a desktop
255 for (int idx
= 1; idx
< argc
; idx
++) {
256 if (IsArg(argv
[idx
], "metrodesktop")) {
257 metroOnDesktop
= true;
258 // Disable crash reporting when running in metrodesktop mode.
259 char crashSwitch
[] = "MOZ_CRASHREPORTER_DISABLE=1";
269 // Desktop browser launch
270 if (mainFlags
!= XRE_MAIN_FLAG_USE_METRO
&& !metroOnDesktop
) {
271 ScopedAppData
appData(&sAppData
);
272 nsCOMPtr
<nsIFile
> exeFile
;
273 rv
= mozilla::BinaryPath::GetFile(argv
[0], getter_AddRefs(exeFile
));
275 Output("Couldn't find the application directory.\n");
279 nsCOMPtr
<nsIFile
> greDir
;
280 exeFile
->GetParent(getter_AddRefs(greDir
));
282 greDir
->SetNativeLeafName(NS_LITERAL_CSTRING(kOSXResourcesFolder
));
284 nsCOMPtr
<nsIFile
> appSubdir
;
285 greDir
->Clone(getter_AddRefs(appSubdir
));
286 appSubdir
->Append(NS_LITERAL_STRING(kDesktopFolder
));
288 SetStrongPtr(appData
.directory
, static_cast<nsIFile
*>(appSubdir
.get()));
289 // xreDirectory already has a refcount from NS_NewLocalFile
290 appData
.xreDirectory
= xreDirectory
;
292 return XRE_main(argc
, argv
, &appData
, mainFlags
);
295 // Metro browser launch
297 nsCOMPtr
<nsIFile
> iniFile
, appSubdir
;
299 xreDirectory
->Clone(getter_AddRefs(iniFile
));
300 xreDirectory
->Clone(getter_AddRefs(appSubdir
));
302 iniFile
->Append(NS_LITERAL_STRING(kMetroFolder
));
303 iniFile
->Append(NS_LITERAL_STRING(kMetroAppIniFilename
));
305 appSubdir
->Append(NS_LITERAL_STRING(kMetroFolder
));
308 if (NS_FAILED(iniFile
->GetNativePath(path
))) {
309 Output("Couldn't get ini file path.\n");
313 nsXREAppData
*appData
;
314 rv
= XRE_CreateAppData(iniFile
, &appData
);
315 if (NS_FAILED(rv
) || !appData
) {
316 Output("Couldn't read application.ini");
320 SetStrongPtr(appData
->directory
, static_cast<nsIFile
*>(appSubdir
.get()));
321 // xreDirectory already has a refcount from NS_NewLocalFile
322 appData
->xreDirectory
= xreDirectory
;
325 if (!metroOnDesktop
) {
326 nsCOMPtr
<nsIFile
> testFile
;
328 xreDirectory
->Clone(getter_AddRefs(testFile
));
329 testFile
->Append(NS_LITERAL_STRING(kMetroTestFile
));
332 if (NS_FAILED(testFile
->GetNativePath(path
))) {
333 Output("Couldn't get test file path.\n");
337 // Check for a metro test harness command line args file
338 HANDLE hTestFile
= CreateFileA(path
.get(),
340 0, nullptr, OPEN_EXISTING
,
341 FILE_ATTRIBUTE_NORMAL
,
343 if (hTestFile
!= INVALID_HANDLE_VALUE
) {
344 // Typical test harness command line args string is around 100 bytes.
346 memset(buffer
, 0, sizeof(buffer
));
348 if (!ReadFile(hTestFile
, (VOID
*)buffer
, sizeof(buffer
)-1,
349 &bytesRead
, nullptr) || !bytesRead
) {
350 CloseHandle(hTestFile
);
351 printf("failed to read test file '%s'", testFile
);
354 CloseHandle(hTestFile
);
356 // Build new args array
360 memset(newArgv
, 0, sizeof(newArgv
));
364 while (*ptr
!= '\0' &&
365 (ptr
- buffer
) < sizeof(buffer
) &&
366 newArgc
< ARRAYSIZE(newArgv
)) {
370 newArgv
[newArgc
] = ptr
;
376 if (ptr
== newArgv
[newArgc
-1])
379 // attach browser stdout to metrotestharness stdout
380 AttachToTestHarness();
382 int result
= XRE_main(newArgc
, newArgv
, appData
, mainFlags
);
383 XRE_FreeAppData(appData
);
389 int result
= XRE_main(argc
, argv
, appData
, mainFlags
);
390 XRE_FreeAppData(appData
);
394 NS_NOTREACHED("browser do_main failed to pickup proper initialization");
401 * Used only when GetTickCount64 is not available on the platform.
402 * Last result of GetTickCount call. Kept in [ms].
404 static DWORD sLastGTCResult
= 0;
407 * Higher part of the 64-bit value of MozGetTickCount64,
408 * incremented atomically.
410 static DWORD sLastGTCRollover
= 0;
413 * Function protecting GetTickCount result from rolling over. The original
414 * code comes from the Windows implementation of the TimeStamp class minus the
415 * locking harness which isn't needed here.
417 * @returns The current time in milliseconds
419 static ULONGLONG WINAPI
422 DWORD GTC
= ::GetTickCount();
424 /* Pull the rollover counter forward only if new value of GTC goes way
425 * down under the last saved result */
426 if ((sLastGTCResult
> GTC
) && ((sLastGTCResult
- GTC
) > (1UL << 30)))
429 sLastGTCResult
= GTC
;
430 return (ULONGLONG
)sLastGTCRollover
<< 32 | sLastGTCResult
;
433 typedef ULONGLONG (WINAPI
* GetTickCount64_t
)();
434 static GetTickCount64_t sGetTickCount64
= nullptr;
439 * Local TimeStamp::Now()-compatible implementation used to record timestamps
440 * which will be passed to XRE_StartupTimelineRecord().
447 ::QueryPerformanceFrequency(&freq
);
449 HMODULE kernelDLL
= GetModuleHandleW(L
"kernel32.dll");
450 sGetTickCount64
= reinterpret_cast<GetTickCount64_t
>
451 (GetProcAddress(kernelDLL
, "GetTickCount64"));
453 if (!sGetTickCount64
) {
454 /* If the platform does not support the GetTickCount64 (Windows XP doesn't),
455 * then use our fallback implementation based on GetTickCount. */
456 sGetTickCount64
= MozGetTickCount64
;
459 return sGetTickCount64() * freq
.QuadPart
;
460 #elif defined(XP_MACOSX)
461 return mach_absolute_time();
462 #elif defined(HAVE_CLOCK_MONOTONIC)
464 int rv
= clock_gettime(CLOCK_MONOTONIC
, &ts
);
470 uint64_t baseNs
= (uint64_t)ts
.tv_sec
* 1000000000;
471 return baseNs
+ (uint64_t)ts
.tv_nsec
;
476 FileExists(const char *path
)
479 wchar_t wideDir
[MAX_PATH
];
480 MultiByteToWideChar(CP_UTF8
, 0, path
, -1, wideDir
, MAX_PATH
);
481 DWORD fileAttrs
= GetFileAttributesW(wideDir
);
482 return fileAttrs
!= INVALID_FILE_ATTRIBUTES
;
484 return access(path
, R_OK
) == 0;
489 # define XPCOM_PATH "xulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
491 # define XPCOM_PATH XPCOM_DLL
494 InitXPCOMGlue(const char *argv0
, nsIFile
**xreDirectory
)
496 char exePath
[MAXPATHLEN
];
498 nsresult rv
= mozilla::BinaryPath::Get(argv0
, exePath
);
500 Output("Couldn't find the application directory.\n");
504 char *lastSlash
= strrchr(exePath
, XPCOM_FILE_PATH_SEPARATOR
[0]);
505 if (!lastSlash
|| (size_t(lastSlash
- exePath
) > MAXPATHLEN
- sizeof(XPCOM_PATH
) - 1))
506 return NS_ERROR_FAILURE
;
508 strcpy(lastSlash
+ 1, XPCOM_PATH
);
509 lastSlash
+= sizeof(XPCOM_PATH
) - sizeof(XPCOM_DLL
);
511 if (!FileExists(exePath
)) {
512 #if defined(LIBXUL_SDK) && defined(XP_MACOSX)
513 // Check for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
514 bool greFound
= false;
515 CFBundleRef appBundle
= CFBundleGetMainBundle();
517 return NS_ERROR_FAILURE
;
518 CFURLRef fwurl
= CFBundleCopyPrivateFrameworksURL(appBundle
);
519 CFURLRef absfwurl
= nullptr;
521 absfwurl
= CFURLCopyAbsoluteURL(fwurl
);
526 CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl
,
527 CFSTR("XUL.framework"),
532 CFURLCreateCopyAppendingPathComponent(nullptr, xulurl
,
533 CFSTR("libxpcom.dylib"),
537 if (CFURLGetFileSystemRepresentation(xpcomurl
, true,
540 access(tbuffer
, R_OK
| X_OK
) == 0) {
541 if (realpath(tbuffer
, exePath
)) {
554 Output("Could not find the Mozilla runtime.\n");
555 return NS_ERROR_FAILURE
;
558 // We do this because of data in bug 771745
559 XPCOMGlueEnablePreload();
561 rv
= XPCOMGlueStartup(exePath
);
563 Output("Couldn't load XPCOM.\n");
567 rv
= XPCOMGlueLoadXULFunctions(kXULFuncs
);
569 Output("Couldn't load XRE functions.\n");
574 // This will set this thread as the main thread, which in metro land is
575 // wrong. We initialize this later from the right thread in nsAppRunner.
579 // chop XPCOM_DLL off exePath
582 lastSlash
= strrchr(exePath
, XPCOM_FILE_PATH_SEPARATOR
[0]);
583 strcpy(lastSlash
+ 1, kOSXResourcesFolder
);
586 rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath
), false,
589 rv
= NS_NewNativeLocalFile(nsDependentCString(exePath
), false,
596 int main(int argc
, char* argv
[])
598 #ifdef DEBUG_delay_start_metro
601 uint64_t start
= TimeStamp_Now();
609 struct rusage initialRUsage
;
610 gotCounters
= !getrusage(RUSAGE_SELF
, &initialRUsage
);
611 #elif defined(XP_WIN)
612 IO_COUNTERS ioCounters
;
613 gotCounters
= GetProcessIoCounters(GetCurrentProcess(), &ioCounters
);
616 nsIFile
*xreDirectory
;
618 #ifdef HAS_DLL_BLOCKLIST
619 DllBlocklist_Initialize();
622 // In order to be effective against AppInit DLLs, the blocklist must be
623 // initialized before user32.dll is loaded into the process (bug 932100).
624 if (GetModuleHandleA("user32.dll")) {
625 fprintf(stderr
, "DLL blocklist was unable to intercept AppInit DLLs.\n");
630 nsresult rv
= InitXPCOMGlue(argv
[0], &xreDirectory
);
635 XRE_StartupTimelineRecord(mozilla::StartupTimeline::START
, start
);
639 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS
,
640 int(ioCounters
.ReadOperationCount
));
641 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER
,
642 int(ioCounters
.ReadTransferCount
/ 1024));
643 IO_COUNTERS newIoCounters
;
644 if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters
)) {
645 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS
,
646 int(newIoCounters
.ReadOperationCount
- ioCounters
.ReadOperationCount
));
647 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER
,
648 int((newIoCounters
.ReadTransferCount
- ioCounters
.ReadTransferCount
) / 1024));
650 #elif defined(XP_UNIX)
651 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS
,
652 int(initialRUsage
.ru_majflt
));
653 struct rusage newRUsage
;
654 if (!getrusage(RUSAGE_SELF
, &newRUsage
)) {
655 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS
,
656 int(newRUsage
.ru_majflt
- initialRUsage
.ru_majflt
));
661 int result
= do_main(argc
, argv
, xreDirectory
);
666 // Allow writes again. While we would like to catch writes from static
667 // destructors to allow early exits to use _exit, we know that there is
668 // at least one such write that we don't control (see bug 826029). For
669 // now we enable writes again and early exits will have to use exit instead
671 XRE_StopLateWriteChecks();