browser/app/nsBrowserApp.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:bee21e416856
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/. */
5
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
20
21 #ifdef XP_MACOSX
22 #include <mach/mach_time.h>
23 #include "MacQuirks.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <time.h>
29
30 #include "nsCOMPtr.h"
31 #include "nsIFile.h"
32 #include "nsStringGlue.h"
33
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
37
38 #ifdef XP_WIN
39 // we want a wmain entry point
40 #include "nsWindowsWMain.cpp"
41 #define snprintf _snprintf
42 #define strcasecmp _stricmp
43 #endif
44 #include "BinaryPath.h"
45
46 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
47
48 #include "mozilla/Telemetry.h"
49 #include "mozilla/WindowsDllBlocklist.h"
50
51 using namespace mozilla;
52
53 #define kDesktopFolder "browser"
54 #define kMetroFolder "metro"
55 #define kMetroAppIniFilename "metroapp.ini"
56 #ifdef XP_WIN
57 #define kMetroTestFile "tests.ini"
58 const char* kMetroConsoleIdParam = "testconsoleid=";
59 #endif
60
61 static void Output(const char *fmt, ... )
62 {
63 va_list ap;
64 va_start(ap, fmt);
65
66 #ifndef XP_WIN
67 vfprintf(stderr, fmt, ap);
68 #else
69 char msg[2048];
70 vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
71
72 wchar_t wide_msg[2048];
73 MultiByteToWideChar(CP_UTF8,
74 0,
75 msg,
76 -1,
77 wide_msg,
78 _countof(wide_msg));
79 #if MOZ_WINCONSOLE
80 fwprintf_s(stderr, wide_msg);
81 #else
82 // Linking user32 at load-time interferes with the DLL blocklist (bug 932100).
83 // This is a rare codepath, so we can load user32 at run-time instead.
84 HMODULE user32 = LoadLibraryW(L"user32.dll");
85 if (user32) {
86 decltype(MessageBoxW)* messageBoxW =
87 (decltype(MessageBoxW)*) GetProcAddress(user32, "MessageBoxW");
88 if (messageBoxW) {
89 messageBoxW(nullptr, wide_msg, L"Firefox", MB_OK
90 | MB_ICONERROR
91 | MB_SETFOREGROUND);
92 }
93 FreeLibrary(user32);
94 }
95 #endif
96 #endif
97
98 va_end(ap);
99 }
100
101 /**
102 * Return true if |arg| matches the given argument name.
103 */
104 static bool IsArg(const char* arg, const char* s)
105 {
106 if (*arg == '-')
107 {
108 if (*++arg == '-')
109 ++arg;
110 return !strcasecmp(arg, s);
111 }
112
113 #if defined(XP_WIN)
114 if (*arg == '/')
115 return !strcasecmp(++arg, s);
116 #endif
117
118 return false;
119 }
120
121 #ifdef XP_WIN
122 /*
123 * AttachToTestHarness - Windows helper for when we are running
124 * in the immersive environment. Firefox is launched by Windows in
125 * response to a request by metrotestharness, which is launched by
126 * runtests.py. As such stdout in fx doesn't point to the right
127 * stream. This helper touches up stdout such that test output gets
128 * routed to a named pipe metrotestharness creates and dumps to its
129 * stdout.
130 */
131 static void AttachToTestHarness()
132 {
133 // attach to the metrotestharness named logging pipe
134 HANDLE winOut = CreateFileA("\\\\.\\pipe\\metrotestharness",
135 GENERIC_WRITE,
136 FILE_SHARE_WRITE, 0,
137 OPEN_EXISTING, 0, 0);
138
139 if (winOut == INVALID_HANDLE_VALUE) {
140 OutputDebugStringW(L"Could not create named logging pipe.\n");
141 return;
142 }
143
144 // Set the c runtime handle
145 int stdOut = _open_osfhandle((intptr_t)winOut, _O_APPEND);
146 if (stdOut == -1) {
147 OutputDebugStringW(L"Could not open c-runtime handle.\n");
148 return;
149 }
150 FILE *fp = _fdopen(stdOut, "a");
151 *stdout = *fp;
152 }
153 #endif
154
155 XRE_GetFileFromPathType XRE_GetFileFromPath;
156 XRE_CreateAppDataType XRE_CreateAppData;
157 XRE_FreeAppDataType XRE_FreeAppData;
158 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
159 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
160 XRE_mainType XRE_main;
161 XRE_StopLateWriteChecksType XRE_StopLateWriteChecks;
162
163 static const nsDynamicFunctionLoad kXULFuncs[] = {
164 { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
165 { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
166 { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
167 { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
168 { "XRE_StartupTimelineRecord", (NSFuncPtr*) &XRE_StartupTimelineRecord },
169 { "XRE_main", (NSFuncPtr*) &XRE_main },
170 { "XRE_StopLateWriteChecks", (NSFuncPtr*) &XRE_StopLateWriteChecks },
171 { nullptr, nullptr }
172 };
173
174 static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
175 {
176 nsCOMPtr<nsIFile> appini;
177 nsresult rv;
178 uint32_t mainFlags = 0;
179
180 // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
181 // Note that -app must be the *first* argument.
182 const char *appDataFile = getenv("XUL_APP_FILE");
183 if (appDataFile && *appDataFile) {
184 rv = XRE_GetFileFromPath(appDataFile, getter_AddRefs(appini));
185 if (NS_FAILED(rv)) {
186 Output("Invalid path found: '%s'", appDataFile);
187 return 255;
188 }
189 }
190 else if (argc > 1 && IsArg(argv[1], "app")) {
191 if (argc == 2) {
192 Output("Incorrect number of arguments passed to -app");
193 return 255;
194 }
195
196 rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(appini));
197 if (NS_FAILED(rv)) {
198 Output("application.ini path not recognized: '%s'", argv[2]);
199 return 255;
200 }
201
202 char appEnv[MAXPATHLEN];
203 snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
204 if (putenv(appEnv)) {
205 Output("Couldn't set %s.\n", appEnv);
206 return 255;
207 }
208 argv[2] = argv[0];
209 argv += 2;
210 argc -= 2;
211 }
212
213 if (appini) {
214 nsXREAppData *appData;
215 rv = XRE_CreateAppData(appini, &appData);
216 if (NS_FAILED(rv)) {
217 Output("Couldn't read application.ini");
218 return 255;
219 }
220 // xreDirectory already has a refcount from NS_NewLocalFile
221 appData->xreDirectory = xreDirectory;
222 int result = XRE_main(argc, argv, appData, mainFlags);
223 XRE_FreeAppData(appData);
224 return result;
225 }
226
227 bool metroOnDesktop = false;
228
229 #ifdef MOZ_METRO
230 if (argc > 1) {
231 // This command-line flag is passed to our executable when it is to be
232 // launched in metro mode (i.e. our EXE is registered as the default
233 // browser and the user has tapped our EXE's tile)
234 if (IsArg(argv[1], "ServerName:DefaultBrowserServer")) {
235 mainFlags = XRE_MAIN_FLAG_USE_METRO;
236 argv[1] = argv[0];
237 argv++;
238 argc--;
239 } else if (IsArg(argv[1], "BackgroundSessionClosed")) {
240 // This command line flag is used for indirect shutdowns, the OS
241 // relaunches Metro Firefox with this command line arg.
242 mainFlags = XRE_MAIN_FLAG_USE_METRO;
243 } else {
244 #ifndef RELEASE_BUILD
245 // This command-line flag is used to test the metro browser in a desktop
246 // environment.
247 for (int idx = 1; idx < argc; idx++) {
248 if (IsArg(argv[idx], "metrodesktop")) {
249 metroOnDesktop = true;
250 // Disable crash reporting when running in metrodesktop mode.
251 char crashSwitch[] = "MOZ_CRASHREPORTER_DISABLE=1";
252 putenv(crashSwitch);
253 break;
254 }
255 }
256 #endif
257 }
258 }
259 #endif
260
261 // Desktop browser launch
262 if (mainFlags != XRE_MAIN_FLAG_USE_METRO && !metroOnDesktop) {
263 ScopedAppData appData(&sAppData);
264 nsCOMPtr<nsIFile> exeFile;
265 rv = mozilla::BinaryPath::GetFile(argv[0], getter_AddRefs(exeFile));
266 if (NS_FAILED(rv)) {
267 Output("Couldn't find the application directory.\n");
268 return 255;
269 }
270
271 nsCOMPtr<nsIFile> greDir;
272 exeFile->GetParent(getter_AddRefs(greDir));
273
274 nsCOMPtr<nsIFile> appSubdir;
275 greDir->Clone(getter_AddRefs(appSubdir));
276 appSubdir->Append(NS_LITERAL_STRING(kDesktopFolder));
277
278 SetStrongPtr(appData.directory, static_cast<nsIFile*>(appSubdir.get()));
279 // xreDirectory already has a refcount from NS_NewLocalFile
280 appData.xreDirectory = xreDirectory;
281
282 return XRE_main(argc, argv, &appData, mainFlags);
283 }
284
285 // Metro browser launch
286 #ifdef MOZ_METRO
287 nsCOMPtr<nsIFile> iniFile, appSubdir;
288
289 xreDirectory->Clone(getter_AddRefs(iniFile));
290 xreDirectory->Clone(getter_AddRefs(appSubdir));
291
292 iniFile->Append(NS_LITERAL_STRING(kMetroFolder));
293 iniFile->Append(NS_LITERAL_STRING(kMetroAppIniFilename));
294
295 appSubdir->Append(NS_LITERAL_STRING(kMetroFolder));
296
297 nsAutoCString path;
298 if (NS_FAILED(iniFile->GetNativePath(path))) {
299 Output("Couldn't get ini file path.\n");
300 return 255;
301 }
302
303 nsXREAppData *appData;
304 rv = XRE_CreateAppData(iniFile, &appData);
305 if (NS_FAILED(rv) || !appData) {
306 Output("Couldn't read application.ini");
307 return 255;
308 }
309
310 SetStrongPtr(appData->directory, static_cast<nsIFile*>(appSubdir.get()));
311 // xreDirectory already has a refcount from NS_NewLocalFile
312 appData->xreDirectory = xreDirectory;
313
314 #ifdef XP_WIN
315 if (!metroOnDesktop) {
316 nsCOMPtr<nsIFile> testFile;
317
318 xreDirectory->Clone(getter_AddRefs(testFile));
319 testFile->Append(NS_LITERAL_STRING(kMetroTestFile));
320
321 nsAutoCString path;
322 if (NS_FAILED(testFile->GetNativePath(path))) {
323 Output("Couldn't get test file path.\n");
324 return 255;
325 }
326
327 // Check for a metro test harness command line args file
328 HANDLE hTestFile = CreateFileA(path.get(),
329 GENERIC_READ,
330 0, nullptr, OPEN_EXISTING,
331 FILE_ATTRIBUTE_NORMAL,
332 nullptr);
333 if (hTestFile != INVALID_HANDLE_VALUE) {
334 // Typical test harness command line args string is around 100 bytes.
335 char buffer[1024];
336 memset(buffer, 0, sizeof(buffer));
337 DWORD bytesRead = 0;
338 if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1,
339 &bytesRead, nullptr) || !bytesRead) {
340 CloseHandle(hTestFile);
341 printf("failed to read test file '%s'", testFile);
342 return -1;
343 }
344 CloseHandle(hTestFile);
345
346 // Build new args array
347 char* newArgv[20];
348 int newArgc = 1;
349
350 memset(newArgv, 0, sizeof(newArgv));
351
352 char* ptr = buffer;
353 newArgv[0] = ptr;
354 while (*ptr != '\0' &&
355 (ptr - buffer) < sizeof(buffer) &&
356 newArgc < ARRAYSIZE(newArgv)) {
357 if (isspace(*ptr)) {
358 *ptr = '\0';
359 ptr++;
360 newArgv[newArgc] = ptr;
361 newArgc++;
362 continue;
363 }
364 ptr++;
365 }
366 if (ptr == newArgv[newArgc-1])
367 newArgc--;
368
369 // attach browser stdout to metrotestharness stdout
370 AttachToTestHarness();
371
372 int result = XRE_main(newArgc, newArgv, appData, mainFlags);
373 XRE_FreeAppData(appData);
374 return result;
375 }
376 }
377 #endif
378
379 int result = XRE_main(argc, argv, appData, mainFlags);
380 XRE_FreeAppData(appData);
381 return result;
382 #endif
383
384 NS_NOTREACHED("browser do_main failed to pickup proper initialization");
385 return 255;
386 }
387
388 #ifdef XP_WIN
389
390 /**
391 * Used only when GetTickCount64 is not available on the platform.
392 * Last result of GetTickCount call. Kept in [ms].
393 */
394 static DWORD sLastGTCResult = 0;
395
396 /**
397 * Higher part of the 64-bit value of MozGetTickCount64,
398 * incremented atomically.
399 */
400 static DWORD sLastGTCRollover = 0;
401
402 /**
403 * Function protecting GetTickCount result from rolling over. The original
404 * code comes from the Windows implementation of the TimeStamp class minus the
405 * locking harness which isn't needed here.
406 *
407 * @returns The current time in milliseconds
408 */
409 static ULONGLONG WINAPI
410 MozGetTickCount64()
411 {
412 DWORD GTC = ::GetTickCount();
413
414 /* Pull the rollover counter forward only if new value of GTC goes way
415 * down under the last saved result */
416 if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30)))
417 ++sLastGTCRollover;
418
419 sLastGTCResult = GTC;
420 return (ULONGLONG)sLastGTCRollover << 32 | sLastGTCResult;
421 }
422
423 typedef ULONGLONG (WINAPI* GetTickCount64_t)();
424 static GetTickCount64_t sGetTickCount64 = nullptr;
425
426 #endif
427
428 /**
429 * Local TimeStamp::Now()-compatible implementation used to record timestamps
430 * which will be passed to XRE_StartupTimelineRecord().
431 */
432 static uint64_t
433 TimeStamp_Now()
434 {
435 #ifdef XP_WIN
436 LARGE_INTEGER freq;
437 ::QueryPerformanceFrequency(&freq);
438
439 HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
440 sGetTickCount64 = reinterpret_cast<GetTickCount64_t>
441 (GetProcAddress(kernelDLL, "GetTickCount64"));
442
443 if (!sGetTickCount64) {
444 /* If the platform does not support the GetTickCount64 (Windows XP doesn't),
445 * then use our fallback implementation based on GetTickCount. */
446 sGetTickCount64 = MozGetTickCount64;
447 }
448
449 return sGetTickCount64() * freq.QuadPart;
450 #elif defined(XP_MACOSX)
451 return mach_absolute_time();
452 #elif defined(HAVE_CLOCK_MONOTONIC)
453 struct timespec ts;
454 int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
455
456 if (rv != 0) {
457 return 0;
458 }
459
460 uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
461 return baseNs + (uint64_t)ts.tv_nsec;
462 #endif
463 }
464
465 static bool
466 FileExists(const char *path)
467 {
468 #ifdef XP_WIN
469 wchar_t wideDir[MAX_PATH];
470 MultiByteToWideChar(CP_UTF8, 0, path, -1, wideDir, MAX_PATH);
471 DWORD fileAttrs = GetFileAttributesW(wideDir);
472 return fileAttrs != INVALID_FILE_ATTRIBUTES;
473 #else
474 return access(path, R_OK) == 0;
475 #endif
476 }
477
478 #ifdef LIBXUL_SDK
479 # define XPCOM_PATH "xulrunner" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
480 #else
481 # define XPCOM_PATH XPCOM_DLL
482 #endif
483 static nsresult
484 InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
485 {
486 char exePath[MAXPATHLEN];
487
488 nsresult rv = mozilla::BinaryPath::Get(argv0, exePath);
489 if (NS_FAILED(rv)) {
490 Output("Couldn't find the application directory.\n");
491 return rv;
492 }
493
494 char *lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
495 if (!lastSlash || (size_t(lastSlash - exePath) > MAXPATHLEN - sizeof(XPCOM_PATH) - 1))
496 return NS_ERROR_FAILURE;
497
498 strcpy(lastSlash + 1, XPCOM_PATH);
499 lastSlash += sizeof(XPCOM_PATH) - sizeof(XPCOM_DLL);
500
501 if (!FileExists(exePath)) {
502 #if defined(LIBXUL_SDK) && defined(XP_MACOSX)
503 // Check for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
504 bool greFound = false;
505 CFBundleRef appBundle = CFBundleGetMainBundle();
506 if (!appBundle)
507 return NS_ERROR_FAILURE;
508 CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle);
509 CFURLRef absfwurl = nullptr;
510 if (fwurl) {
511 absfwurl = CFURLCopyAbsoluteURL(fwurl);
512 CFRelease(fwurl);
513 }
514 if (absfwurl) {
515 CFURLRef xulurl =
516 CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl,
517 CFSTR("XUL.framework"),
518 true);
519
520 if (xulurl) {
521 CFURLRef xpcomurl =
522 CFURLCreateCopyAppendingPathComponent(nullptr, xulurl,
523 CFSTR("libxpcom.dylib"),
524 false);
525
526 if (xpcomurl) {
527 if (CFURLGetFileSystemRepresentation(xpcomurl, true,
528 (UInt8*) exePath,
529 sizeof(exePath)) &&
530 access(tbuffer, R_OK | X_OK) == 0) {
531 if (realpath(tbuffer, exePath)) {
532 greFound = true;
533 }
534 }
535 CFRelease(xpcomurl);
536 }
537 CFRelease(xulurl);
538 }
539 CFRelease(absfwurl);
540 }
541 }
542 if (!greFound) {
543 #endif
544 Output("Could not find the Mozilla runtime.\n");
545 return NS_ERROR_FAILURE;
546 }
547
548 // We do this because of data in bug 771745
549 XPCOMGlueEnablePreload();
550
551 rv = XPCOMGlueStartup(exePath);
552 if (NS_FAILED(rv)) {
553 Output("Couldn't load XPCOM.\n");
554 return rv;
555 }
556
557 rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
558 if (NS_FAILED(rv)) {
559 Output("Couldn't load XRE functions.\n");
560 return rv;
561 }
562
563 NS_LogInit();
564
565 // chop XPCOM_DLL off exePath
566 *lastSlash = '\0';
567 #ifdef XP_WIN
568 rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), false,
569 xreDirectory);
570 #else
571 rv = NS_NewNativeLocalFile(nsDependentCString(exePath), false,
572 xreDirectory);
573 #endif
574
575 return rv;
576 }
577
578 int main(int argc, char* argv[])
579 {
580 #ifdef DEBUG_delay_start_metro
581 Sleep(5000);
582 #endif
583 uint64_t start = TimeStamp_Now();
584
585 #ifdef XP_MACOSX
586 TriggerQuirks();
587 #endif
588
589 int gotCounters;
590 #if defined(XP_UNIX)
591 struct rusage initialRUsage;
592 gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
593 #elif defined(XP_WIN)
594 IO_COUNTERS ioCounters;
595 gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
596 #endif
597
598 nsIFile *xreDirectory;
599
600 #ifdef HAS_DLL_BLOCKLIST
601 DllBlocklist_Initialize();
602
603 #ifdef DEBUG
604 // In order to be effective against AppInit DLLs, the blocklist must be
605 // initialized before user32.dll is loaded into the process (bug 932100).
606 if (GetModuleHandleA("user32.dll")) {
607 fprintf(stderr, "DLL blocklist was unable to intercept AppInit DLLs.\n");
608 }
609 #endif
610 #endif
611
612 nsresult rv = InitXPCOMGlue(argv[0], &xreDirectory);
613 if (NS_FAILED(rv)) {
614 return 255;
615 }
616
617 XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
618
619 if (gotCounters) {
620 #if defined(XP_WIN)
621 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS,
622 int(ioCounters.ReadOperationCount));
623 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER,
624 int(ioCounters.ReadTransferCount / 1024));
625 IO_COUNTERS newIoCounters;
626 if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) {
627 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS,
628 int(newIoCounters.ReadOperationCount - ioCounters.ReadOperationCount));
629 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER,
630 int((newIoCounters.ReadTransferCount - ioCounters.ReadTransferCount) / 1024));
631 }
632 #elif defined(XP_UNIX)
633 XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS,
634 int(initialRUsage.ru_majflt));
635 struct rusage newRUsage;
636 if (!getrusage(RUSAGE_SELF, &newRUsage)) {
637 XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS,
638 int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
639 }
640 #endif
641 }
642
643 int result = do_main(argc, argv, xreDirectory);
644
645 NS_LogTerm();
646
647 #ifdef XP_MACOSX
648 // Allow writes again. While we would like to catch writes from static
649 // destructors to allow early exits to use _exit, we know that there is
650 // at least one such write that we don't control (see bug 826029). For
651 // now we enable writes again and early exits will have to use exit instead
652 // of _exit.
653 XRE_StopLateWriteChecks();
654 #endif
655
656 return result;
657 }

mercurial