mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
d44f201e8f
Differential Revision: https://phabricator.services.mozilla.com/D160586
455 lines
14 KiB
C++
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;
|
|
}
|