gecko-dev/browser/app/nsBrowserApp.cpp

455 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsXULAppAPI.h"
#include "mozilla/XREAppData.h"
#include "XREShellData.h"
#include "application.ini.h"
#include "mozilla/Bootstrap.h"
#include "mozilla/ProcessType.h"
#include "mozilla/RuntimeExceptionModule.h"
#include "mozilla/ScopeExit.h"
#include "BrowserDefines.h"
#if defined(XP_WIN)
# include <windows.h>
# include <stdlib.h>
#elif defined(XP_UNIX)
# include <sys/resource.h>
# include <unistd.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include "nsCOMPtr.h"
#ifdef XP_WIN
# include "mozilla/PreXULSkeletonUI.h"
# include "freestanding/SharedSection.h"
# include "LauncherProcessWin.h"
# include "mozilla/GeckoArgs.h"
# include "mozilla/mscom/ProcessRuntime.h"
# include "mozilla/WindowsDllBlocklist.h"
# include "mozilla/WindowsDpiInitialization.h"
# include "mozilla/WindowsProcessMitigations.h"
# define XRE_WANT_ENVIRON
# define strcasecmp _stricmp
# ifdef MOZ_SANDBOX
# include "mozilla/sandboxing/SandboxInitialization.h"
# endif
#endif
#include "BinaryPath.h"
#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
#include "mozilla/Sprintf.h"
#include "mozilla/StartupTimeline.h"
#include "BaseProfiler.h"
#ifdef LIBFUZZER
# include "FuzzerDefs.h"
#endif
#ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
# include <cpuid.h>
# include "mozilla/Unused.h"
static bool IsSSE2Available() {
// The rest of the app has been compiled to assume that SSE2 is present
// unconditionally, so we can't use the normal copy of SSE.cpp here.
// Since SSE.cpp caches the results and we need them only transiently,
// instead of #including SSE.cpp here, let's just inline the specific check
// that's needed.
unsigned int level = 1u;
unsigned int eax, ebx, ecx, edx;
unsigned int bits = (1u << 26);
unsigned int max = __get_cpuid_max(0, nullptr);
if (level > max) {
return false;
}
__cpuid_count(level, 0, eax, ebx, ecx, edx);
return (edx & bits) == bits;
}
static const char sSSE2Message[] =
"This browser version requires a processor with the SSE2 instruction "
"set extension.\nYou may be able to obtain a version that does not "
"require SSE2 from your Linux distribution.\n";
__attribute__((constructor)) static void SSE2Check() {
if (IsSSE2Available()) {
return;
}
// Using write() in order to avoid jemalloc-based buffering. Ignoring return
// values, since there isn't much we could do on failure and there is no
// point in trying to recover from errors.
MOZ_UNUSED(
write(STDERR_FILENO, sSSE2Message, MOZ_ARRAY_LENGTH(sSSE2Message) - 1));
// _exit() instead of exit() to avoid running the usual "at exit" code.
_exit(255);
}
#endif
#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID)
# define MOZ_BROWSER_CAN_BE_CONTENTPROC
# include "../../ipc/contentproc/plugin-container.cpp"
#endif
using namespace mozilla;
#ifdef XP_MACOSX
# define kOSXResourcesFolder "Resources"
#endif
#define kDesktopFolder "browser"
static MOZ_FORMAT_PRINTF(1, 2) void Output(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
#ifndef XP_WIN
vfprintf(stderr, fmt, ap);
#else
char msg[2048];
vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
wchar_t wide_msg[2048];
MultiByteToWideChar(CP_UTF8, 0, msg, -1, wide_msg, _countof(wide_msg));
# if MOZ_WINCONSOLE
fwprintf_s(stderr, wide_msg);
# else
// Linking user32 at load-time interferes with the DLL blocklist (bug 932100).
// This is a rare codepath, so we can load user32 at run-time instead.
HMODULE user32 = LoadLibraryW(L"user32.dll");
if (user32) {
decltype(MessageBoxW)* messageBoxW =
(decltype(MessageBoxW)*)GetProcAddress(user32, "MessageBoxW");
if (messageBoxW) {
messageBoxW(nullptr, wide_msg, L"Firefox",
MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
}
FreeLibrary(user32);
}
# endif
#endif
va_end(ap);
}
/**
* Return true if |arg| matches the given argument name.
*/
static bool IsArg(const char* arg, const char* s) {
if (*arg == '-') {
if (*++arg == '-') ++arg;
return !strcasecmp(arg, s);
}
#if defined(XP_WIN)
if (*arg == '/') return !strcasecmp(++arg, s);
#endif
return false;
}
Bootstrap::UniquePtr gBootstrap;
static int do_main(int argc, char* argv[], char* envp[]) {
// Allow firefox.exe to launch XULRunner apps via -app <application.ini>
// Note that -app must be the *first* argument.
const char* appDataFile = getenv("XUL_APP_FILE");
if ((!appDataFile || !*appDataFile) && (argc > 1 && IsArg(argv[1], "app"))) {
if (argc == 2) {
Output("Incorrect number of arguments passed to -app");
return 255;
}
appDataFile = argv[2];
char appEnv[MAXPATHLEN];
SprintfLiteral(appEnv, "XUL_APP_FILE=%s", argv[2]);
if (putenv(strdup(appEnv))) {
Output("Couldn't set %s.\n", appEnv);
return 255;
}
argv[2] = argv[0];
argv += 2;
argc -= 2;
} else if (argc > 1 && IsArg(argv[1], "xpcshell")) {
for (int i = 1; i < argc; i++) {
argv[i] = argv[i + 1];
}
XREShellData shellData;
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
shellData.sandboxBrokerServices =
sandboxing::GetInitializedBrokerServices();
#endif
#ifdef LIBFUZZER
shellData.fuzzerDriver = fuzzer::FuzzerDriver;
#endif
return gBootstrap->XRE_XPCShellMain(--argc, argv, envp, &shellData);
}
BootstrapConfig config;
if (appDataFile && *appDataFile) {
config.appData = nullptr;
config.appDataPath = appDataFile;
} else {
// no -app flag so we use the compiled-in app data
config.appData = &sAppData;
config.appDataPath = kDesktopFolder;
}
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
sandbox::BrokerServices* brokerServices =
sandboxing::GetInitializedBrokerServices();
if (!brokerServices) {
Output("Couldn't initialize the broker services.\n");
return 255;
}
config.sandboxBrokerServices = brokerServices;
#endif
#ifdef LIBFUZZER
if (getenv("FUZZER"))
gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
#endif
EnsureBrowserCommandlineSafe(argc, argv);
return gBootstrap->XRE_main(argc, argv, config);
}
static nsresult InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy) {
if (gBootstrap) {
return NS_OK;
}
UniqueFreePtr<char> exePath = BinaryPath::Get();
if (!exePath) {
Output("Couldn't find the application directory.\n");
return NS_ERROR_FAILURE;
}
auto bootstrapResult =
mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy);
if (bootstrapResult.isErr()) {
Output("Couldn't load XPCOM.\n");
return NS_ERROR_FAILURE;
}
gBootstrap = bootstrapResult.unwrap();
// This will set this thread as the main thread.
gBootstrap->NS_LogInit();
return NS_OK;
}
#ifdef HAS_DLL_BLOCKLIST
// NB: This must be extern, as this value is checked elsewhere
uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault;
#endif
int main(int argc, char* argv[], char* envp[]) {
#if defined(MOZ_ENABLE_FORKSERVER)
if (strcmp(argv[argc - 1], "forkserver") == 0) {
nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
if (NS_FAILED(rv)) {
return 255;
}
// Run a fork server in this process, single thread. When it
// returns, it means the fork server have been stopped or a new
// content process is created.
//
// For the later case, XRE_ForkServer() will return false, running
// in a content process just forked from the fork server process.
// argc & argv will be updated with the values passing from the
// chrome process. With the new values, this function
// continues the reset of the code acting as a content process.
if (gBootstrap->XRE_ForkServer(&argc, &argv)) {
// Return from the fork server in the fork server process.
// Stop the fork server.
gBootstrap->NS_LogTerm();
return 0;
}
// In a content process forked from the fork server.
// Start acting as a content process.
}
#endif
mozilla::TimeStamp start = mozilla::TimeStamp::Now();
AUTO_BASE_PROFILER_INIT;
AUTO_BASE_PROFILER_LABEL("nsBrowserApp main", OTHER);
// Make sure we unregister the runtime exception module before returning.
// We do this here to cover both registers for child and main processes.
auto unregisterRuntimeExceptionModule =
MakeScopeExit([] { CrashReporter::UnregisterRuntimeExceptionModule(); });
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
// We are launching as a content process, delegate to the appropriate
// main
if (argc > 1 && IsArg(argv[1], "contentproc")) {
// Set the process type. We don't remove the arg here as that will be done
// later in common code.
SetGeckoProcessType(argv[argc - 1]);
// Register an external module to report on otherwise uncatchable
// exceptions. Note that in child processes this must be called after Gecko
// process type has been set.
CrashReporter::RegisterRuntimeExceptionModule();
# ifdef HAS_DLL_BLOCKLIST
uint32_t initFlags =
gBlocklistInitFlags | eDllBlocklistInitFlagIsChildProcess;
// This is too early in launch to call XRE_IsUtilityProcess(), so roll
// our own.
if (GetGeckoProcessType() == GeckoProcessType_Utility) {
initFlags |= eDllBlocklistInitFlagIsUtilityProcess;
} else if (GetGeckoProcessType() == GeckoProcessType_Socket) {
initFlags |= eDllBlocklistInitFlagIsSocketProcess;
}
DllBlocklist_Initialize(initFlags);
# endif // HAS_DLL_BLOCKLIST
# if defined(XP_WIN) && defined(MOZ_SANDBOX)
// We need to set whether our process is supposed to have win32k locked down
// from the command line setting before GetInitializedTargetServices and
// WindowsDpiInitialization.
Maybe<bool> win32kLockedDown =
mozilla::geckoargs::sWin32kLockedDown.Get(argc, argv);
if (win32kLockedDown.isSome() && *win32kLockedDown) {
mozilla::SetWin32kLockedDownInPolicy();
}
// We need to initialize the sandbox TargetServices before InitXPCOMGlue
// because we might need the sandbox broker to give access to some files.
if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) {
Output("Failed to initialize the sandbox target services.");
return 255;
}
# endif
# if defined(XP_WIN)
// Ideally, we would be able to set our DPI awareness in
// firefox.exe.manifest Unfortunately, that would cause Win32k calls when
// user32.dll gets loaded, which would be incompatible with Win32k Lockdown
//
// MSDN says that it's allowed-but-not-recommended to initialize DPI
// programatically, as long as it's done before any HWNDs are created.
// Thus, we do it almost as soon as we possibly can
{
auto result = mozilla::WindowsDpiInitialization();
(void)result; // Ignore errors since some tools block DPI calls
}
# endif
nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
if (NS_FAILED(rv)) {
return 255;
}
int result = content_process_main(gBootstrap.get(), argc, argv);
# if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
# endif
// InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
gBootstrap->NS_LogTerm();
return result;
}
#endif
// Register an external module to report on otherwise uncatchable exceptions.
CrashReporter::RegisterRuntimeExceptionModule();
#ifdef HAS_DLL_BLOCKLIST
DllBlocklist_Initialize(gBlocklistInitFlags);
#endif
// We will likely only ever support this as a command line argument on Windows
// and OSX, so we're ifdefing here just to not create any expectations.
#if defined(XP_WIN) || defined(XP_MACOSX)
if (argc > 1 && IsArg(argv[1], "silentmode")) {
::putenv(const_cast<char*>("MOZ_APP_SILENT_START=1"));
# if defined(XP_WIN)
// On windows We also want to set a separate variable, which we want to
// persist across restarts, which will let us keep the process alive
// even if the last window is closed.
::putenv(const_cast<char*>("MOZ_APP_ALLOW_WINDOWLESS=1"));
# endif
# if defined(XP_MACOSX)
::putenv(const_cast<char*>("MOZ_APP_NO_DOCK=1"));
# endif
}
#endif
#if defined(XP_WIN)
// Ideally, we would be able to set our DPI awareness in firefox.exe.manifest
// Unfortunately, that would cause Win32k calls when user32.dll gets loaded,
// which would be incompatible with Win32k Lockdown
//
// MSDN says that it's allowed-but-not-recommended to initialize DPI
// programatically, as long as it's done before any HWNDs are created.
// Thus, we do it almost as soon as we possibly can
{
auto result = mozilla::WindowsDpiInitialization();
(void)result; // Ignore errors since some tools block DPI calls
}
// Once the browser process hits the main function, we no longer need
// a writable section handle because all dependent modules have been
// loaded.
mozilla::freestanding::gSharedSection.ConvertToReadOnly();
::RtlRunOnceInitialize(&mozilla::freestanding::gK32ExportsResolveOnce);
mozilla::CreateAndStorePreXULSkeletonUI(GetModuleHandle(nullptr), argc, argv);
#endif
nsresult rv = InitXPCOMGlue(LibLoadingStrategy::ReadAhead);
if (NS_FAILED(rv)) {
return 255;
}
gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
gBootstrap->XRE_EnableSameExecutableForContentProc();
#endif
int result = do_main(argc, argv, envp);
#if defined(XP_WIN)
CleanupProcessRuntime();
#endif
gBootstrap->NS_LogTerm();
#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
#endif
#ifdef XP_MACOSX
// Allow writes again. While we would like to catch writes from static
// destructors to allow early exits to use _exit, we know that there is
// at least one such write that we don't control (see bug 826029). For
// now we enable writes again and early exits will have to use exit instead
// of _exit.
gBootstrap->XRE_StopLateWriteChecks();
#endif
gBootstrap.reset();
return result;
}