Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / browser / app / nsBrowserApp.cpp
blob36f0aaa712d4931d97037e397f810272fb1e88ad
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"
10 #if defined(XP_WIN)
11 #include <windows.h>
12 #include <stdlib.h>
13 #include <io.h>
14 #include <fcntl.h>
15 #elif defined(XP_UNIX)
16 #include <sys/resource.h>
17 #include <time.h>
18 #include <unistd.h>
19 #endif
21 #ifdef XP_MACOSX
22 #include <mach/mach_time.h>
23 #include "MacQuirks.h"
24 #endif
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <time.h>
30 #include "nsCOMPtr.h"
31 #include "nsIFile.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
38 #ifdef XP_WIN
39 // we want a wmain entry point
40 #ifdef MOZ_ASAN
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
44 #endif
45 #include "nsWindowsWMain.cpp"
46 #define snprintf _snprintf
47 #define strcasecmp _stricmp
48 #endif
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;
58 #ifdef XP_MACOSX
59 #define kOSXResourcesFolder "Resources"
60 #endif
61 #define kDesktopFolder "browser"
62 #define kMetroFolder "metro"
63 #define kMetroAppIniFilename "metroapp.ini"
64 #ifdef XP_WIN
65 #define kMetroTestFile "tests.ini"
66 const char* kMetroConsoleIdParam = "testconsoleid=";
67 #endif
69 static void Output(const char *fmt, ... )
71 va_list ap;
72 va_start(ap, fmt);
74 #ifndef XP_WIN
75 vfprintf(stderr, fmt, ap);
76 #else
77 char msg[2048];
78 vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
80 wchar_t wide_msg[2048];
81 MultiByteToWideChar(CP_UTF8,
83 msg,
84 -1,
85 wide_msg,
86 _countof(wide_msg));
87 #if MOZ_WINCONSOLE
88 fwprintf_s(stderr, wide_msg);
89 #else
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");
93 if (user32) {
94 decltype(MessageBoxW)* messageBoxW =
95 (decltype(MessageBoxW)*) GetProcAddress(user32, "MessageBoxW");
96 if (messageBoxW) {
97 messageBoxW(nullptr, wide_msg, L"Firefox", MB_OK
98 | MB_ICONERROR
99 | MB_SETFOREGROUND);
101 FreeLibrary(user32);
103 #endif
104 #endif
106 va_end(ap);
110 * Return true if |arg| matches the given argument name.
112 static bool IsArg(const char* arg, const char* s)
114 if (*arg == '-')
116 if (*++arg == '-')
117 ++arg;
118 return !strcasecmp(arg, s);
121 #if defined(XP_WIN)
122 if (*arg == '/')
123 return !strcasecmp(++arg, s);
124 #endif
126 return false;
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
137 * stdout.
139 static void AttachToTestHarness()
141 // attach to the metrotestharness named logging pipe
142 HANDLE winOut = CreateFileA("\\\\.\\pipe\\metrotestharness",
143 GENERIC_WRITE,
144 FILE_SHARE_WRITE, 0,
145 OPEN_EXISTING, 0, 0);
147 if (winOut == INVALID_HANDLE_VALUE) {
148 OutputDebugStringW(L"Could not create named logging pipe.\n");
149 return;
152 // Set the c runtime handle
153 int stdOut = _open_osfhandle((intptr_t)winOut, _O_APPEND);
154 if (stdOut == -1) {
155 OutputDebugStringW(L"Could not open c-runtime handle.\n");
156 return;
158 FILE *fp = _fdopen(stdOut, "a");
159 *stdout = *fp;
161 #endif
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 },
179 { nullptr, nullptr }
182 static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
184 nsCOMPtr<nsIFile> appini;
185 nsresult rv;
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));
193 if (NS_FAILED(rv)) {
194 Output("Invalid path found: '%s'", appDataFile);
195 return 255;
198 else if (argc > 1 && IsArg(argv[1], "app")) {
199 if (argc == 2) {
200 Output("Incorrect number of arguments passed to -app");
201 return 255;
204 rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(appini));
205 if (NS_FAILED(rv)) {
206 Output("application.ini path not recognized: '%s'", argv[2]);
207 return 255;
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);
214 return 255;
216 argv[2] = argv[0];
217 argv += 2;
218 argc -= 2;
221 if (appini) {
222 nsXREAppData *appData;
223 rv = XRE_CreateAppData(appini, &appData);
224 if (NS_FAILED(rv)) {
225 Output("Couldn't read application.ini");
226 return 255;
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);
232 return result;
235 bool metroOnDesktop = false;
237 #ifdef MOZ_METRO
238 if (argc > 1) {
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;
244 argv[1] = argv[0];
245 argv++;
246 argc--;
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;
251 } else {
252 #ifndef RELEASE_BUILD
253 // This command-line flag is used to test the metro browser in a desktop
254 // environment.
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";
260 putenv(crashSwitch);
261 break;
264 #endif
267 #endif
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));
274 if (NS_FAILED(rv)) {
275 Output("Couldn't find the application directory.\n");
276 return 255;
279 nsCOMPtr<nsIFile> greDir;
280 exeFile->GetParent(getter_AddRefs(greDir));
281 #ifdef XP_MACOSX
282 greDir->SetNativeLeafName(NS_LITERAL_CSTRING(kOSXResourcesFolder));
283 #endif
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
296 #ifdef MOZ_METRO
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));
307 nsAutoCString path;
308 if (NS_FAILED(iniFile->GetNativePath(path))) {
309 Output("Couldn't get ini file path.\n");
310 return 255;
313 nsXREAppData *appData;
314 rv = XRE_CreateAppData(iniFile, &appData);
315 if (NS_FAILED(rv) || !appData) {
316 Output("Couldn't read application.ini");
317 return 255;
320 SetStrongPtr(appData->directory, static_cast<nsIFile*>(appSubdir.get()));
321 // xreDirectory already has a refcount from NS_NewLocalFile
322 appData->xreDirectory = xreDirectory;
324 #ifdef XP_WIN
325 if (!metroOnDesktop) {
326 nsCOMPtr<nsIFile> testFile;
328 xreDirectory->Clone(getter_AddRefs(testFile));
329 testFile->Append(NS_LITERAL_STRING(kMetroTestFile));
331 nsAutoCString path;
332 if (NS_FAILED(testFile->GetNativePath(path))) {
333 Output("Couldn't get test file path.\n");
334 return 255;
337 // Check for a metro test harness command line args file
338 HANDLE hTestFile = CreateFileA(path.get(),
339 GENERIC_READ,
340 0, nullptr, OPEN_EXISTING,
341 FILE_ATTRIBUTE_NORMAL,
342 nullptr);
343 if (hTestFile != INVALID_HANDLE_VALUE) {
344 // Typical test harness command line args string is around 100 bytes.
345 char buffer[1024];
346 memset(buffer, 0, sizeof(buffer));
347 DWORD bytesRead = 0;
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);
352 return -1;
354 CloseHandle(hTestFile);
356 // Build new args array
357 char* newArgv[20];
358 int newArgc = 1;
360 memset(newArgv, 0, sizeof(newArgv));
362 char* ptr = buffer;
363 newArgv[0] = ptr;
364 while (*ptr != '\0' &&
365 (ptr - buffer) < sizeof(buffer) &&
366 newArgc < ARRAYSIZE(newArgv)) {
367 if (isspace(*ptr)) {
368 *ptr = '\0';
369 ptr++;
370 newArgv[newArgc] = ptr;
371 newArgc++;
372 continue;
374 ptr++;
376 if (ptr == newArgv[newArgc-1])
377 newArgc--;
379 // attach browser stdout to metrotestharness stdout
380 AttachToTestHarness();
382 int result = XRE_main(newArgc, newArgv, appData, mainFlags);
383 XRE_FreeAppData(appData);
384 return result;
387 #endif
389 int result = XRE_main(argc, argv, appData, mainFlags);
390 XRE_FreeAppData(appData);
391 return result;
392 #endif
394 NS_NOTREACHED("browser do_main failed to pickup proper initialization");
395 return 255;
398 #ifdef XP_WIN
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
420 MozGetTickCount64()
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)))
427 ++sLastGTCRollover;
429 sLastGTCResult = GTC;
430 return (ULONGLONG)sLastGTCRollover << 32 | sLastGTCResult;
433 typedef ULONGLONG (WINAPI* GetTickCount64_t)();
434 static GetTickCount64_t sGetTickCount64 = nullptr;
436 #endif
439 * Local TimeStamp::Now()-compatible implementation used to record timestamps
440 * which will be passed to XRE_StartupTimelineRecord().
442 static uint64_t
443 TimeStamp_Now()
445 #ifdef XP_WIN
446 LARGE_INTEGER freq;
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)
463 struct timespec ts;
464 int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
466 if (rv != 0) {
467 return 0;
470 uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
471 return baseNs + (uint64_t)ts.tv_nsec;
472 #endif
475 static bool
476 FileExists(const char *path)
478 #ifdef XP_WIN
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;
483 #else
484 return access(path, R_OK) == 0;
485 #endif
488 #ifdef LIBXUL_SDK
489 # define XPCOM_PATH "xulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
490 #else
491 # define XPCOM_PATH XPCOM_DLL
492 #endif
493 static nsresult
494 InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
496 char exePath[MAXPATHLEN];
498 nsresult rv = mozilla::BinaryPath::Get(argv0, exePath);
499 if (NS_FAILED(rv)) {
500 Output("Couldn't find the application directory.\n");
501 return rv;
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();
516 if (!appBundle)
517 return NS_ERROR_FAILURE;
518 CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle);
519 CFURLRef absfwurl = nullptr;
520 if (fwurl) {
521 absfwurl = CFURLCopyAbsoluteURL(fwurl);
522 CFRelease(fwurl);
524 if (absfwurl) {
525 CFURLRef xulurl =
526 CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl,
527 CFSTR("XUL.framework"),
528 true);
530 if (xulurl) {
531 CFURLRef xpcomurl =
532 CFURLCreateCopyAppendingPathComponent(nullptr, xulurl,
533 CFSTR("libxpcom.dylib"),
534 false);
536 if (xpcomurl) {
537 if (CFURLGetFileSystemRepresentation(xpcomurl, true,
538 (UInt8*) exePath,
539 sizeof(exePath)) &&
540 access(tbuffer, R_OK | X_OK) == 0) {
541 if (realpath(tbuffer, exePath)) {
542 greFound = true;
545 CFRelease(xpcomurl);
547 CFRelease(xulurl);
549 CFRelease(absfwurl);
552 if (!greFound) {
553 #endif
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);
562 if (NS_FAILED(rv)) {
563 Output("Couldn't load XPCOM.\n");
564 return rv;
567 rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
568 if (NS_FAILED(rv)) {
569 Output("Couldn't load XRE functions.\n");
570 return rv;
573 #ifndef MOZ_METRO
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.
576 NS_LogInit();
577 #endif
579 // chop XPCOM_DLL off exePath
580 *lastSlash = '\0';
581 #ifdef XP_MACOSX
582 lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
583 strcpy(lastSlash + 1, kOSXResourcesFolder);
584 #endif
585 #ifdef XP_WIN
586 rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), false,
587 xreDirectory);
588 #else
589 rv = NS_NewNativeLocalFile(nsDependentCString(exePath), false,
590 xreDirectory);
591 #endif
593 return rv;
596 int main(int argc, char* argv[])
598 #ifdef DEBUG_delay_start_metro
599 Sleep(5000);
600 #endif
601 uint64_t start = TimeStamp_Now();
603 #ifdef XP_MACOSX
604 TriggerQuirks();
605 #endif
607 int gotCounters;
608 #if defined(XP_UNIX)
609 struct rusage initialRUsage;
610 gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
611 #elif defined(XP_WIN)
612 IO_COUNTERS ioCounters;
613 gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
614 #endif
616 nsIFile *xreDirectory;
618 #ifdef HAS_DLL_BLOCKLIST
619 DllBlocklist_Initialize();
621 #ifdef DEBUG
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");
627 #endif
628 #endif
630 nsresult rv = InitXPCOMGlue(argv[0], &xreDirectory);
631 if (NS_FAILED(rv)) {
632 return 255;
635 XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
637 if (gotCounters) {
638 #if defined(XP_WIN)
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));
658 #endif
661 int result = do_main(argc, argv, xreDirectory);
663 NS_LogTerm();
665 #ifdef XP_MACOSX
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
670 // of _exit.
671 XRE_StopLateWriteChecks();
672 #endif
674 return result;