Bug 1726269: part 1) Repeatedly call `::OleSetClipboard` for the Windows-specific...
[gecko.git] / browser / app / nsBrowserApp.cpp
blobc1b2a39d91d06e704cd903709c2eedc74c78d18b
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/CmdLineAndEnvUtils.h"
8 #include "mozilla/XREAppData.h"
9 #include "XREShellData.h"
10 #include "application.ini.h"
11 #include "mozilla/Bootstrap.h"
12 #if defined(XP_WIN)
13 # include <windows.h>
14 # include <stdlib.h>
15 #elif defined(XP_UNIX)
16 # include <sys/resource.h>
17 # include <unistd.h>
18 #endif
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <time.h>
24 #include "nsCOMPtr.h"
26 #ifdef XP_WIN
27 # include "mozilla/PreXULSkeletonUI.h"
28 # include "freestanding/SharedSection.h"
29 # include "LauncherProcessWin.h"
30 # include "mozilla/WindowsDllBlocklist.h"
31 # include "mozilla/WindowsDpiInitialization.h"
33 # define XRE_WANT_ENVIRON
34 # define strcasecmp _stricmp
35 # ifdef MOZ_SANDBOX
36 # include "mozilla/sandboxing/SandboxInitialization.h"
37 # endif
38 #endif
39 #include "BinaryPath.h"
41 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
43 #include "mozilla/Sprintf.h"
44 #include "mozilla/StartupTimeline.h"
45 #include "BaseProfiler.h"
47 #ifdef LIBFUZZER
48 # include "FuzzerDefs.h"
49 #endif
51 #ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
52 # include <cpuid.h>
53 # include "mozilla/Unused.h"
55 static bool IsSSE2Available() {
56 // The rest of the app has been compiled to assume that SSE2 is present
57 // unconditionally, so we can't use the normal copy of SSE.cpp here.
58 // Since SSE.cpp caches the results and we need them only transiently,
59 // instead of #including SSE.cpp here, let's just inline the specific check
60 // that's needed.
61 unsigned int level = 1u;
62 unsigned int eax, ebx, ecx, edx;
63 unsigned int bits = (1u << 26);
64 unsigned int max = __get_cpuid_max(0, nullptr);
65 if (level > max) {
66 return false;
68 __cpuid_count(level, 0, eax, ebx, ecx, edx);
69 return (edx & bits) == bits;
72 static const char sSSE2Message[] =
73 "This browser version requires a processor with the SSE2 instruction "
74 "set extension.\nYou may be able to obtain a version that does not "
75 "require SSE2 from your Linux distribution.\n";
77 __attribute__((constructor)) static void SSE2Check() {
78 if (IsSSE2Available()) {
79 return;
81 // Using write() in order to avoid jemalloc-based buffering. Ignoring return
82 // values, since there isn't much we could do on failure and there is no
83 // point in trying to recover from errors.
84 MOZ_UNUSED(
85 write(STDERR_FILENO, sSSE2Message, MOZ_ARRAY_LENGTH(sSSE2Message) - 1));
86 // _exit() instead of exit() to avoid running the usual "at exit" code.
87 _exit(255);
89 #endif
91 #if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID)
92 # define MOZ_BROWSER_CAN_BE_CONTENTPROC
93 # include "../../ipc/contentproc/plugin-container.cpp"
94 #endif
96 using namespace mozilla;
98 #ifdef XP_MACOSX
99 # define kOSXResourcesFolder "Resources"
100 #endif
101 #define kDesktopFolder "browser"
103 static MOZ_FORMAT_PRINTF(1, 2) void Output(const char* fmt, ...) {
104 va_list ap;
105 va_start(ap, fmt);
107 #ifndef XP_WIN
108 vfprintf(stderr, fmt, ap);
109 #else
110 char msg[2048];
111 vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
113 wchar_t wide_msg[2048];
114 MultiByteToWideChar(CP_UTF8, 0, msg, -1, wide_msg, _countof(wide_msg));
115 # if MOZ_WINCONSOLE
116 fwprintf_s(stderr, wide_msg);
117 # else
118 // Linking user32 at load-time interferes with the DLL blocklist (bug 932100).
119 // This is a rare codepath, so we can load user32 at run-time instead.
120 HMODULE user32 = LoadLibraryW(L"user32.dll");
121 if (user32) {
122 decltype(MessageBoxW)* messageBoxW =
123 (decltype(MessageBoxW)*)GetProcAddress(user32, "MessageBoxW");
124 if (messageBoxW) {
125 messageBoxW(nullptr, wide_msg, L"Firefox",
126 MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
128 FreeLibrary(user32);
130 # endif
131 #endif
133 va_end(ap);
137 * Return true if |arg| matches the given argument name.
139 static bool IsArg(const char* arg, const char* s) {
140 if (*arg == '-') {
141 if (*++arg == '-') ++arg;
142 return !strcasecmp(arg, s);
145 #if defined(XP_WIN)
146 if (*arg == '/') return !strcasecmp(++arg, s);
147 #endif
149 return false;
152 Bootstrap::UniquePtr gBootstrap;
154 static int do_main(int argc, char* argv[], char* envp[]) {
155 // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
156 // Note that -app must be the *first* argument.
157 const char* appDataFile = getenv("XUL_APP_FILE");
158 if ((!appDataFile || !*appDataFile) && (argc > 1 && IsArg(argv[1], "app"))) {
159 if (argc == 2) {
160 Output("Incorrect number of arguments passed to -app");
161 return 255;
163 appDataFile = argv[2];
165 char appEnv[MAXPATHLEN];
166 SprintfLiteral(appEnv, "XUL_APP_FILE=%s", argv[2]);
167 if (putenv(strdup(appEnv))) {
168 Output("Couldn't set %s.\n", appEnv);
169 return 255;
171 argv[2] = argv[0];
172 argv += 2;
173 argc -= 2;
174 } else if (argc > 1 && IsArg(argv[1], "xpcshell")) {
175 for (int i = 1; i < argc; i++) {
176 argv[i] = argv[i + 1];
179 XREShellData shellData;
180 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
181 shellData.sandboxBrokerServices =
182 sandboxing::GetInitializedBrokerServices();
183 #endif
185 #ifdef LIBFUZZER
186 shellData.fuzzerDriver = fuzzer::FuzzerDriver;
187 #endif
189 return gBootstrap->XRE_XPCShellMain(--argc, argv, envp, &shellData);
192 BootstrapConfig config;
194 if (appDataFile && *appDataFile) {
195 config.appData = nullptr;
196 config.appDataPath = appDataFile;
197 } else {
198 // no -app flag so we use the compiled-in app data
199 config.appData = &sAppData;
200 config.appDataPath = kDesktopFolder;
203 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
204 sandbox::BrokerServices* brokerServices =
205 sandboxing::GetInitializedBrokerServices();
206 sandboxing::PermissionsService* permissionsService =
207 sandboxing::GetPermissionsService();
208 if (!brokerServices) {
209 Output("Couldn't initialize the broker services.\n");
210 return 255;
212 config.sandboxBrokerServices = brokerServices;
213 config.sandboxPermissionsService = permissionsService;
214 #endif
216 #ifdef LIBFUZZER
217 if (getenv("FUZZER"))
218 gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
219 #endif
221 // Note: keep in sync with LauncherProcessWin.
222 const char* acceptableParams[] = {"url", nullptr};
223 EnsureCommandlineSafe(argc, argv, acceptableParams);
225 return gBootstrap->XRE_main(argc, argv, config);
228 static nsresult InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy) {
229 if (gBootstrap) {
230 return NS_OK;
233 UniqueFreePtr<char> exePath = BinaryPath::Get();
234 if (!exePath) {
235 Output("Couldn't find the application directory.\n");
236 return NS_ERROR_FAILURE;
239 auto bootstrapResult =
240 mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy);
241 if (bootstrapResult.isErr()) {
242 Output("Couldn't load XPCOM.\n");
243 return NS_ERROR_FAILURE;
246 gBootstrap = bootstrapResult.unwrap();
248 // This will set this thread as the main thread.
249 gBootstrap->NS_LogInit();
251 return NS_OK;
254 #ifdef HAS_DLL_BLOCKLIST
255 // NB: This must be extern, as this value is checked elsewhere
256 uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault;
257 #endif
259 int main(int argc, char* argv[], char* envp[]) {
260 #if defined(MOZ_ENABLE_FORKSERVER)
261 if (strcmp(argv[argc - 1], "forkserver") == 0) {
262 nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
263 if (NS_FAILED(rv)) {
264 return 255;
267 // Run a fork server in this process, single thread. When it
268 // returns, it means the fork server have been stopped or a new
269 // content process is created.
271 // For the later case, XRE_ForkServer() will return false, running
272 // in a content process just forked from the fork server process.
273 // argc & argv will be updated with the values passing from the
274 // chrome process. With the new values, this function
275 // continues the reset of the code acting as a content process.
276 if (gBootstrap->XRE_ForkServer(&argc, &argv)) {
277 // Return from the fork server in the fork server process.
278 // Stop the fork server.
279 gBootstrap->NS_LogTerm();
280 return 0;
282 // In a content process forked from the fork server.
283 // Start acting as a content process.
285 #endif
287 mozilla::TimeStamp start = mozilla::TimeStamp::Now();
289 AUTO_BASE_PROFILER_INIT;
290 AUTO_BASE_PROFILER_LABEL("nsBrowserApp main", OTHER);
292 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
293 // We are launching as a content process, delegate to the appropriate
294 // main
295 if (argc > 1 && IsArg(argv[1], "contentproc")) {
296 # ifdef HAS_DLL_BLOCKLIST
297 DllBlocklist_Initialize(gBlocklistInitFlags |
298 eDllBlocklistInitFlagIsChildProcess);
299 # endif
300 # if defined(XP_WIN)
301 // Ideally, we would be able to set our DPI awareness in
302 // firefox.exe.manifest Unfortunately, that would cause Win32k calls when
303 // user32.dll gets loaded, which would be incompatible with Win32k Lockdown
305 // MSDN says that it's allowed-but-not-recommended to initialize DPI
306 // programatically, as long as it's done before any HWNDs are created.
307 // Thus, we do it almost as soon as we possibly can
309 auto result = mozilla::WindowsDpiInitialization();
310 (void)result; // Ignore errors since some tools block DPI calls
312 # endif
313 # if defined(XP_WIN) && defined(MOZ_SANDBOX)
314 // We need to initialize the sandbox TargetServices before InitXPCOMGlue
315 // because we might need the sandbox broker to give access to some files.
316 if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) {
317 Output("Failed to initialize the sandbox target services.");
318 return 255;
320 # endif
322 nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
323 if (NS_FAILED(rv)) {
324 return 255;
327 int result = content_process_main(gBootstrap.get(), argc, argv);
329 # if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
330 DllBlocklist_Shutdown();
331 # endif
333 // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
334 gBootstrap->NS_LogTerm();
336 return result;
338 #endif
340 #ifdef HAS_DLL_BLOCKLIST
341 DllBlocklist_Initialize(gBlocklistInitFlags);
342 #endif
344 // We will likely only ever support this as a command line argument on Windows
345 // and OSX, so we're ifdefing here just to not create any expectations.
346 #if defined(XP_WIN) || defined(XP_MACOSX)
347 if (argc > 1 && IsArg(argv[1], "silentmode")) {
348 ::putenv(const_cast<char*>("MOZ_APP_SILENT_START=1"));
350 #endif
352 #if defined(XP_WIN)
354 // Ideally, we would be able to set our DPI awareness in firefox.exe.manifest
355 // Unfortunately, that would cause Win32k calls when user32.dll gets loaded,
356 // which would be incompatible with Win32k Lockdown
358 // MSDN says that it's allowed-but-not-recommended to initialize DPI
359 // programatically, as long as it's done before any HWNDs are created.
360 // Thus, we do it almost as soon as we possibly can
362 auto result = mozilla::WindowsDpiInitialization();
363 (void)result; // Ignore errors since some tools block DPI calls
366 // Once the browser process hits the main function, we no longer need
367 // a writable section handle because all dependent modules have been
368 // loaded.
369 mozilla::freestanding::gSharedSection.ConvertToReadOnly();
370 ::RtlRunOnceInitialize(&mozilla::freestanding::gK32ExportsResolveOnce);
372 mozilla::CreateAndStorePreXULSkeletonUI(GetModuleHandle(nullptr), argc, argv);
373 #endif
375 nsresult rv = InitXPCOMGlue(LibLoadingStrategy::ReadAhead);
376 if (NS_FAILED(rv)) {
377 return 255;
380 gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
382 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
383 gBootstrap->XRE_EnableSameExecutableForContentProc();
384 #endif
386 int result = do_main(argc, argv, envp);
388 #if defined(XP_WIN)
389 CleanupProcessRuntime();
390 #endif
392 gBootstrap->NS_LogTerm();
394 #if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
395 DllBlocklist_Shutdown();
396 #endif
398 #ifdef XP_MACOSX
399 // Allow writes again. While we would like to catch writes from static
400 // destructors to allow early exits to use _exit, we know that there is
401 // at least one such write that we don't control (see bug 826029). For
402 // now we enable writes again and early exits will have to use exit instead
403 // of _exit.
404 gBootstrap->XRE_StopLateWriteChecks();
405 #endif
407 gBootstrap.reset();
409 return result;