summaryrefslogtreecommitdiffstats
path: root/browser/app
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/app
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/app')
-rw-r--r--browser/app/Makefile.in106
-rw-r--r--browser/app/firefox.exe.manifest53
-rw-r--r--browser/app/macbuild/Contents/Info.plist.in263
-rw-r--r--browser/app/macbuild/Contents/MacOS-files-copy.in11
-rw-r--r--browser/app/macbuild/Contents/MacOS-files.in23
-rw-r--r--browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in5
-rw-r--r--browser/app/macbuild/Contents/moz.build25
-rw-r--r--browser/app/macversion.py47
-rw-r--r--browser/app/module.ver8
-rw-r--r--browser/app/moz.build151
-rw-r--r--browser/app/no-pie/NoPie.c26
-rw-r--r--browser/app/no-pie/moz.build24
-rw-r--r--browser/app/nsBrowserApp.cpp364
-rw-r--r--browser/app/permissions25
-rw-r--r--browser/app/profile/channel-prefs.js9
-rw-r--r--browser/app/profile/firefox.js2530
-rw-r--r--browser/app/splash.rc19
-rw-r--r--browser/app/winlauncher/DllBlocklistInit.cpp213
-rw-r--r--browser/app/winlauncher/DllBlocklistInit.h25
-rw-r--r--browser/app/winlauncher/ErrorHandler.cpp781
-rw-r--r--browser/app/winlauncher/ErrorHandler.h54
-rw-r--r--browser/app/winlauncher/LaunchUnelevated.cpp243
-rw-r--r--browser/app/winlauncher/LaunchUnelevated.h32
-rw-r--r--browser/app/winlauncher/LauncherProcessWin.cpp412
-rw-r--r--browser/app/winlauncher/LauncherProcessWin.h39
-rw-r--r--browser/app/winlauncher/NtLoaderAPI.cpp30
-rw-r--r--browser/app/winlauncher/ProcThreadAttributes.h159
-rw-r--r--browser/app/winlauncher/SameBinary.h142
-rw-r--r--browser/app/winlauncher/freestanding/CheckForCaller.h36
-rw-r--r--browser/app/winlauncher/freestanding/DllBlocklist.cpp483
-rw-r--r--browser/app/winlauncher/freestanding/DllBlocklist.h38
-rw-r--r--browser/app/winlauncher/freestanding/Freestanding.h67
-rw-r--r--browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp287
-rw-r--r--browser/app/winlauncher/freestanding/LoaderPrivateAPI.h62
-rw-r--r--browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp139
-rw-r--r--browser/app/winlauncher/freestanding/ModuleLoadFrame.h95
-rw-r--r--browser/app/winlauncher/freestanding/SafeThreadLocal.h96
-rw-r--r--browser/app/winlauncher/freestanding/SharedSection.cpp267
-rw-r--r--browser/app/winlauncher/freestanding/SharedSection.h118
-rw-r--r--browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py30
-rw-r--r--browser/app/winlauncher/freestanding/moz.build56
-rw-r--r--browser/app/winlauncher/freestanding/ntdll_freestanding.def25
-rw-r--r--browser/app/winlauncher/moz.build51
-rw-r--r--browser/app/winlauncher/test/TestCrossProcessWin.cpp365
-rw-r--r--browser/app/winlauncher/test/TestSafeThreadLocal.cpp74
-rw-r--r--browser/app/winlauncher/test/TestSameBinary.cpp255
-rw-r--r--browser/app/winlauncher/test/moz.build30
47 files changed, 8393 insertions, 0 deletions
diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in
new file mode 100644
index 0000000000..54d6b43fe1
--- /dev/null
+++ b/browser/app/Makefile.in
@@ -0,0 +1,106 @@
+# 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/.
+
+dist_dest = $(DIST)/$(MOZ_MACBUNDLE_NAME)
+
+# hardcode en-US for the moment
+AB_CD = en-US
+
+# Build a binary bootstrapping with XRE_main
+
+ifndef MOZ_WINCONSOLE
+ifneq (,$(MOZ_DEBUG)$(MOZ_ASAN))
+MOZ_WINCONSOLE = 1
+else
+MOZ_WINCONSOLE = 0
+endif
+endif
+
+include $(topsrcdir)/config/config.mk
+
+# If we are trying to show an error dialog about the lack of SSE2 support,
+# make sure that code itself doesn't use SSE2.
+ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
+CXX := $(filter-out -march=% -msse -msse2 -mfpmath=sse,$(CXX))
+CXX += -march=pentiumpro
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+# Rebuild firefox.exe if the manifest changes - it's included by splash.rc.
+# (this dependency should really be just for firefox.exe, not other targets)
+# Note the manifest file exists in the tree, so we use the explicit filename
+# here.
+EXTRA_DEPS += $(srcdir)/firefox.exe.manifest
+endif
+
+PROGRAMS_DEST = $(DIST)/bin
+objdir = $(topobjdir)/browser/app
+
+include $(topsrcdir)/config/rules.mk
+
+ifneq (,$(filter-out WINNT,$(OS_ARCH)))
+
+ifdef COMPILE_ENVIRONMENT
+ifndef MOZ_NO_PIE_COMPAT
+libs::
+ cp -p $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/bin/$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
+endif
+endif
+
+endif
+
+# channel-prefs.js is handled separate from other prefs due to bug 756325
+# DO NOT change the content of channel-prefs.js without taking the appropriate
+# steps. See bug 1431342.
+libs:: $(srcdir)/profile/channel-prefs.js
+ $(NSINSTALL) -D $(DIST)/bin/defaults/pref
+ $(call py_action,preprocessor,-Fsubstitution $(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js)
+
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+
+MAC_APP_NAME = $(MOZ_APP_DISPLAYNAME)
+
+ifdef MOZ_DEBUG
+MAC_APP_NAME := $(MAC_APP_NAME)Debug
+endif
+
+AB_CD = $(MOZ_UI_LOCALE)
+
+ifeq (zh-TW,$(AB_CD))
+LPROJ_ROOT := $(subst -,_,$(AB_CD))
+else
+LPROJ_ROOT := $(firstword $(subst -, ,$(AB_CD)))
+endif
+LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj
+
+repackage::
+ $(RM) -r $(dist_dest)
+
+MAC_BUNDLE_VERSION = $(shell $(PYTHON3) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h)
+
+.PHONY: repackage
+tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(objdir)/macbuild/Contents/MacOS-files.txt
+ rm -rf $(dist_dest)
+ $(MKDIR) -p '$(dist_dest)/Contents/MacOS'
+ $(MKDIR) -p '$(dist_dest)/$(LPROJ)'
+ rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj
+ rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)'
+ $(call py_action,preprocessor,-Fsubstitution -DAPP_VERSION='$(MOZ_APP_VERSION)' -DMOZ_APP_NAME='$(MOZ_APP_NAME)' -DMAC_APP_NAME='$(MAC_APP_NAME)' -DMOZ_MACBUNDLE_ID='$(MOZ_MACBUNDLE_ID)' -DMAC_BUNDLE_VERSION='$(MAC_BUNDLE_VERSION)' -DMOZ_DEVELOPER_REPO_PATH='$(topsrcdir)' -DMOZ_DEVELOPER_OBJ_PATH='$(topobjdir)' $(srcdir)/macbuild/Contents/Info.plist.in -o '$(dist_dest)/Contents/Info.plist')
+ $(call py_action,preprocessor,-Fsubstitution --output-encoding utf-16 -DMAC_APP_NAME='$(MAC_APP_NAME)' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o '$(dist_dest)/$(LPROJ)/InfoPlist.strings')
+ rsync -a --exclude-from='$(objdir)/macbuild/Contents/MacOS-files.txt' $(DIST)/bin/ '$(dist_dest)/Contents/Resources'
+ rsync -a --include-from='$(objdir)/macbuild/Contents/MacOS-files.txt' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
+ # MacOS-files-copy.in is a list of files that should be copies rather
+ # than symlinks and placed in .app/Contents/MacOS.
+ rsync -aL --include-from='$(srcdir)/macbuild/Contents/MacOS-files-copy.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
+ $(RM) '$(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)'
+ rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS'
+ cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns'
+ cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns'
+ $(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices'
+ifdef MOZ_UPDATER
+ mv -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices'
+ ln -s ../../../../Library/LaunchServices/org.mozilla.updater '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater'
+endif
+ printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo'
+endif
diff --git a/browser/app/firefox.exe.manifest b/browser/app/firefox.exe.manifest
new file mode 100644
index 0000000000..1e1d6f651d
--- /dev/null
+++ b/browser/app/firefox.exe.manifest
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="Firefox"
+ type="win32"
+/>
+<description>Firefox</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="mozglue"
+ version="1.0.0.0"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:security>
+ <ms_asmv3:requestedPrivileges>
+ <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </ms_asmv3:requestedPrivileges>
+ </ms_asmv3:security>
+</ms_asmv3:trustInfo>
+ <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>True/PM</dpiAware>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
+ </ms_asmv3:windowsSettings>
+ </ms_asmv3:application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/browser/app/macbuild/Contents/Info.plist.in b/browser/app/macbuild/Contents/Info.plist.in
new file mode 100644
index 0000000000..fb5e6993ea
--- /dev/null
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>html</string>
+ <string>htm</string>
+ <string>shtml</string>
+ <string>xht</string>
+ <string>xhtml</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeName</key>
+ <string>HTML Document</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>HTML</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>json</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>application/json</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>JSON File</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>svg</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>image/svg+xml</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>SVG document</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>NSDocumentClass</key>
+ <string>BrowserDocument</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>text</string>
+ <string>txt</string>
+ <string>js</string>
+ <string>log</string>
+ <string>css</string>
+ <string>xul</string>
+ <string>rdf</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeName</key>
+ <string>Text Document</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ <string>utxt</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>jpeg</string>
+ <string>jpg</string>
+ <string>png</string>
+ <string>gif</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>fileBookmark.icns</string>
+ <key>CFBundleTypeName</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>GIFf</string>
+ <string>JPEG</string>
+ <string>PNGf</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>oga</string>
+ <string>ogg</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>audio/ogg</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Audio (Ogg)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>ogv</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>video/ogg</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Video (Ogg)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>webm</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>video/webm</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Video (WebM)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>@MOZ_APP_NAME@</string>
+ <key>CFBundleGetInfoString</key>
+ <string>@MAC_APP_NAME@ @APP_VERSION@</string>
+ <key>CFBundleIconFile</key>
+ <string>firefox.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>@MOZ_MACBUNDLE_ID@</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>@MAC_APP_NAME@</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@APP_VERSION@</string>
+ <key>CFBundleSignature</key>
+ <string>MOZB</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleURLName</key>
+ <string>http URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>http</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleURLName</key>
+ <string>https URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>https</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>ftp URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>ftp</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>file URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>file</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>@MAC_BUNDLE_VERSION@</string>
+ <key>NSUserActivityTypes</key>
+ <array>
+ <string>NSUserActivityTypeBrowsingWeb</string>
+ </array>
+ <key>NSAppleScriptEnabled</key>
+ <true/>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.productivity</string>
+ <key>LSEnvironment</key>
+ <dict>
+ <key>MallocNanoZone</key>
+ <string>0</string>
+ </dict>
+ <key>LSFileQuarantineEnabled</key>
+ <true/>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.12.0</string>
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+ <key>NSRequiresAquaSystemAppearance</key>
+ <true/>
+ <key>NSPrincipalClass</key>
+ <string>GeckoNSApplication</string>
+ <key>SMPrivilegedExecutables</key>
+ <dict>
+ <key>org.mozilla.updater</key>
+ <string>identifier "org.mozilla.updater" and ((anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]) or (anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13] and certificate leaf[subject.OU] = "43AQ936H96"))</string>
+ </dict>
+ <key>MozillaDeveloperRepoPath</key>
+ <string>@MOZ_DEVELOPER_REPO_PATH@</string>
+ <key>MozillaDeveloperObjPath</key>
+ <string>@MOZ_DEVELOPER_OBJ_PATH@</string>
+
+ <key>NSCameraUsageDescription</key>
+ <string>Only sites you allow within @MAC_APP_NAME@ will be able to use the camera.</string>
+
+ <key>NSMicrophoneUsageDescription</key>
+ <string>Only sites you allow within @MAC_APP_NAME@ will be able to use the microphone.</string>
+</dict>
+</plist>
diff --git a/browser/app/macbuild/Contents/MacOS-files-copy.in b/browser/app/macbuild/Contents/MacOS-files-copy.in
new file mode 100644
index 0000000000..e9d0f0efb9
--- /dev/null
+++ b/browser/app/macbuild/Contents/MacOS-files-copy.in
@@ -0,0 +1,11 @@
+# Specifies files that should be copied (via deep copy, resolving symlinks)
+# from dist/bin to the .app/Contents/MacOS directory. Linking is preferred to
+# reduce disk I/O during builds, so just include dylibs which need to be in the
+# same directory as returned by dladddr(3).
+#
+# Some of these dylibs load other dylibs which are assumed to be siblings in
+# the same directory obtained from dladdr(3). With macOS 10.15, dladdr returns
+# absolute resolved paths which breaks this assumption if symlinks are used
+# because the symlink targets are in different directories. Hence the need for
+# them to be copied to the same directory.
+/*.dylib
diff --git a/browser/app/macbuild/Contents/MacOS-files.in b/browser/app/macbuild/Contents/MacOS-files.in
new file mode 100644
index 0000000000..3c6a1db5d6
--- /dev/null
+++ b/browser/app/macbuild/Contents/MacOS-files.in
@@ -0,0 +1,23 @@
+#if 0
+; Specifies files that should be copied (preserving symlinks) from dist/bin
+; to the .app/Contents/MacOS directory.
+#endif
+#filter substitution
+/*.app/***
+/certutil
+/@MOZ_APP_NAME@-bin
+#if defined(MOZ_GECKODRIVER)
+/geckodriver
+#endif
+/gtest/***
+#if defined(MOZ_ASAN) || defined(MOZ_TSAN)
+/llvm-symbolizer
+#endif
+#if defined(MOZ_CRASHREPORTER)
+/minidump-analyzer
+#endif
+/pingsender
+/pk12util
+/ssltunnel
+/xpcshell
+/XUL
diff --git a/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in b/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
new file mode 100644
index 0000000000..c84535de6a
--- /dev/null
+++ b/browser/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
@@ -0,0 +1,5 @@
+/* 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/. */
+
+CFBundleName = "@MAC_APP_NAME@";
diff --git a/browser/app/macbuild/Contents/moz.build b/browser/app/macbuild/Contents/moz.build
new file mode 100644
index 0000000000..b8b28926de
--- /dev/null
+++ b/browser/app/macbuild/Contents/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+defs = []
+
+for s in (
+ "MOZ_GECKODRIVER",
+ "MOZ_ASAN",
+ "MOZ_TSAN",
+ "MOZ_CRASHREPORTER",
+ "MOZ_APP_NAME",
+):
+ if CONFIG[s]:
+ defs.append("-D%s=%s" % (s, "1" if CONFIG[s] is True else CONFIG[s]))
+
+GeneratedFile(
+ "MacOS-files.txt",
+ script="/python/mozbuild/mozbuild/action/preprocessor.py",
+ entry_point="generate",
+ inputs=["MacOS-files.in"],
+ flags=defs,
+)
diff --git a/browser/app/macversion.py b/browser/app/macversion.py
new file mode 100644
index 0000000000..3d9aaaa94a
--- /dev/null
+++ b/browser/app/macversion.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+import io
+from optparse import OptionParser
+import sys
+import re
+
+o = OptionParser()
+o.add_option("--buildid", dest="buildid")
+o.add_option("--version", dest="version")
+
+(options, args) = o.parse_args()
+
+if not options.buildid:
+ print("--buildid is required", file=sys.stderr)
+ sys.exit(1)
+
+if not options.version:
+ print("--version is required", file=sys.stderr)
+ sys.exit(1)
+
+# We want to build a version number that matches the format allowed for
+# CFBundleVersion (nnnnn[.nn[.nn]]). We'll incorporate both the version
+# number as well as the date, so that it changes at least daily (for nightly
+# builds), but also so that newly-built older versions (e.g. beta build) aren't
+# considered "newer" than previously-built newer versions (e.g. a trunk nightly)
+
+define, MOZ_BUILDID, buildid = (
+ io.open(options.buildid, "r", encoding="utf-8").read().split()
+)
+
+# extract only the major version (i.e. "14" from "14.0b1")
+majorVersion = re.match(r"^(\d+)[^\d].*", options.version).group(1)
+# last two digits of the year
+twodigityear = buildid[2:4]
+month = buildid[4:6]
+if month[0] == "0":
+ month = month[1]
+day = buildid[6:8]
+if day[0] == "0":
+ day = day[1]
+
+print("%s.%s.%s" % (majorVersion + twodigityear, month, day))
diff --git a/browser/app/module.ver b/browser/app/module.ver
new file mode 100644
index 0000000000..5ef8d2a02a
--- /dev/null
+++ b/browser/app/module.ver
@@ -0,0 +1,8 @@
+WIN32_MODULE_COMPANYNAME=Mozilla Corporation
+WIN32_MODULE_COPYRIGHT=©Firefox and Mozilla Developers; available under the MPL 2 license.
+WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
+WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
+WIN32_MODULE_TRADEMARKS=Firefox is a Trademark of The Mozilla Foundation.
+WIN32_MODULE_DESCRIPTION=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@
diff --git a/browser/app/moz.build b/browser/app/moz.build
new file mode 100644
index 0000000000..9da2da3376
--- /dev/null
+++ b/browser/app/moz.build
@@ -0,0 +1,151 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Firefox", "General")
+
+with Files("firefox.exe.manifest"):
+ BUG_COMPONENT = ("Core", "Widget: Win32")
+with Files("module.ver"):
+ BUG_COMPONENT = ("Core", "Widget: Win32")
+with Files("splash.rc"):
+ BUG_COMPONENT = ("Core", "Widget: Win32")
+
+with Files("macversion.py"):
+ BUG_COMPONENT = ("Core", "Widget: Cocoa")
+with Files("macbuild/**"):
+ BUG_COMPONENT = ("Core", "Widget: Cocoa")
+
+with Files("moz.build"):
+ BUG_COMPONENT = ("Firefox Build System", "General")
+with Files("Makefile.in"):
+ BUG_COMPONENT = ("Firefox Build System", "General")
+
+with Files("profile/channel-prefs.js"):
+ BUG_COMPONENT = ("Firefox", "Installer")
+with Files("profile/firefox.js"):
+ BUG_COMPONENT = ("Firefox", "General")
+
+if CONFIG["MOZ_MACBUNDLE_NAME"]:
+ DIRS += ["macbuild/Contents"]
+
+if CONFIG["MOZ_NO_PIE_COMPAT"]:
+ GeckoProgram(CONFIG["MOZ_APP_NAME"] + "-bin")
+
+ DIRS += ["no-pie"]
+else:
+ GeckoProgram(CONFIG["MOZ_APP_NAME"])
+
+SOURCES += [
+ "nsBrowserApp.cpp",
+]
+
+# Neither channel-prefs.js nor firefox.exe want to end up in dist/bin/browser.
+DIST_SUBDIR = ""
+
+LOCAL_INCLUDES += [
+ "!/build",
+ "/toolkit/xre",
+ "/xpcom/base",
+ "/xpcom/build",
+]
+
+if CONFIG["LIBFUZZER"]:
+ USE_LIBS += ["fuzzer"]
+ LOCAL_INCLUDES += [
+ "/tools/fuzzing/libfuzzer",
+ ]
+
+if CONFIG["ENABLE_GECKODRIVER"]:
+ DEFINES["MOZ_GECKODRIVER"] = True
+
+if CONFIG["CC_TYPE"] == "clang-cl":
+ # Always enter a Windows program through wmain, whether or not we're
+ # a console application.
+ WIN32_EXE_LDFLAGS += ["-ENTRY:wmainCRTStartup"]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ RCINCLUDE = "splash.rc"
+ DIRS += [
+ "winlauncher",
+ ]
+ USE_LIBS += [
+ "winlauncher",
+ ]
+ LOCAL_INCLUDES += [
+ "/browser/app/winlauncher",
+ ]
+ DELAYLOAD_DLLS += [
+ "oleaut32.dll",
+ "ole32.dll",
+ "rpcrt4.dll",
+ "version.dll",
+ ]
+
+ if CONFIG["CC_TYPE"] == "clang-cl":
+ libpath_flag = "-LIBPATH:"
+ else:
+ libpath_flag = "-L"
+
+ WIN32_EXE_LDFLAGS += [
+ libpath_flag + OBJDIR + "/winlauncher/freestanding",
+ ]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "Darwin":
+ USE_LIBS += [
+ "mozsandbox",
+ ]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "WINNT":
+ # For sandbox includes and the include dependencies those have
+ LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+ ]
+
+ USE_LIBS += [
+ "sandbox_s",
+ ]
+
+ DELAYLOAD_DLLS += [
+ "winmm.dll",
+ "user32.dll",
+ ]
+
+# Control the default heap size.
+# This is the heap returned by GetProcessHeap().
+# As we use the CRT heap, the default size is too large and wastes VM.
+#
+# The default heap size is 1MB on Win32.
+# The heap will grow if need be.
+#
+# Set it to 256k. See bug 127069.
+if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["CC_TYPE"] not in ("clang", "gcc"):
+ LDFLAGS += ["/HEAP:0x40000"]
+
+DisableStlWrapping()
+
+if CONFIG["HAVE_CLOCK_MONOTONIC"]:
+ OS_LIBS += CONFIG["REALTIME_LIBS"]
+
+if CONFIG["MOZ_LINUX_32_SSE2_STARTUP_ERROR"]:
+ DEFINES["MOZ_LINUX_32_SSE2_STARTUP_ERROR"] = True
+ COMPILE_FLAGS["OS_CXXFLAGS"] = [
+ f
+ for f in COMPILE_FLAGS.get("OS_CXXFLAGS", [])
+ if not f.startswith("-march=") and f not in ("-msse", "-msse2", "-mfpmath=sse")
+ ] + [
+ "-mno-sse",
+ "-mno-sse2",
+ "-mfpmath=387",
+ ]
+
+for icon in ("firefox", "document", "newwindow", "newtab", "pbmode"):
+ DEFINES[icon.upper() + "_ICO"] = '"%s/%s/%s.ico"' % (
+ TOPSRCDIR,
+ CONFIG["MOZ_BRANDING_DIRECTORY"],
+ icon,
+ )
diff --git a/browser/app/no-pie/NoPie.c b/browser/app/no-pie/NoPie.c
new file mode 100644
index 0000000000..39b206e0af
--- /dev/null
+++ b/browser/app/no-pie/NoPie.c
@@ -0,0 +1,26 @@
+/* -*- 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 <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char* argv[]) {
+ // Ideally, we'd use mozilla::BinaryPath, but that pulls in stdc++compat,
+ // and further causes trouble linking with LTO.
+ char path[PATH_MAX + 4];
+ ssize_t len = readlink("/proc/self/exe", path, PATH_MAX - 1);
+ if (len < 0) {
+ fprintf(stderr, "Couldn't find the application directory.\n");
+ return 255;
+ }
+ strcpy(path + len, "-bin");
+ execv(path, argv);
+ // execv never returns. If it did, there was an error.
+ fprintf(stderr, "Exec failed with error: %s\n", strerror(errno));
+ return 255;
+}
diff --git a/browser/app/no-pie/moz.build b/browser/app/no-pie/moz.build
new file mode 100644
index 0000000000..74aa89409d
--- /dev/null
+++ b/browser/app/no-pie/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Program(CONFIG["MOZ_APP_NAME"])
+
+SOURCES += [
+ "NoPie.c",
+]
+
+# For some reason, LTO messes things up. We don't care anyways.
+CFLAGS += [
+ "-fno-lto",
+]
+
+# Use OS_LIBS instead of LDFLAGS to "force" the flag to come after -pie
+# from MOZ_PROGRAM_LDFLAGS.
+if CONFIG["CC_TYPE"] == "clang":
+ # clang < 5.0 doesn't support -no-pie.
+ OS_LIBS += ["-nopie"]
+else:
+ OS_LIBS += ["-no-pie"]
diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp
new file mode 100644
index 0000000000..de7bd4d94a
--- /dev/null
+++ b/browser/app/nsBrowserApp.cpp
@@ -0,0 +1,364 @@
+/* -*- 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/CmdLineAndEnvUtils.h"
+#include "mozilla/XREAppData.h"
+#include "XREShellData.h"
+#include "application.ini.h"
+#include "mozilla/Bootstrap.h"
+#if defined(XP_WIN)
+# include <windows.h>
+# include <stdlib.h>
+# include "mozilla/PreXULSkeletonUI.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 "freestanding/SharedSection.h"
+# include "LauncherProcessWin.h"
+# include "mozilla/WindowsDllBlocklist.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
+
+ 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();
+ sandboxing::PermissionsService* permissionsService =
+ sandboxing::GetPermissionsService();
+ if (!brokerServices) {
+ Output("Couldn't initialize the broker services.\n");
+ return 255;
+ }
+ config.sandboxBrokerServices = brokerServices;
+ config.sandboxPermissionsService = permissionsService;
+#endif
+
+#ifdef LIBFUZZER
+ if (getenv("FUZZER"))
+ gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
+#endif
+
+ // Note: keep in sync with LauncherProcessWin.
+ const char* acceptableParams[] = {"url", nullptr};
+ EnsureCommandlineSafe(argc, argv, acceptableParams);
+
+ 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;
+ }
+
+ gBootstrap = mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy);
+ if (!gBootstrap) {
+ Output("Couldn't load XPCOM.\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ // 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);
+
+#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")) {
+# ifdef HAS_DLL_BLOCKLIST
+ DllBlocklist_Initialize(gBlocklistInitFlags |
+ eDllBlocklistInitFlagIsChildProcess);
+# endif
+# if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ // 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
+
+ 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
+
+#ifdef HAS_DLL_BLOCKLIST
+ DllBlocklist_Initialize(gBlocklistInitFlags);
+#endif
+
+#if defined(XP_WIN)
+ // 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);
+
+ 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;
+}
diff --git a/browser/app/permissions b/browser/app/permissions
new file mode 100644
index 0000000000..991284081d
--- /dev/null
+++ b/browser/app/permissions
@@ -0,0 +1,25 @@
+# This file has default permissions for the permission manager.
+# The file-format is strict:
+# * matchtype \t type \t permission \t host
+# * "origin" should be used for matchtype, "host" is supported for legacy reasons
+# * type is a string that identifies the type of permission (e.g. "cookie")
+# * permission is an integer between 1 and 15
+# See PermissionManager.cpp for more...
+
+# UITour
+# Bug 1557153: www.mozilla.org gets a special workaround in UITourChild.jsm
+origin uitour 1 https://www.mozilla.org
+origin uitour 1 https://monitor.firefox.com
+origin uitour 1 https://screenshots.firefox.com
+origin uitour 1 https://support.mozilla.org
+origin uitour 1 about:home
+origin uitour 1 about:newtab
+
+# XPInstall
+origin install 1 https://addons.mozilla.org
+
+# Remote troubleshooting
+origin remote-troubleshooting 1 https://support.mozilla.org
+
+# addon install
+origin install 1 https://fpn.firefox.com
diff --git a/browser/app/profile/channel-prefs.js b/browser/app/profile/channel-prefs.js
new file mode 100644
index 0000000000..eed6e634c2
--- /dev/null
+++ b/browser/app/profile/channel-prefs.js
@@ -0,0 +1,9 @@
+/* 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/. */
+//
+// This pref is in its own file for complex reasons. See the comment in
+// browser/app/Makefile.in, bug 756325, and bug 1431342 for details. Do not add
+// other prefs to this file.
+
+pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
new file mode 100644
index 0000000000..5aad2ddcf4
--- /dev/null
+++ b/browser/app/profile/firefox.js
@@ -0,0 +1,2530 @@
+#filter dumbComments emptyLines substitution
+
+// -*- indent-tabs-mode: nil; js-indent-level: 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/.
+
+// Non-static prefs that are specific to desktop Firefox belong in this file
+// (unless there is a compelling and documented reason for them to belong in
+// another file).
+//
+// Please indent all prefs defined within #ifdef/#ifndef conditions. This
+// improves readability, particular for conditional blocks that exceed a single
+// screen.
+
+#ifdef XP_UNIX
+ #ifndef XP_MACOSX
+ #define UNIX_BUT_NOT_MAC
+ #endif
+#endif
+
+pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindowMac.xhtml");
+
+// Enables some extra Extension System Logging (can reduce performance)
+pref("extensions.logging.enabled", false);
+
+// Disables strict compatibility, making addons compatible-by-default.
+pref("extensions.strictCompatibility", false);
+
+// Temporary preference to forcibly make themes more safe with Australis even if
+// extensions.checkCompatibility=false has been set.
+pref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "29.0a1");
+
+pref("extensions.webextPermissionPrompts", true);
+pref("extensions.webextOptionalPermissionPrompts", true);
+// If enabled, install origin permission verification happens after addons are downloaded.
+pref("extensions.postDownloadThirdPartyPrompt", true);
+
+// Preferences for AMO integration
+pref("extensions.getAddons.cache.enabled", true);
+pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v4/addons/search/?guid=%IDS%&lang=%LOCALE%");
+pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
+pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/");
+pref("extensions.getAddons.langpacks.url", "https://services.addons.mozilla.org/api/v4/addons/language-tools/?app=firefox&type=language&appversion=%VERSION%");
+pref("extensions.getAddons.discovery.api_url", "https://services.addons.mozilla.org/api/v4/discovery/?lang=%LOCALE%&edition=%DISTRIBUTION%");
+
+// Use bloomfilters for the addons blocklist, instead of JSON only.
+pref("extensions.blocklist.useMLBF", true);
+pref("extensions.blocklist.useMLBF.stashes", true);
+
+// The URL for the privacy policy related to recommended extensions.
+pref("extensions.recommendations.privacyPolicyUrl", "https://www.mozilla.org/privacy/firefox/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_content=privacy-policy-link#addons");
+// The URL for Firefox Color, recommended on the theme page in about:addons.
+pref("extensions.recommendations.themeRecommendationUrl", "https://color.firefox.com/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_content=theme-footer-link");
+
+pref("extensions.update.autoUpdateDefault", true);
+
+// Check AUS for system add-on updates.
+pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+pref("extensions.systemAddon.update.enabled", true);
+
+// Disable add-ons that are not installed by the user in all scopes by default.
+// See the SCOPE constants in AddonManager.jsm for values to use here.
+pref("extensions.autoDisableScopes", 15);
+// Scopes to scan for changes at startup.
+pref("extensions.startupScanScopes", 0);
+
+pref("extensions.geckoProfiler.acceptedExtensionIds", "geckoprofiler@mozilla.com,quantum-foxfooding@mozilla.com,raptor@mozilla.org");
+
+
+pref("extensions.webextensions.remote", true);
+pref("extensions.webextensions.background-delayed-startup", true);
+
+// Require signed add-ons by default
+pref("extensions.langpacks.signatures.required", true);
+pref("xpinstall.signatures.required", true);
+pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
+
+// Enable extensionStorage storage actor by default
+pref("devtools.storage.extensionStorage.enabled", true);
+
+// Dictionary download preference
+pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/language-tools/");
+
+// At startup, should we check to see if the installation
+// date is older than some threshold
+pref("app.update.checkInstallTime", true);
+
+// The number of days a binary is permitted to be old without checking is defined in
+// firefox-branding.js (app.update.checkInstallTime.days)
+
+// The minimum delay in seconds for the timer to fire between the notification
+// of each consumer of the timer manager.
+// minimum=30 seconds, default=120 seconds, and maximum=300 seconds
+pref("app.update.timerMinimumDelay", 120);
+
+// The minimum delay in milliseconds for the first firing after startup of the timer
+// to notify consumers of the timer manager.
+// minimum=10 seconds, default=30 seconds, and maximum=120 seconds
+pref("app.update.timerFirstInterval", 30000);
+
+// App-specific update preferences
+
+// The interval to check for updates (app.update.interval) is defined in
+// firefox-branding.js
+
+// Enables some extra Application Update Logging (can reduce performance)
+pref("app.update.log", false);
+// Causes Application Update Logging to be sent to a file in the profile
+// directory. This preference is automatically disabled on application start to
+// prevent it from being left on accidentally. Turning this pref on enables
+// logging, even if app.update.log is false.
+pref("app.update.log.file", false);
+
+// The number of general background check failures to allow before notifying the
+// user of the failure. User initiated update checks always notify the user of
+// the failure.
+pref("app.update.backgroundMaxErrors", 10);
+
+// Ids of the links to the "What's new" update documentation
+pref("app.update.link.updateAvailableWhatsNew", "update-available-whats-new");
+pref("app.update.link.updateManualWhatsNew", "update-manual-whats-new");
+
+// How many times we should let downloads fail before prompting the user to
+// download a fresh installer.
+pref("app.update.download.promptMaxAttempts", 2);
+
+// How many times we should let an elevation prompt fail before prompting the user to
+// download a fresh installer.
+pref("app.update.elevation.promptMaxAttempts", 2);
+
+// If set to true, a message will be displayed in the hamburger menu while
+// an update is being downloaded.
+pref("app.update.notifyDuringDownload", false);
+
+// If set to true, the Update Service will automatically download updates if the
+// user can apply updates. This pref is no longer used on Windows, except as the
+// default value to migrate to the new location that this data is now stored
+// (which is in a file in the update directory). Because of this, this pref
+// should no longer be used directly. Instead, getAppUpdateAutoEnabled and
+// getAppUpdateAutoEnabled from UpdateUtils.jsm should be used.
+#ifndef XP_WIN
+ pref("app.update.auto", true);
+#endif
+
+// If set to true, the Update Service will apply updates in the background
+// when it finishes downloading them.
+pref("app.update.staging.enabled", true);
+
+// Update service URL:
+// app.update.url was removed in Bug 1568994
+// app.update.url.manual is in branding section
+// app.update.url.details is in branding section
+
+// app.update.badgeWaitTime is in branding section
+// app.update.interval is in branding section
+// app.update.promptWaitTime is in branding section
+
+// Whether or not to attempt using the service for updates.
+#ifdef MOZ_MAINTENANCE_SERVICE
+ pref("app.update.service.enabled", true);
+#endif
+
+#ifdef MOZ_BITS_DOWNLOAD
+ // If set to true, the Update Service will attempt to use Windows BITS to
+ // download updates and will fallback to downloading internally if that fails.
+ pref("app.update.BITS.enabled", true);
+#endif
+
+pref("app.update.langpack.enabled", true);
+
+// Symmetric (can be overridden by individual extensions) update preferences.
+// e.g.
+// extensions.{GUID}.update.enabled
+// extensions.{GUID}.update.url
+// .. etc ..
+//
+pref("extensions.update.enabled", true);
+pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.interval", 86400); // Check for updates to Extensions and
+ // Themes every day
+
+pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
+
+#if defined(MOZ_WIDEVINE_EME)
+ pref("browser.eme.ui.enabled", true);
+#else
+ pref("browser.eme.ui.enabled", false);
+#endif
+
+// UI tour experience.
+pref("browser.uitour.enabled", true);
+pref("browser.uitour.loglevel", "Error");
+pref("browser.uitour.requireSecure", true);
+pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
+pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
+// How long to show a Hearbeat survey (two hours, in seconds)
+pref("browser.uitour.surveyDuration", 7200);
+
+pref("keyword.enabled", true);
+
+// Fixup whitelists, the urlbar won't try to search for these words, but will
+// instead consider them valid TLDs. Don't check these directly, use
+// Services.uriFixup.isDomainKnown() instead.
+pref("browser.fixup.domainwhitelist.localhost", true);
+// https://tools.ietf.org/html/rfc2606
+pref("browser.fixup.domainsuffixwhitelist.test", true);
+pref("browser.fixup.domainsuffixwhitelist.example", true);
+pref("browser.fixup.domainsuffixwhitelist.invalid", true);
+pref("browser.fixup.domainsuffixwhitelist.localhost", true);
+// https://tools.ietf.org/html/draft-wkumari-dnsop-internal-00
+pref("browser.fixup.domainsuffixwhitelist.internal", true);
+// https://tools.ietf.org/html/rfc6762
+pref("browser.fixup.domainsuffixwhitelist.local", true);
+
+// Whether to always go through the DNS server before sending a single word
+// search string, that may contain a valid host, to a search engine.
+pref("browser.fixup.dns_first_for_single_words", false);
+
+#ifdef UNIX_BUT_NOT_MAC
+ pref("general.autoScroll", false);
+#else
+ pref("general.autoScroll", true);
+#endif
+
+// UI density of the browser chrome. This mostly affects toolbarbutton
+// and urlbar spacing. The possible values are 0=normal, 1=compact, 2=touch.
+pref("browser.uidensity", 0);
+// Whether Firefox will automatically override the uidensity to "touch"
+// while the user is in a touch environment (such as Windows tablet mode).
+pref("browser.touchmode.auto", true);
+
+// At startup, check if we're the default browser and prompt user if not.
+pref("browser.shell.checkDefaultBrowser", true);
+pref("browser.shell.shortcutFavicons",true);
+pref("browser.shell.mostRecentDateSetAsDefault", "");
+pref("browser.shell.skipDefaultBrowserCheckOnFirstRun", true);
+pref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", false);
+pref("browser.shell.defaultBrowserCheckCount", 0);
+#ifdef EARLY_BETA_OR_EARLIER
+pref("browser.defaultbrowser.notificationbar", true);
+#else
+pref("browser.defaultbrowser.notificationbar", false);
+#endif
+pref("browser.defaultbrowser.notificationbar.checkcount", 0);
+pref("browser.defaultbrowser.notificationbar.checklimit", 10000);
+
+// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
+// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
+pref("browser.startup.page", 1);
+pref("browser.startup.homepage", "about:home");
+#ifdef NIGHTLY_BUILD
+pref("browser.startup.homepage.abouthome_cache.enabled", true);
+#else
+pref("browser.startup.homepage.abouthome_cache.enabled", false);
+#endif
+pref("browser.startup.homepage.abouthome_cache.loglevel", "Warn");
+
+// Whether we should skip the homepage when opening the first-run page
+pref("browser.startup.firstrunSkipsHomepage", true);
+
+// Show an about:blank window as early as possible for quick startup feedback.
+// Held to nightly on Linux due to bug 1450626.
+// Disabled on Mac because the bouncing dock icon already provides feedback.
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)
+ pref("browser.startup.blankWindow", true);
+#else
+ pref("browser.startup.blankWindow", false);
+#endif
+
+// Show a skeleton UI window prior to loading libxul. Only visible for windows
+// users as it is not implemented anywhere else.
+#if defined(XP_WIN)
+#ifdef NIGHTLY_BUILD
+pref("browser.startup.preXulSkeletonUI", true);
+#else
+pref("browser.startup.preXulSkeletonUI", false);
+#endif
+#endif
+
+// Don't create the hidden window during startup on
+// platforms that don't always need it (Win/Linux).
+pref("toolkit.lazyHiddenWindow", true);
+
+pref("browser.slowStartup.notificationDisabled", false);
+pref("browser.slowStartup.timeThreshold", 20000);
+pref("browser.slowStartup.maxSamples", 5);
+
+pref("browser.chrome.site_icons", true);
+// browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
+pref("browser.warnOnQuit", true);
+pref("browser.fullscreen.autohide", true);
+pref("browser.overlink-delay", 80);
+
+// Whether using `ctrl` when hitting return/enter in the URL bar
+// (or clicking 'go') should prefix 'www.' and suffix
+// browser.fixup.alternate.suffix to the URL bar value prior to
+// navigating.
+pref("browser.urlbar.ctrlCanonizesURLs", true);
+
+// Whether we announce to screen readers when tab-to-search results are
+// inserted.
+pref("browser.urlbar.accessibility.tabToSearch.announceResults", true);
+
+// Control autoFill behavior
+pref("browser.urlbar.autoFill", true);
+
+// Whether to warm up network connections for autofill or search results.
+pref("browser.urlbar.speculativeConnect.enabled", true);
+
+// Whether bookmarklets should be filtered out of Address Bar matches.
+// This is enabled for security reasons, when true it is still possible to
+// search for bookmarklets typing "javascript: " followed by the actual query.
+pref("browser.urlbar.filter.javascript", true);
+
+// Enable a certain level of urlbar logging to the Browser Console. See Log.jsm.
+pref("browser.urlbar.loglevel", "Error");
+
+// the maximum number of results to show in autocomplete when doing richResults
+pref("browser.urlbar.maxRichResults", 10);
+
+// The maximum number of historical search results to show.
+pref("browser.urlbar.maxHistoricalSearchSuggestions", 2);
+
+// The default behavior for the urlbar can be configured to use any combination
+// of the match filters with each additional filter adding more results (union).
+pref("browser.urlbar.suggest.bookmark", true);
+pref("browser.urlbar.suggest.history", true);
+pref("browser.urlbar.suggest.openpage", true);
+pref("browser.urlbar.suggest.searches", true);
+pref("browser.urlbar.suggest.topsites", true);
+pref("browser.urlbar.suggest.engines", true);
+
+// As a user privacy measure, don't fetch search suggestions if a pasted string
+// is longer than this.
+pref("browser.urlbar.maxCharsForSearchSuggestions", 100);
+
+pref("browser.urlbar.trimURLs", true);
+
+// If changed to true, copying the entire URL from the location bar will put the
+// human readable (percent-decoded) URL on the clipboard.
+pref("browser.urlbar.decodeURLsOnCopy", false);
+
+// Whether or not to move tabs into the active window when using the "Switch to
+// Tab" feature of the awesomebar.
+pref("browser.urlbar.switchTabs.adoptIntoActiveWindow", false);
+
+// Whether addresses and search results typed into the address bar
+// should be opened in new tabs by default.
+pref("browser.urlbar.openintab", false);
+
+// If true, we show tail suggestions when available.
+pref("browser.urlbar.richSuggestions.tail", true);
+
+// Controls the empty search behavior in Search Mode:
+// 0 - Show nothing
+// 1 - Show search history
+// 2 - Show search and browsing history
+pref("browser.urlbar.update2.emptySearchBehavior", 0);
+
+// Whether the urlbar displays one-offs to filter searches to history,
+// bookmarks, or tabs.
+pref("browser.urlbar.shortcuts.bookmarks", true);
+pref("browser.urlbar.shortcuts.tabs", true);
+pref("browser.urlbar.shortcuts.history", true);
+
+pref("browser.urlbar.eventTelemetry.enabled", false);
+
+// When we send events to Urlbar extensions, we wait this amount of time in
+// milliseconds for them to respond before timing out.
+pref("browser.urlbar.extension.timeout", 400);
+
+// Controls when to DNS resolve single word search strings, after they were
+// searched for. If the string is resolved as a valid host, show a
+// "Did you mean to go to 'host'" prompt.
+// 0 - never resolve; 1 - use heuristics (default); 2 - always resolve
+pref("browser.urlbar.dnsResolveSingleWordsAfterSearch", 1);
+
+// Whether IME composition should close the results panel.
+// The default value is true because some IME open a picker panel, and we end
+// up with two panels on top of each other. Since for now we can't detect that
+// we leave this choice to the user, hopefully in the future this can be flipped
+// for everyone.
+pref("browser.urlbar.imeCompositionClosesPanel", true);
+
+pref("browser.altClickSave", false);
+
+// Enable logging downloads operations to the Console.
+pref("browser.download.loglevel", "Error");
+
+// Number of milliseconds to wait for the http headers (and thus
+// the Content-Disposition filename) before giving up and falling back to
+// picking a filename without that info in hand so that the user sees some
+// feedback from their action.
+pref("browser.download.saveLinkAsFilenameTimeout", 4000);
+
+pref("browser.download.useDownloadDir", true);
+pref("browser.download.folderList", 1);
+pref("browser.download.manager.addToRecentDocs", true);
+pref("browser.download.manager.resumeOnWakeDelay", 10000);
+
+// This allows disabling the animated notifications shown by
+// the Downloads Indicator when a download starts or completes.
+pref("browser.download.animateNotifications", true);
+
+// This records whether or not the panel has been shown at least once.
+pref("browser.download.panel.shown", false);
+
+// This records whether or not to show the 'Open in system viewer' context menu item when appropriate
+pref("browser.download.openInSystemViewerContextMenuItem", true);
+
+// This records whether or not to show the 'Always open...' context menu item when appropriate
+pref("browser.download.alwaysOpenInSystemViewerContextMenuItem", true);
+
+// Open downloaded file types internally for the given types.
+// This is a comma-separated list, the empty string ("") means no types are
+// viewable internally.
+pref("browser.download.viewableInternally.enabledTypes", "xml,svg,webp,avif");
+
+
+// This controls whether the button is automatically shown/hidden depending
+// on whether there are downloads to show.
+pref("browser.download.autohideButton", true);
+
+#ifndef XP_MACOSX
+ pref("browser.helperApps.deleteTempFileOnExit", true);
+#endif
+
+// This controls the visibility of the radio button in the
+// Unknown Content Type (Helper App) dialog that will open
+// the content in the browser for PDF and for other
+// Viewable Internally types
+// (see browser.download.viewableInternally.enabledTypes)
+pref("browser.helperApps.showOpenOptionForPdfJS", true);
+pref("browser.helperApps.showOpenOptionForViewableInternally", true);
+
+// search engines URL
+pref("browser.search.searchEnginesURL", "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
+
+// search bar results always open in a new tab
+pref("browser.search.openintab", false);
+
+// context menu searches open in the foreground
+pref("browser.search.context.loadInBackground", false);
+
+// comma seperated list of of engines to hide in the search panel.
+pref("browser.search.hiddenOneOffs", "");
+
+// Mirrors whether the search-container widget is in the navigation toolbar.
+pref("browser.search.widget.inNavBar", false);
+
+// Enables display of the options for the user using a separate default search
+// engine in private browsing mode.
+pref("browser.search.separatePrivateDefault.ui.enabled", false);
+// The maximum amount of times the private default banner is shown.
+pref("browser.search.separatePrivateDefault.ui.banner.max", 0);
+
+// Enables the display of the Mozilla VPN banner in private browsing windows
+pref("browser.privatebrowsing.vpnpromourl", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-%CHANNEL%-browser&utm_campaign=private-browsing-vpn-link");
+
+pref("browser.sessionhistory.max_entries", 50);
+
+// Built-in default permissions.
+pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");
+
+// Set default fallback values for site permissions we want
+// the user to be able to globally change.
+pref("permissions.default.camera", 0);
+pref("permissions.default.microphone", 0);
+pref("permissions.default.geo", 0);
+pref("permissions.default.xr", 0);
+pref("permissions.default.desktop-notification", 0);
+pref("permissions.default.shortcuts", 0);
+
+pref("permissions.desktop-notification.postPrompt.enabled", true);
+pref("permissions.desktop-notification.notNow.enabled", false);
+
+pref("permissions.fullscreen.allowed", false);
+
+// handle links targeting new windows
+// 1=current window/tab, 2=new window, 3=new tab in most recent window
+pref("browser.link.open_newwindow", 3);
+
+// handle external links (i.e. links opened from a different application)
+// default: use browser.link.open_newwindow
+// 1-3: see browser.link.open_newwindow for interpretation
+pref("browser.link.open_newwindow.override.external", -1);
+
+// 0: no restrictions - divert everything
+// 1: don't divert window.open at all
+// 2: don't divert window.open with features
+pref("browser.link.open_newwindow.restriction", 2);
+
+// If true, this pref causes windows opened by window.open to be forced into new
+// tabs (rather than potentially opening separate windows, depending on
+// window.open arguments) when the browser is in fullscreen mode.
+// We set this differently on Mac because the fullscreen implementation there is
+// different.
+#ifdef XP_MACOSX
+ pref("browser.link.open_newwindow.disabled_in_fullscreen", true);
+#else
+ pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
+#endif
+
+// Tabbed browser
+pref("browser.tabs.closeTabByDblclick", false);
+pref("browser.tabs.closeWindowWithLastTab", true);
+pref("browser.tabs.allowTabDetach", true);
+// Open related links to a tab, e.g., link in current tab, at next to the
+// current tab if |insertRelatedAfterCurrent| is true. Otherwise, always
+// append new tab to the end.
+pref("browser.tabs.insertRelatedAfterCurrent", true);
+// Open all links, e.g., bookmarks, history items at next to current tab
+// if |insertAfterCurrent| is true. Otherwise, append new tab to the end
+// for non-related links. Note that if this is set to true, it will trump
+// the value of browser.tabs.insertRelatedAfterCurrent.
+pref("browser.tabs.insertAfterCurrent", false);
+pref("browser.tabs.warnOnClose", true);
+pref("browser.tabs.warnOnCloseOtherTabs", true);
+pref("browser.tabs.warnOnOpen", true);
+pref("browser.tabs.maxOpenBeforeWarn", 15);
+pref("browser.tabs.loadInBackground", true);
+pref("browser.tabs.opentabfor.middleclick", true);
+pref("browser.tabs.loadDivertedInBackground", false);
+pref("browser.tabs.loadBookmarksInBackground", false);
+pref("browser.tabs.loadBookmarksInTabs", false);
+pref("browser.tabs.tabClipWidth", 140);
+pref("browser.tabs.tabMinWidth", 76);
+// Initial titlebar state is managed by -moz-gtk-csd-hide-titlebar-by-default
+// on Linux.
+#ifndef UNIX_BUT_NOT_MAC
+ pref("browser.tabs.drawInTitlebar", true);
+#endif
+
+//Control the visibility of Tab Manager Menu.
+pref("browser.tabs.tabmanager.enabled", false);
+
+// Offer additional drag space to the user. The drag space
+// will only be shown if browser.tabs.drawInTitlebar is true.
+pref("browser.tabs.extraDragSpace", false);
+
+// When tabs opened by links in other tabs via a combination of
+// browser.link.open_newwindow being set to 3 and target="_blank" etc are
+// closed:
+// true return to the tab that opened this tab (its owner)
+// false return to the adjacent tab (old default)
+pref("browser.tabs.selectOwnerOnClose", true);
+
+// This should match Chromium's audio indicator delay.
+pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000);
+
+// Pref to control whether we use a separate privileged content process
+// for about: pages. This pref name did not age well: we will have multiple
+// types of privileged content processes, each with different privileges.
+// types of privleged content processes, each with different privleges.
+pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
+
+#if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN)
+ // This pref will cause assertions when a remoteType triggers a process switch
+ // to a new remoteType it should not be able to trigger.
+ pref("browser.tabs.remote.enforceRemoteTypeRestrictions", true);
+#endif
+
+// Pref to control whether we use a separate privileged content process
+// for certain mozilla webpages (which are listed in the pref
+// browser.tabs.remote.separatedMozillaDomains).
+pref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true);
+
+#ifdef NIGHTLY_BUILD
+pref("browser.tabs.tooltipsShowPidAndActiveness", true);
+#else
+pref("browser.tabs.tooltipsShowPidAndActiveness", false);
+#endif
+
+// allow_eval_* is enabled on Firefox Desktop only at this
+// point in time
+pref("security.allow_eval_with_system_principal", false);
+pref("security.allow_eval_in_parent_process", false);
+
+#if defined(NIGHTLY_BUILD)
+ pref("security.allow_parent_unrestricted_js_loads", false);
+#endif
+
+// Unload tabs when available memory is running low
+pref("browser.tabs.unloadOnLowMemory", false);
+
+pref("browser.ctrlTab.recentlyUsedOrder", true);
+
+// By default, do not export HTML at shutdown.
+// If true, at shutdown the bookmarks in your menu and toolbar will
+// be exported as HTML to the bookmarks.html file.
+pref("browser.bookmarks.autoExportHTML", false);
+
+// The maximum number of daily bookmark backups to
+// keep in {PROFILEDIR}/bookmarkbackups. Special values:
+// -1: unlimited
+// 0: no backups created (and deletes all existing backups)
+pref("browser.bookmarks.max_backups", 15);
+
+// Whether menu should close after Ctrl-click, middle-click, etc.
+pref("browser.bookmarks.openInTabClosesMenu", true);
+
+// Where new bookmarks go by default.
+// Use PlacesUIUtils.defaultParentGuid to read this; do NOT read the pref
+// directly.
+// The pref is ignored if the browser.toolbars.bookmarks.2h2020 pref is false,
+// in which case bookmarks always go in the "Other bookmarks" folder.
+// The value is one of:
+// - a bookmarks guid
+// - "toolbar", "menu" or "unfiled" for those folders.
+// If we use the pref but the value isn't any of these, we'll fall back to
+// the bookmarks toolbar as a default.
+pref("browser.bookmarks.defaultLocation", "toolbar");
+
+// Scripts & Windows prefs
+pref("dom.disable_open_during_load", true);
+pref("javascript.options.showInConsole", true);
+
+// allow JS to move and resize existing windows
+pref("dom.disable_window_move_resize", false);
+// prevent JS from monkeying with window focus, etc
+pref("dom.disable_window_flip", true);
+
+// popups.policy 1=allow,2=reject
+pref("privacy.popups.policy", 1);
+pref("privacy.popups.usecustom", true);
+pref("privacy.popups.showBrowserMessage", true);
+
+pref("privacy.item.cookies", false);
+
+pref("privacy.clearOnShutdown.history", true);
+pref("privacy.clearOnShutdown.formdata", true);
+pref("privacy.clearOnShutdown.downloads", true);
+pref("privacy.clearOnShutdown.cookies", true);
+pref("privacy.clearOnShutdown.cache", true);
+pref("privacy.clearOnShutdown.sessions", true);
+pref("privacy.clearOnShutdown.offlineApps", false);
+pref("privacy.clearOnShutdown.siteSettings", false);
+pref("privacy.clearOnShutdown.openWindows", false);
+
+pref("privacy.cpd.history", true);
+pref("privacy.cpd.formdata", true);
+pref("privacy.cpd.passwords", false);
+pref("privacy.cpd.downloads", true);
+pref("privacy.cpd.cookies", true);
+pref("privacy.cpd.cache", true);
+pref("privacy.cpd.sessions", true);
+pref("privacy.cpd.offlineApps", false);
+pref("privacy.cpd.siteSettings", false);
+pref("privacy.cpd.openWindows", false);
+
+pref("privacy.history.custom", false);
+
+// What default should we use for the time span in the sanitizer:
+// 0 - Clear everything
+// 1 - Last Hour
+// 2 - Last 2 Hours
+// 3 - Last 4 Hours
+// 4 - Today
+// 5 - Last 5 minutes
+// 6 - Last 24 hours
+pref("privacy.sanitize.timeSpan", 1);
+
+pref("privacy.sanitize.migrateFx3Prefs", false);
+
+pref("privacy.panicButton.enabled", true);
+
+// Time until temporary permissions expire, in ms
+pref("privacy.temporary_permission_expire_time_ms", 3600000);
+
+pref("network.proxy.share_proxy_settings", false); // use the same proxy settings for all protocols
+
+// simple gestures support
+pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
+pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
+pref("browser.gesture.swipe.up", "cmd_scrollTop");
+pref("browser.gesture.swipe.down", "cmd_scrollBottom");
+pref("browser.gesture.pinch.latched", false);
+pref("browser.gesture.pinch.threshold", 25);
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // Enabled for touch input display zoom.
+ pref("browser.gesture.pinch.out", "cmd_fullZoomEnlarge");
+ pref("browser.gesture.pinch.in", "cmd_fullZoomReduce");
+ pref("browser.gesture.pinch.out.shift", "cmd_fullZoomReset");
+ pref("browser.gesture.pinch.in.shift", "cmd_fullZoomReset");
+#else
+ // Disabled by default due to issues with track pad input.
+ pref("browser.gesture.pinch.out", "");
+ pref("browser.gesture.pinch.in", "");
+ pref("browser.gesture.pinch.out.shift", "");
+ pref("browser.gesture.pinch.in.shift", "");
+#endif
+pref("browser.gesture.twist.latched", false);
+pref("browser.gesture.twist.threshold", 0);
+pref("browser.gesture.twist.right", "cmd_gestureRotateRight");
+pref("browser.gesture.twist.left", "cmd_gestureRotateLeft");
+pref("browser.gesture.twist.end", "cmd_gestureRotateEnd");
+pref("browser.gesture.tap", "cmd_fullZoomReset");
+
+pref("browser.history_swipe_animation.disabled", false);
+
+// 0: Nothing happens
+// 1: Scrolling contents
+// 2: Go back or go forward, in your history
+// 3: Zoom in or out (reflowing zoom).
+// 4: Treat vertical wheel as horizontal scroll
+// 5: Zoom in or out (pinch zoom).
+#ifdef XP_MACOSX
+ // On macOS, if the wheel has one axis only, shift+wheel comes through as a
+ // horizontal scroll event. Thus, we can't assign anything other than normal
+ // scrolling to shift+wheel.
+ pref("mousewheel.with_shift.action", 1);
+ pref("mousewheel.with_alt.action", 2);
+ // On MacOS X, control+wheel is typically handled by system and we don't
+ // receive the event. So, command key which is the main modifier key for
+ // acceleration is the best modifier for zoom-in/out. However, we should keep
+ // the control key setting for backward compatibility.
+ pref("mousewheel.with_meta.action", 3); // command key on Mac
+ // Disable control-/meta-modified horizontal wheel events, since those are
+ // used on Mac as part of modified swipe gestures (e.g. Left swipe+Cmd is
+ // "go back" in a new tab).
+ pref("mousewheel.with_control.action.override_x", 0);
+ pref("mousewheel.with_meta.action.override_x", 0);
+#else
+ // On the other platforms (non-macOS), user may use legacy mouse which
+ // supports only vertical wheel but want to scroll horizontally. For such
+ // users, we should provide horizontal scroll with shift+wheel (same as
+ // Chrome). However, shift+wheel was used for navigating history. For users
+ // who want to keep using this feature, let's enable it with alt+wheel. This
+ // is better for consistency with macOS users.
+ pref("mousewheel.with_shift.action", 4);
+ pref("mousewheel.with_alt.action", 2);
+ pref("mousewheel.with_meta.action", 1); // win key on Win, Super/Hyper on Linux
+#endif
+pref("mousewheel.with_control.action",3);
+pref("mousewheel.with_win.action", 1);
+
+pref("browser.xul.error_pages.expert_bad_cert", false);
+pref("browser.xul.error_pages.show_safe_browsing_details_on_load", false);
+
+// Enable captive portal detection.
+pref("network.captive-portal-service.enabled", true);
+
+// If true, network link events will change the value of navigator.onLine
+pref("network.manage-offline-status", true);
+
+// We want to make sure mail URLs are handled externally...
+pref("network.protocol-handler.external.mailto", true); // for mail
+pref("network.protocol-handler.external.news", true); // for news
+pref("network.protocol-handler.external.snews", true); // for secure news
+pref("network.protocol-handler.external.nntp", true); // also news
+#ifdef XP_WIN
+ pref("network.protocol-handler.external.ms-windows-store", true);
+#endif
+
+// ...without warning dialogs
+pref("network.protocol-handler.warn-external.mailto", false);
+pref("network.protocol-handler.warn-external.news", false);
+pref("network.protocol-handler.warn-external.snews", false);
+pref("network.protocol-handler.warn-external.nntp", false);
+#ifdef XP_WIN
+ pref("network.protocol-handler.warn-external.ms-windows-store", false);
+#endif
+
+// By default, all protocol handlers are exposed. This means that
+// the browser will respond to openURL commands for all URL types.
+// It will also try to open link clicks inside the browser before
+// failing over to the system handlers.
+pref("network.protocol-handler.expose-all", true);
+pref("network.protocol-handler.expose.mailto", false);
+pref("network.protocol-handler.expose.news", false);
+pref("network.protocol-handler.expose.snews", false);
+pref("network.protocol-handler.expose.nntp", false);
+
+pref("accessibility.typeaheadfind", false);
+pref("accessibility.typeaheadfind.timeout", 5000);
+pref("accessibility.typeaheadfind.linksonly", false);
+pref("accessibility.typeaheadfind.flashBar", 1);
+
+// Accessibility indicator preferences such as support URL, enabled flag.
+pref("accessibility.support.url", "https://support.mozilla.org/%LOCALE%/kb/accessibility-services");
+pref("accessibility.indicator.enabled", false);
+
+pref("plugins.testmode", false);
+
+// Should plugins that are hidden show the infobar UI?
+pref("plugins.show_infobar", false);
+
+#if defined(_ARM64_) && defined(XP_WIN)
+ pref("plugin.default.state", 0);
+#else
+ pref("plugin.default.state", 1);
+#endif
+
+// Enables the download and use of the flash blocklists.
+pref("plugins.flashBlock.enabled", true);
+
+// Prefer HTML5 video over Flash content, and don't
+// load plugin instances with no src declared.
+// These prefs are documented in details on all.js.
+// With the "follow-ctp" setting, this will only
+// apply to users that have plugin.state.flash = 1.
+pref("plugins.favorfallback.mode", "follow-ctp");
+pref("plugins.favorfallback.rules", "nosrc,video");
+
+#ifdef XP_WIN
+ pref("browser.preferences.instantApply", false);
+#else
+ pref("browser.preferences.instantApply", true);
+#endif
+
+// Toggling Search bar on and off in about:preferences
+pref("browser.preferences.search", true);
+#if defined(NIGHTLY_BUILD)
+pref("browser.preferences.experimental", true);
+#else
+pref("browser.preferences.experimental", false);
+#endif
+pref("browser.preferences.experimental.hidden", false);
+pref("browser.preferences.defaultPerformanceSettings.enabled", true);
+
+pref("browser.preferences.exposeHTTPSOnly", true);
+
+pref("browser.proton.enabled", false);
+
+// Backspace and Shift+Backspace behavior
+// 0 goes Back/Forward
+// 1 act like PgUp/PgDown
+// 2 and other values, nothing
+#ifdef UNIX_BUT_NOT_MAC
+ pref("browser.backspace_action", 2);
+#else
+ pref("browser.backspace_action", 0);
+#endif
+
+pref("intl.regional_prefs.use_os_locales", false);
+
+// this will automatically enable inline spellchecking (if it is available) for
+// editable elements in HTML
+// 0 = spellcheck nothing
+// 1 = check multi-line controls [default]
+// 2 = check multi/single line controls
+pref("layout.spellcheckDefault", 1);
+
+pref("browser.send_pings", false);
+
+// At startup, if the handler service notices that the version number in the
+// region.properties file is newer than the version number in the handler
+// service datastore, it will add any new handlers it finds in the prefs (as
+// seeded by this file) to its datastore.
+pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties");
+
+// The default set of web-based protocol handlers shown in the application
+// selection dialog for webcal: ; I've arbitrarily picked 4 default handlers
+// per protocol, but if some locale wants more than that (or defaults for some
+// protocol not currently listed here), we should go ahead and add those.
+
+// webcal
+pref("gecko.handlerService.schemes.webcal.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// mailto
+pref("gecko.handlerService.schemes.mailto.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// irc
+pref("gecko.handlerService.schemes.irc.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// ircs
+pref("gecko.handlerService.schemes.ircs.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");
+pref("browser.xr.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/xr/");
+
+pref("browser.sessionstore.resume_from_crash", true);
+pref("browser.sessionstore.resume_session_once", false);
+pref("browser.sessionstore.resuming_after_os_restart", false);
+
+// Minimal interval between two save operations in milliseconds (while the user is active).
+pref("browser.sessionstore.interval", 15000); // 15 seconds
+
+// Minimal interval between two save operations in milliseconds (while the user is idle).
+pref("browser.sessionstore.interval.idle", 3600000); // 1h
+
+// Time (ms) before we assume that the user is idle and that we don't need to
+// collect/save the session quite as often.
+pref("browser.sessionstore.idleDelay", 180000); // 3 minutes
+
+// on which sites to save text data, POSTDATA and cookies
+// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
+pref("browser.sessionstore.privacy_level", 0);
+// how many tabs can be reopened (per window)
+pref("browser.sessionstore.max_tabs_undo", 25);
+// how many windows can be reopened (per session) - on non-OS X platforms this
+// pref may be ignored when dealing with pop-up windows to ensure proper startup
+pref("browser.sessionstore.max_windows_undo", 3);
+// number of crashes that can occur before the about:sessionrestore page is displayed
+// (this pref has no effect if more than 6 hours have passed since the last crash)
+pref("browser.sessionstore.max_resumed_crashes", 1);
+// number of back button session history entries to restore (-1 = all of them)
+pref("browser.sessionstore.max_serialize_back", 10);
+// number of forward button session history entries to restore (-1 = all of them)
+pref("browser.sessionstore.max_serialize_forward", -1);
+// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
+// and restore_hidden_tabs. When true, tabs will not be restored until they are
+// focused (also applies to tabs that aren't visible). When false, the values
+// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected.
+// Selected tabs are always restored regardless of this pref.
+pref("browser.sessionstore.restore_on_demand", true);
+// Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
+pref("browser.sessionstore.restore_hidden_tabs", false);
+// If restore_on_demand is set, pinned tabs are restored on startup by default.
+// When set to true, this pref overrides that behavior, and pinned tabs will only
+// be restored when they are focused.
+pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
+// The version at which we performed the latest upgrade backup
+pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
+// How many upgrade backups should be kept
+pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3);
+// End-users should not run sessionstore in debug mode
+pref("browser.sessionstore.debug", false);
+// Causes SessionStore to ignore non-final update messages from
+// browser tabs that were not caused by a flush from the parent.
+// This is a testing flag and should not be used by end-users.
+pref("browser.sessionstore.debug.no_auto_updates", false);
+// Forget closed windows/tabs after two weeks
+pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
+// Amount of failed SessionFile writes until we restart the worker.
+pref("browser.sessionstore.max_write_failures", 5);
+
+// Whether to warn the user when quitting, even though their tabs will be restored.
+pref("browser.sessionstore.warnOnQuit", false);
+
+// allow META refresh by default
+pref("accessibility.blockautorefresh", false);
+
+// Whether history is enabled or not.
+pref("places.history.enabled", true);
+
+// Whether or not diacritics must match in history text searches.
+pref("places.search.matchDiacritics", false);
+
+// the (maximum) number of the recent visits to sample
+// when calculating frecency
+pref("places.frecency.numVisits", 10);
+
+// buckets (in days) for frecency calculation
+pref("places.frecency.firstBucketCutoff", 4);
+pref("places.frecency.secondBucketCutoff", 14);
+pref("places.frecency.thirdBucketCutoff", 31);
+pref("places.frecency.fourthBucketCutoff", 90);
+
+// weights for buckets for frecency calculations
+pref("places.frecency.firstBucketWeight", 100);
+pref("places.frecency.secondBucketWeight", 70);
+pref("places.frecency.thirdBucketWeight", 50);
+pref("places.frecency.fourthBucketWeight", 30);
+pref("places.frecency.defaultBucketWeight", 10);
+
+// bonus (in percent) for visit transition types for frecency calculations
+pref("places.frecency.embedVisitBonus", 0);
+pref("places.frecency.framedLinkVisitBonus", 0);
+pref("places.frecency.linkVisitBonus", 100);
+pref("places.frecency.typedVisitBonus", 2000);
+// The bookmarks bonus is always added on top of any other bonus, including
+// the redirect source and the typed ones.
+pref("places.frecency.bookmarkVisitBonus", 75);
+// The redirect source bonus overwrites any transition bonus.
+// 0 would hide these pages, instead we want them low ranked. Thus we use
+// linkVisitBonus - bookmarkVisitBonus, so that a bookmarked source is in par
+// with a common link.
+pref("places.frecency.redirectSourceVisitBonus", 25);
+pref("places.frecency.downloadVisitBonus", 0);
+// The perm/temp redirects here relate to redirect targets, not sources.
+pref("places.frecency.permRedirectVisitBonus", 50);
+pref("places.frecency.tempRedirectVisitBonus", 40);
+pref("places.frecency.reloadVisitBonus", 0);
+pref("places.frecency.defaultVisitBonus", 0);
+
+// bonus (in percent) for place types for frecency calculations
+pref("places.frecency.unvisitedBookmarkBonus", 140);
+pref("places.frecency.unvisitedTypedBonus", 200);
+
+// Controls behavior of the "Add Exception" dialog launched from SSL error pages
+// 0 - don't pre-populate anything
+// 1 - pre-populate site URL, but don't fetch certificate
+// 2 - pre-populate site URL and pre-fetch certificate
+pref("browser.ssl_override_behavior", 2);
+
+// if true, use full page zoom instead of text zoom
+pref("browser.zoom.full", true);
+
+// Whether or not to save and restore zoom levels on a per-site basis.
+pref("browser.zoom.siteSpecific", true);
+
+// Whether or not to update background tabs to the current zoom level.
+pref("browser.zoom.updateBackgroundTabs", true);
+
+// The breakpad report server to link to in about:crashes
+pref("breakpad.reportURL", "https://crash-stats.mozilla.org/report/index/");
+
+// URL for "Learn More" for DataCollection
+pref("toolkit.datacollection.infoURL",
+ "https://www.mozilla.org/legal/privacy/firefox.html");
+
+// URL for "Learn More" for Crash Reporter
+pref("toolkit.crashreporter.infoURL",
+ "https://www.mozilla.org/legal/privacy/firefox.html#crash-reporter");
+
+// base URL for web-based support pages
+pref("app.support.baseURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/");
+
+// base url for web-based feedback pages
+#ifdef MOZ_DEV_EDITION
+ pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/firefoxdev/%VERSION%/");
+#else
+ pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/%APP%/%VERSION%/");
+#endif
+
+// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
+pref("security.alternate_certificate_error_page", "certerror");
+
+pref("security.certerrors.recordEventTelemetry", true);
+pref("security.certerrors.permanentOverride", true);
+pref("security.certerrors.mitm.priming.enabled", true);
+pref("security.certerrors.mitm.priming.endpoint", "https://mitmdetection.services.mozilla.com/");
+pref("security.certerrors.mitm.auto_enable_enterprise_roots", true);
+
+// Whether the bookmark panel should be shown when bookmarking a page.
+pref("browser.bookmarks.editDialog.showForNewBookmarks", true);
+
+// Don't try to alter this pref, it'll be reset the next time you use the
+// bookmarking dialog
+pref("browser.bookmarks.editDialog.firstEditField", "namePicker");
+
+// The number of recently selected folders in the edit bookmarks dialog.
+pref("browser.bookmarks.editDialog.maxRecentFolders", 7);
+
+pref("dom.ipc.plugins.flash.disable-protected-mode", false);
+
+// Feature-disable the protected-mode auto-flip
+pref("browser.flash-protected-mode-flip.enable", false);
+
+// Whether we've already flipped protected mode automatically
+pref("browser.flash-protected-mode-flip.done", false);
+
+pref("dom.ipc.shims.enabledWarnings", false);
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ // Controls whether and how the Windows NPAPI plugin process is sandboxed.
+ // To get a different setting for a particular plugin replace "default", with
+ // the plugin's nice file name, see: nsPluginTag::GetNiceFileName.
+ // On windows these levels are:
+ // 0 - no sandbox
+ // 1 - sandbox with USER_NON_ADMIN access token level
+ // 2 - a more strict sandbox, which might cause functionality issues. This now
+ // includes running at low integrity.
+ // 3 - the strongest settings we seem to be able to use without breaking
+ // everything, but will probably cause some functionality restrictions
+ pref("dom.ipc.plugins.sandbox-level.default", 0);
+ #if defined(_AMD64_)
+ // The base sandbox level in nsPluginTag::InitSandboxLevel must be
+ // updated to keep in sync with this value.
+ pref("dom.ipc.plugins.sandbox-level.flash", 3);
+ #else
+ pref("dom.ipc.plugins.sandbox-level.flash", 0);
+ #endif
+
+ // This controls the strength of the Windows content process sandbox for
+ // testing purposes. This will require a restart.
+ // On windows these levels are:
+ // See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+ // SetSecurityLevelForContentProcess() for what the different settings mean.
+ pref("security.sandbox.content.level", 6);
+
+ // This controls the strength of the Windows GPU process sandbox. Changes
+ // will require restart.
+ // For information on what the level number means, see
+ // SetSecurityLevelForGPUProcess() in
+ // security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+ pref("security.sandbox.gpu.level", 0);
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ // This pref is discussed in bug 1083344, the naming is inspired from its
+ // Windows counterpart, but on Mac it's an integer which means:
+ // 0 -> "no sandbox" (nightly only)
+ // 1 -> "preliminary content sandboxing enabled: write access to
+ // home directory is prevented"
+ // 2 -> "preliminary content sandboxing enabled with profile protection:
+ // write access to home directory is prevented, read and write access
+ // to ~/Library and profile directories are prevented (excluding
+ // $PROFILE/{extensions,chrome})"
+ // 3 -> "no global read/write access, read access permitted to
+ // $PROFILE/{extensions,chrome}"
+ // This setting is read when the content process is started. On Mac the
+ // content process is killed when all windows are closed, so a change will
+ // take effect when the 1st window is opened.
+ pref("security.sandbox.content.level", 3);
+
+ // Prefs for controlling whether and how the Mac NPAPI Flash plugin process is
+ // sandboxed. On Mac these levels are:
+ // 0 - "no sandbox"
+ // 1 - "global read access, limited write access for Flash functionality"
+ // 2 - "read access triggered by file dialog activity, limited read/write"
+ // "access for Flash functionality"
+ // 3 - "limited read/write access for Flash functionality"
+ pref("dom.ipc.plugins.sandbox-level.flash", 1);
+ // Controls the level used on older OS X versions. Is overriden when the
+ // "dom.ipc.plugins.sandbox-level.flash" is set to 0.
+ pref("dom.ipc.plugins.sandbox-level.flash.legacy", 1);
+ // The max OS minor version where we use the above legacy sandbox level.
+ pref("dom.ipc.plugins.sandbox-level.flash.max-legacy-os-minor", 10);
+ // Controls the sandbox level used by plugins other than Flash. On Mac,
+ // no other plugins are supported and this pref is only used for test
+ // plugins used in automated tests.
+ pref("dom.ipc.plugins.sandbox-level.default", 1);
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ // This pref is introduced as part of bug 742434, the naming is inspired from
+ // its Windows/Mac counterpart, but on Linux it's an integer which means:
+ // 0 -> "no sandbox"
+ // 1 -> "content sandbox using seccomp-bpf when available" + ipc restrictions
+ // 2 -> "seccomp-bpf + write file broker"
+ // 3 -> "seccomp-bpf + read/write file brokering"
+ // 4 -> all of the above + network/socket restrictions + chroot
+ //
+ // The purpose of this setting is to allow Linux users or distros to disable
+ // the sandbox while we fix their problems, or to allow running Firefox with
+ // exotic configurations we can't reasonably support out of the box.
+ //
+ pref("security.sandbox.content.level", 4);
+ // Introduced as part of bug 1608558. Linux is currently the only platform
+ // that uses a sandbox level for the socket process. There are currently
+ // only 2 levels:
+ // 0 -> "no sandbox"
+ // 1 -> "sandboxed, allows socket operations and reading necessary certs"
+ pref("security.sandbox.socket.process.level", 1);
+ pref("security.sandbox.content.write_path_whitelist", "");
+ pref("security.sandbox.content.read_path_whitelist", "");
+ pref("security.sandbox.content.syscall_whitelist", "");
+#endif
+
+#if defined(XP_OPENBSD) && defined(MOZ_SANDBOX)
+ pref("security.sandbox.content.level", 1);
+#endif
+
+#if defined(MOZ_SANDBOX)
+ // ID (a UUID when set by gecko) that is used to form the name of a
+ // sandbox-writable temporary directory to be used by content processes
+ // when a temporary writable file is required in a level 1 sandbox.
+ pref("security.sandbox.content.tempDirSuffix", "");
+ pref("security.sandbox.plugin.tempDirSuffix", "");
+
+ // This pref determines if messages relevant to sandbox violations are
+ // logged.
+ #if defined(XP_WIN) || defined(XP_MACOSX)
+ pref("security.sandbox.logging.enabled", false);
+ #endif
+#endif
+
+// This pref governs whether we attempt to work around problems caused by
+// plugins using OS calls to manipulate the cursor while running out-of-
+// process. These workarounds all involve intercepting (hooking) certain
+// OS calls in the plugin process, then arranging to make certain OS calls
+// in the browser process. Eventually plugins will be required to use the
+// NPAPI to manipulate the cursor, and these workarounds will be removed.
+// See bug 621117.
+#ifdef XP_MACOSX
+ pref("dom.ipc.plugins.nativeCursorSupport", true);
+#endif
+
+#ifdef XP_WIN
+ pref("browser.taskbar.previews.enable", false);
+ pref("browser.taskbar.previews.max", 20);
+ pref("browser.taskbar.previews.cachetime", 5);
+ pref("browser.taskbar.lists.enabled", true);
+ pref("browser.taskbar.lists.frequent.enabled", true);
+ pref("browser.taskbar.lists.recent.enabled", false);
+ pref("browser.taskbar.lists.maxListItemCount", 7);
+ pref("browser.taskbar.lists.tasks.enabled", true);
+ pref("browser.taskbar.lists.refreshInSeconds", 120);
+#endif
+
+// Preferences to be synced by default
+pref("services.sync.prefs.sync.accessibility.blockautorefresh", true);
+pref("services.sync.prefs.sync.accessibility.browsewithcaret", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true);
+pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true);
+pref("services.sync.prefs.sync.app.shield.optoutstudies.enabled", true);
+// The addons prefs related to repository verification are intentionally
+// not synced for security reasons. If a system is compromised, a user
+// could weaken the pref locally, install an add-on from an untrusted
+// source, and this would propagate automatically to other,
+// uncompromised Sync-connected devices.
+pref("services.sync.prefs.sync.browser.contentblocking.category", true);
+pref("services.sync.prefs.sync.browser.contentblocking.features.strict", true);
+pref("services.sync.prefs.sync.browser.crashReports.unsubmittedCheck.autoSubmit2", true);
+pref("services.sync.prefs.sync.browser.ctrlTab.recentlyUsedOrder", true);
+pref("services.sync.prefs.sync.browser.discovery.enabled", true);
+pref("services.sync.prefs.sync.browser.download.useDownloadDir", true);
+pref("services.sync.prefs.sync.browser.formfill.enable", true);
+pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSearch", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSponsored", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSponsoredTopSites", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.topsites", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.topSitesRows", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.snippets", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.topstories", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.topstories.rows", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.highlights", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeVisited", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeBookmarks", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeDownloads", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includePocket", true);
+pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.rows", true);
+pref("services.sync.prefs.sync.browser.newtabpage.enabled", true);
+pref("services.sync.prefs.sync.browser.newtabpage.pinned", true);
+pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.downloads.enabled", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.downloads.remote.block_potentially_unwanted", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.phishing.enabled", true);
+pref("services.sync.prefs.sync.browser.search.update", true);
+pref("services.sync.prefs.sync.browser.search.widget.inNavBar", true);
+pref("services.sync.prefs.sync.browser.sessionstore.warnOnQuit", true);
+pref("services.sync.prefs.sync.browser.startup.homepage", true);
+pref("services.sync.prefs.sync.browser.startup.page", true);
+pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
+pref("services.sync.prefs.sync.browser.taskbar.previews.enable", true);
+pref("services.sync.prefs.sync.browser.urlbar.matchBuckets", true);
+pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.bookmark", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.history", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.openpage", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.searches", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.topsites", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.engines", true);
+pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
+pref("services.sync.prefs.sync.dom.disable_window_flip", true);
+pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
+pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
+pref("services.sync.prefs.sync.dom.security.https_only_mode", true);
+pref("services.sync.prefs.sync.dom.security.https_only_mode_ever_enabled", true);
+pref("services.sync.prefs.sync.dom.security.https_only_mode_ever_enabled_pbm", true);
+pref("services.sync.prefs.sync.dom.security.https_only_mode_pbm", true);
+pref("services.sync.prefs.sync.extensions.update.enabled", true);
+pref("services.sync.prefs.sync.extensions.activeThemeID", true);
+pref("services.sync.prefs.sync.general.autoScroll", true);
+pref("services.sync.prefs.sync.general.smoothScroll", true);
+pref("services.sync.prefs.sync.intl.accept_languages", true);
+pref("services.sync.prefs.sync.intl.regional_prefs.use_os_locales", true);
+pref("services.sync.prefs.sync.layout.spellcheckDefault", true);
+pref("services.sync.prefs.sync.media.autoplay.default", true);
+pref("services.sync.prefs.sync.media.eme.enabled", true);
+pref("services.sync.prefs.sync.media.videocontrols.picture-in-picture.video-toggle.enabled", true);
+pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
+pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true);
+pref("services.sync.prefs.sync.network.cookie.thirdparty.sessionOnly", true);
+pref("services.sync.prefs.sync.permissions.default.image", true);
+pref("services.sync.prefs.sync.pref.downloads.disable_button.edit_actions", true);
+pref("services.sync.prefs.sync.pref.privacy.disable_button.cookie_exceptions", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cache", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
+pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
+pref("services.sync.prefs.sync.privacy.fuzzyfox.enabled", false);
+pref("services.sync.prefs.sync.privacy.fuzzyfox.clockgrainus", false);
+pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
+pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
+pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
+pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
+pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
+pref("services.sync.prefs.sync.privacy.userContext.enabled", true);
+pref("services.sync.prefs.sync.privacy.userContext.newTabContainerOnLeftClick.enabled", true);
+pref("services.sync.prefs.sync.security.default_personal_cert", true);
+pref("services.sync.prefs.sync.services.sync.syncedTabs.showRemoteIcons", true);
+pref("services.sync.prefs.sync.signon.autofillForms", true);
+pref("services.sync.prefs.sync.signon.generation.enabled", true);
+pref("services.sync.prefs.sync.signon.management.page.breach-alerts.enabled", true);
+pref("services.sync.prefs.sync.signon.rememberSignons", true);
+pref("services.sync.prefs.sync.spellchecker.dictionary", true);
+pref("services.sync.prefs.sync.ui.osk.enabled", true);
+
+// A preference which, if false, means sync will only apply incoming preference
+// changes if there's already a local services.sync.prefs.sync.* control pref.
+// If true, all incoming preferences will be applied and the local "control
+// pref" updated accordingly.
+pref("services.sync.prefs.dangerously_allow_arbitrary", false);
+
+// A preference that controls whether we should show the icon for a remote tab.
+// This pref has no UI but exists because some people may be concerned that
+// fetching these icons to show remote tabs may leak information about that
+// user's tabs and bookmarks. Note this pref is also synced.
+pref("services.sync.syncedTabs.showRemoteIcons", true);
+
+// Whether the character encoding menu is under the main Firefox button. This
+// preference is a string so that localizers can alter it.
+pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
+
+// Whether prompts should be content modal (1) tab modal (2) or window modal(3) by default
+// This is a fallback value for when prompt callers do not specify a modalType.
+pref("prompts.defaultModalType", 3);
+
+pref("browser.topsites.useRemoteSetting", false);
+
+// The base URL for the Quick Suggest anonymizing proxy. To make a request to
+// the proxy, include a campaign ID in the path.
+pref("browser.partnerlink.attributionURL", "https://topsites.services.mozilla.com/cid/");
+pref("browser.partnerlink.campaign.topsites", "amzn_2020_a1");
+
+// Whether to show tab level system prompts opened via nsIPrompt(Service) as
+// SubDialogs in the TabDialogBox (true) or as TabModalPrompt in the
+// TabModalPromptBox (false).
+pref("prompts.tabChromePromptSubDialog", true);
+
+// Whether to show the dialogs opened at the content level, such as
+// alert() or prompt(), using a SubDialogManager in the TabDialogBox.
+#ifdef EARLY_BETA_OR_EARLIER
+ pref("prompts.contentPromptSubDialog", true);
+#else
+ pref("prompts.contentPromptSubDialog", false);
+#endif
+
+// Activates preloading of the new tab url.
+pref("browser.newtab.preload", true);
+
+// Preference to enable the entire new newtab experience at once.
+pref("browser.newtabpage.activity-stream.newNewtabExperience.enabled", false);
+
+// A preference which allows us to enable the fly out customization overlay
+// on the newtab page.
+pref("browser.newtabpage.activity-stream.customizationMenu.enabled", false);
+pref("browser.newtabpage.activity-stream.newNewtabExperience.colors", "#0090ED,#FF4F5F,#2AC3A2,#FF7139,#A172FF,#FFA437,#FF2A8A");
+
+// Activity Stream prefs that control to which page to redirect
+#ifndef RELEASE_OR_BETA
+ pref("browser.newtabpage.activity-stream.debug", false);
+#endif
+
+pref("browser.library.activity-stream.enabled", true);
+
+// The remote FxA root content URL for the Activity Stream firstrun page.
+pref("browser.newtabpage.activity-stream.fxaccounts.endpoint", "https://accounts.firefox.com/");
+
+// The pref that controls if the search shortcuts experiment is on
+pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts", true);
+
+// ASRouter provider configuration
+pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"cfr\",\"updateCycleInMs\":3600000}");
+pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "{\"id\":\"whats-new-panel\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"whats-new-panel\",\"updateCycleInMs\":3600000}");
+pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"message-groups\",\"updateCycleInMs\":3600000}");
+// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
+// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
+// repackager of this code using an alternate snippet url, please keep your users safe
+pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "{\"id\":\"snippets\",\"enabled\":true,\"type\":\"remote\",\"url\":\"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/\",\"updateCycleInMs\":14400000}");
+pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"messageGroups\":[\"cfr\",\"whats-new-panel\",\"moments-page\",\"snippets\",\"cfr-fxa\",\"aboutwelcome\",\"infobar\"],\"updateCycleInMs\":3600000}");
+
+// ASRouter user prefs
+pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", true);
+pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", true);
+
+// The pref that controls if ASRouter uses the remote fluent files.
+// It's enabled by default, but could be disabled to force ASRouter to use the local files.
+pref("browser.newtabpage.activity-stream.asrouter.useRemoteL10n", true);
+
+// These prefs control if Discovery Stream is enabled.
+pref("browser.newtabpage.activity-stream.discoverystream.enabled", true);
+pref("browser.newtabpage.activity-stream.discoverystream.hardcoded-basic-layout", false);
+pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint", "");
+pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint-query", "");
+
+// List of regions that do not get stories, regardless of locale-list-config.
+pref("browser.newtabpage.activity-stream.discoverystream.region-stories-block", "FR");
+// List of locales that get stories, regardless of region-stories-config.
+#ifdef NIGHTLY_BUILD
+ pref("browser.newtabpage.activity-stream.discoverystream.locale-list-config", "en-US,en-CA,en-GB");
+#else
+ pref("browser.newtabpage.activity-stream.discoverystream.locale-list-config", "");
+#endif
+// List of regions that get stories by default.
+pref("browser.newtabpage.activity-stream.discoverystream.region-stories-config", "US,DE,CA,GB,IE,CH,AT,BE,IN");
+
+// List of regions that get spocs by default.
+pref("browser.newtabpage.activity-stream.discoverystream.region-spocs-config", "US,CA,DE,GB");
+// List of regions that don't get the 7 row layout.
+pref("browser.newtabpage.activity-stream.discoverystream.region-basic-config", "");
+
+// Allows Pocket story collections to be dismissed.
+pref("browser.newtabpage.activity-stream.discoverystream.isCollectionDismissible", true);
+pref("browser.newtabpage.activity-stream.discoverystream.personalization.version", 2);
+// Configurable keys used by personalization version 2.
+pref("browser.newtabpage.activity-stream.discoverystream.personalization.modelKeys", "nb_model_arts_and_entertainment, nb_model_autos_and_vehicles, nb_model_beauty_and_fitness, nb_model_blogging_resources_and_services, nb_model_books_and_literature, nb_model_business_and_industrial, nb_model_computers_and_electronics, nb_model_finance, nb_model_food_and_drink, nb_model_games, nb_model_health, nb_model_hobbies_and_leisure, nb_model_home_and_garden, nb_model_internet_and_telecom, nb_model_jobs_and_education, nb_model_law_and_government, nb_model_online_communities, nb_model_people_and_society, nb_model_pets_and_animals, nb_model_real_estate, nb_model_reference, nb_model_science, nb_model_shopping, nb_model_sports, nb_model_travel");
+// System pref to allow Pocket stories personalization to be turned on/off.
+pref("browser.newtabpage.activity-stream.discoverystream.recs.personalized", false);
+// System pref to allow Pocket sponsored content personalization to be turned on/off.
+pref("browser.newtabpage.activity-stream.discoverystream.spocs.personalized", true);
+
+// User pref to show stories on newtab (feeds.system.topstories has to be set to true as well)
+pref("browser.newtabpage.activity-stream.feeds.section.topstories", true);
+
+// The pref controls if search hand-off is enabled for Activity Stream.
+#ifdef NIGHTLY_BUILD
+ pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", true);
+#else
+ pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", false);
+#endif
+
+pref("browser.newtabpage.activity-stream.logowordmark.alwaysVisible", false);
+
+// Used to display triplet cards on newtab
+pref("trailhead.firstrun.newtab.triplets", "");
+// Separate about welcome
+pref("browser.aboutwelcome.enabled", true);
+// Used to set multistage welcome UX
+pref("browser.aboutwelcome.overrideContent", "");
+
+// The pref that controls if the What's New panel is enabled.
+pref("browser.messaging-system.whatsNewPanel.enabled", true);
+// Used for CFR messages with scores. See Bug 1594422.
+pref("browser.messaging-system.personalized-cfr.scores", "{}");
+pref("browser.messaging-system.personalized-cfr.score-threshold", 5000);
+
+// Experiment Manager
+// See Console.jsm LOG_LEVELS for all possible values
+pref("messaging-system.log", "warn");
+pref("messaging-system.rsexperimentloader.enabled", true);
+pref("messaging-system.rsexperimentloader.collection_id", "nimbus-desktop-experiments");
+
+// Enable the DOM fullscreen API.
+pref("full-screen-api.enabled", true);
+
+// Startup Crash Tracking
+// number of startup crashes that can occur before starting into safe mode automatically
+// (this pref has no effect if more than 6 hours have passed since the last crash)
+pref("toolkit.startup.max_resumed_crashes", 3);
+
+// Whether to use RegisterApplicationRestart to restart the browser and resume
+// the session on next Windows startup
+#if defined(XP_WIN)
+ pref("toolkit.winRegisterApplicationRestart", true);
+#endif
+
+// Completely disable pdf.js as an option to preview pdfs within firefox.
+// Note: if this is not disabled it does not necessarily mean pdf.js is the pdf
+// handler just that it is an option.
+pref("pdfjs.disabled", false);
+// Used by pdf.js to know the first time firefox is run with it installed so it
+// can become the default pdf viewer.
+pref("pdfjs.firstRun", true);
+// The values of preferredAction and alwaysAskBeforeHandling before pdf.js
+// became the default.
+pref("pdfjs.previousHandler.preferredAction", 0);
+pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
+
+// Try to convert PDFs sent as octet-stream
+pref("pdfjs.handleOctetStream", true);
+
+// Is the sidebar positioned ahead of the content browser
+pref("sidebar.position_start", true);
+
+pref("security.identitypopup.recordEventTelemetry", true);
+pref("security.protectionspopup.recordEventTelemetry", true);
+pref("security.app_menu.recordEventTelemetry", true);
+
+// Block insecure active content on https pages
+pref("security.mixed_content.block_active_content", true);
+
+// Show in-content login form warning UI for insecure login fields
+pref("security.insecure_field_warning.contextual.enabled", true);
+
+// Show degraded UI for http pages.
+pref("security.insecure_connection_icon.enabled", true);
+// Show degraded UI for http pages in private mode.
+pref("security.insecure_connection_icon.pbmode.enabled", true);
+
+// For secure connections, show gray instead of green lock icon
+pref("security.secure_connection_icon_color_gray", true);
+
+// Show "Not Secure" text for http pages; disabled for now
+pref("security.insecure_connection_text.enabled", false);
+pref("security.insecure_connection_text.pbmode.enabled", false);
+
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
+
+// If this turns true, Moz*Gesture events are not called stopPropagation()
+// before content.
+pref("dom.debug.propagate_gesture_events_through_content", false);
+
+// CustomizableUI debug logging.
+pref("browser.uiCustomization.debug", false);
+
+// CustomizableUI state of the browser's user interface
+pref("browser.uiCustomization.state", "");
+
+// If set to false, FxAccounts and Sync will be unavailable.
+// A restart is mandatory after flipping that preference.
+pref("identity.fxaccounts.enabled", true);
+
+// The remote FxA root content URL. Must use HTTPS.
+pref("identity.fxaccounts.remote.root", "https://accounts.firefox.com/");
+
+// The value of the context query parameter passed in fxa requests.
+pref("identity.fxaccounts.contextParam", "fx_desktop_v3");
+
+// The remote URL of the FxA Profile Server
+pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
+
+// The remote URL of the FxA OAuth Server
+pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
+
+// Whether FxA pairing using QR codes is enabled.
+pref("identity.fxaccounts.pairing.enabled", true);
+
+// The remote URI of the FxA pairing server
+pref("identity.fxaccounts.remote.pairing.uri", "wss://channelserver.services.mozilla.com");
+
+// Token server used by the FxA Sync identity.
+pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
+
+// Fetch Sync tokens using the OAuth token function
+pref("identity.sync.useOAuthForSyncToken", true);
+
+// Using session tokens to fetch OAuth tokens
+pref("identity.fxaccounts.useSessionTokensForOAuth", true);
+
+// Auto-config URL for FxA self-hosters, makes an HTTP request to
+// [identity.fxaccounts.autoconfig.uri]/.well-known/fxa-client-configuration
+// This is now the prefered way of pointing to a custom FxA server, instead
+// of making changes to "identity.fxaccounts.*.uri".
+pref("identity.fxaccounts.autoconfig.uri", "");
+
+// URL for help link about Send Tab.
+pref("identity.sendtabpromo.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/send-tab");
+
+// URLs for promo links to mobile browsers. Note that consumers are expected to
+// append a value for utm_campaign.
+pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
+pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
+
+// Migrate any existing Firefox Account data from the default profile to the
+// Developer Edition profile.
+#ifdef MOZ_DEV_EDITION
+ pref("identity.fxaccounts.migrateToDevEdition", true);
+#else
+ pref("identity.fxaccounts.migrateToDevEdition", false);
+#endif
+
+// If activated, send tab will use the new FxA commands backend.
+pref("identity.fxaccounts.commands.enabled", true);
+// How often should we try to fetch missed FxA commands on sync (in seconds).
+// Default is 24 hours.
+pref("identity.fxaccounts.commands.missed.fetch_interval", 86400);
+
+// Whether we should run a test-pattern through EME GMPs before assuming they'll
+// decode H.264.
+pref("media.gmp.trial-create.enabled", true);
+
+// Note: when media.gmp-*.visible is true, provided we're running on a
+// supported platform/OS version, the corresponding CDM appears in the
+// plugins list, Firefox will download the GMP/CDM if enabled, and our
+// UI to re-enable EME prompts the user to re-enable EME if it's disabled
+// and script requests EME. If *.visible is false, we won't show the UI
+// to enable the CDM if its disabled; it's as if the keysystem is completely
+// unsupported.
+
+#ifdef MOZ_WIDEVINE_EME
+ pref("media.gmp-widevinecdm.visible", true);
+ pref("media.gmp-widevinecdm.enabled", true);
+#endif
+
+pref("media.gmp-gmpopenh264.visible", true);
+pref("media.gmp-gmpopenh264.enabled", true);
+
+// Set Firefox to block autoplay, asking for permission by default.
+pref("media.autoplay.default", 1); // 0=Allowed, 1=Blocked, 5=All Blocked
+
+#ifdef NIGHTLY_BUILD
+ // Block WebAudio from playing automatically.
+ pref("media.autoplay.block-webaudio", true);
+#else
+ pref("media.autoplay.block-webaudio", false);
+#endif
+
+pref("media.videocontrols.picture-in-picture.enabled", true);
+pref("media.videocontrols.picture-in-picture.video-toggle.enabled", true);
+pref("media.videocontrols.picture-in-picture.keyboard-controls.enabled", true);
+
+#ifdef NIGHTLY_BUILD
+ // Show the audio toggle for Picture-in-Picture.
+ pref("media.videocontrols.picture-in-picture.audio-toggle.enabled", true);
+ // Enable keyboard controls for Picture-in-Picture.
+#else
+ pref("media.videocontrols.picture-in-picture.audio-toggle.enabled", false);
+#endif
+
+pref("browser.translation.detectLanguage", false);
+pref("browser.translation.neverForLanguages", "");
+// Show the translation UI bits, like the info bar, notification icon and preferences.
+pref("browser.translation.ui.show", false);
+// Allows to define the translation engine. Google is default, Bing or Yandex are other options.
+pref("browser.translation.engine", "Google");
+
+// Telemetry settings.
+// Determines if Telemetry pings can be archived locally.
+pref("toolkit.telemetry.archive.enabled", true);
+// Enables sending the shutdown ping when Firefox shuts down.
+pref("toolkit.telemetry.shutdownPingSender.enabled", true);
+// Enables sending the shutdown ping using the pingsender from the first session.
+pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false);
+// Enables sending a duplicate of the first shutdown ping from the first session.
+pref("toolkit.telemetry.firstShutdownPing.enabled", true);
+// Enables sending the 'new-profile' ping on new profiles.
+pref("toolkit.telemetry.newProfilePing.enabled", true);
+// Enables sending 'update' pings on Firefox updates.
+pref("toolkit.telemetry.updatePing.enabled", true);
+// Enables sending 'bhr' pings when the browser hangs.
+pref("toolkit.telemetry.bhrPing.enabled", true);
+// Whether to enable Ecosystem Telemetry, requires a restart.
+pref("toolkit.telemetry.ecosystemtelemetry.enabled", false);
+
+// Ping Centre Telemetry settings.
+pref("browser.ping-centre.telemetry", true);
+pref("browser.ping-centre.log", false);
+
+// Enable GMP support in the addon manager.
+pref("media.gmp-provider.enabled", true);
+
+#ifdef NIGHTLY_BUILD
+// Enable Dynamic First-Party Isolation in Nightly.
+pref("network.cookie.cookieBehavior", 5 /* BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN */);
+#else
+// Enable blocking access to storage from tracking resources by default.
+pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
+#endif
+
+// Enable fingerprinting blocking by default for all channels, only on desktop.
+pref("privacy.trackingprotection.fingerprinting.enabled", true);
+
+// Enable cryptomining blocking by default for all channels, only on desktop.
+pref("privacy.trackingprotection.cryptomining.enabled", true);
+
+pref("browser.contentblocking.database.enabled", true);
+
+pref("dom.storage_access.enabled", true);
+
+pref("browser.contentblocking.cryptomining.preferences.ui.enabled", true);
+pref("browser.contentblocking.fingerprinting.preferences.ui.enabled", true);
+#ifdef NIGHTLY_BUILD
+ // Enable cookieBehavior = BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN as an option in the custom category ui
+ pref("browser.contentblocking.reject-and-isolate-cookies.preferences.ui.enabled", true);
+#endif
+// State Partitioning MVP UI.
+pref("browser.contentblocking.state-partitioning.mvp.ui.enabled", true);
+
+// Possible values for browser.contentblocking.features.strict pref:
+// Tracking Protection:
+// "tp": tracking protection enabled
+// "-tp": tracking protection disabled
+// Tracking Protection in private windows:
+// "tpPrivate": tracking protection in private windows enabled
+// "-tpPrivate": tracking protection in private windows disabled
+// Fingerprinting:
+// "fp": fingerprinting blocking enabled
+// "-fp": fingerprinting blocking disabled
+// Cryptomining:
+// "cm": cryptomining blocking enabled
+// "-cm": cryptomining blocking disabled
+// Social Tracking Protection:
+// "stp": social tracking protection enabled
+// "-stp": social tracking protection disabled
+// Cookie behavior:
+// "cookieBehavior0": cookie behaviour BEHAVIOR_ACCEPT
+// "cookieBehavior1": cookie behaviour BEHAVIOR_REJECT_FOREIGN
+// "cookieBehavior2": cookie behaviour BEHAVIOR_REJECT
+// "cookieBehavior3": cookie behaviour BEHAVIOR_LIMIT_FOREIGN
+// "cookieBehavior4": cookie behaviour BEHAVIOR_REJECT_TRACKER
+// "cookieBehavior5": cookie behaviour BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
+// One value from each section must be included in the browser.contentblocking.features.strict pref.
+pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cm,fp,stp,lvl2");
+
+// Hide the "Change Block List" link for trackers/tracking content in the custom
+// Content Blocking/ETP panel. By default, it will not be visible. There is also
+// an UI migration in place to set this pref to true if a user has a custom block
+// lists enabled.
+pref("browser.contentblocking.customBlockList.preferences.ui.enabled", false);
+
+pref("browser.contentblocking.reportBreakage.url", "https://tracking-protection-issues.herokuapp.com/new");
+
+// Enable Protections report's Lockwise card by default.
+pref("browser.contentblocking.report.lockwise.enabled", true);
+
+// Enable Protections report's Monitor card by default.
+pref("browser.contentblocking.report.monitor.enabled", true);
+
+// Disable Protections report's Proxy card by default.
+pref("browser.contentblocking.report.proxy.enabled", false);
+
+// Disable the mobile promotion by default.
+pref("browser.contentblocking.report.show_mobile_app", true);
+
+// Enable the vpn card by default.
+pref("browser.contentblocking.report.vpn.enabled", true);
+// Only show vpn card to certain regions. Comma separated string of two letter ISO 3166-1 country codes.
+pref("browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb");
+// Comma separated string of mozilla vpn supported platforms.
+pref("browser.contentblocking.report.vpn_platforms", "win");
+pref("browser.contentblocking.report.hide_vpn_banner", false);
+pref("browser.contentblocking.report.vpn_sub_id", "sub_HrfCZF7VPHzZkA");
+
+pref("browser.contentblocking.report.monitor.url", "https://monitor.firefox.com/?entrypoint=protection_report_monitor&utm_source=about-protections");
+pref("browser.contentblocking.report.monitor.how_it_works.url", "https://monitor.firefox.com/about");
+pref("browser.contentblocking.report.monitor.sign_in_url", "https://monitor.firefox.com/oauth/init?entrypoint=protection_report_monitor&utm_source=about-protections&email=");
+pref("browser.contentblocking.report.monitor.preferences_url", "https://monitor.firefox.com/user/preferences");
+pref("browser.contentblocking.report.monitor.home_page_url", "https://monitor.firefox.com/user/dashboard");
+pref("browser.contentblocking.report.manage_devices.url", "https://accounts.firefox.com/settings/clients");
+pref("browser.contentblocking.report.endpoint_url", "https://monitor.firefox.com/user/breach-stats?includeResolved=true");
+pref("browser.contentblocking.report.proxy_extension.url", "https://fpn.firefox.com/browser?utm_source=firefox-desktop&utm_medium=referral&utm_campaign=about-protections&utm_content=about-protections");
+pref("browser.contentblocking.report.mobile-ios.url", "https://apps.apple.com/app/firefox-private-safe-browser/id989804926");
+pref("browser.contentblocking.report.mobile-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox&referrer=utm_source%3Dprotection_report%26utm_content%3Dmobile_promotion");
+pref("browser.contentblocking.report.vpn.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-card");
+pref("browser.contentblocking.report.vpn-promo.url", "https://vpn.mozilla.org/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=about-protections-top-promo");
+pref("browser.contentblocking.report.vpn-android.url", "https://play.google.com/store/apps/details?id=org.mozilla.firefox.vpn&referrer=utm_source%3Dfirefox-browser%26utm_medium%3Dfirefox-browser%26utm_campaign%3Dabout-protections-mobile-vpn%26anid%3D--");
+pref("browser.contentblocking.report.vpn-ios.url", "https://apps.apple.com/us/app/firefox-private-network-vpn/id1489407738");
+
+// Protection Report's SUMO urls
+pref("browser.contentblocking.report.lockwise.how_it_works.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/password-manager-report");
+pref("browser.contentblocking.report.social.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/social-media-tracking-report");
+pref("browser.contentblocking.report.cookie.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cross-site-tracking-report");
+pref("browser.contentblocking.report.tracker.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/tracking-content-report");
+pref("browser.contentblocking.report.fingerprinter.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/fingerprinters-report");
+pref("browser.contentblocking.report.cryptominer.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cryptominers-report");
+
+pref("browser.contentblocking.cfr-milestone.enabled", true);
+pref("browser.contentblocking.cfr-milestone.milestone-achieved", 0);
+// Milestones should always be in increasing order
+pref("browser.contentblocking.cfr-milestone.milestones", "[1000, 5000, 10000, 25000, 50000, 100000, 250000, 314159, 500000, 750000, 1000000, 1250000, 1500000, 1750000, 2000000, 2250000, 2500000, 8675309]");
+
+// Enables the new Protections Panel.
+#ifdef NIGHTLY_BUILD
+ pref("browser.protections_panel.enabled", true);
+ pref("browser.protections_panel.infoMessage.seen", false);
+#endif
+
+// Always enable newtab segregation using containers
+pref("privacy.usercontext.about_newtab_segregation.enabled", true);
+// Enable Contextual Identity Containers
+#ifdef NIGHTLY_BUILD
+ pref("privacy.userContext.enabled", true);
+ pref("privacy.userContext.ui.enabled", true);
+#else
+ pref("privacy.userContext.enabled", false);
+ pref("privacy.userContext.ui.enabled", false);
+#endif
+pref("privacy.userContext.extension", "");
+// allows user to open container menu on a left click instead of a new
+// tab in the default container
+pref("privacy.userContext.newTabContainerOnLeftClick.enabled", false);
+
+#if defined(NIGHTLY_BUILD) || defined(XP_WIN) || defined(XP_MACOSX)
+// Set to true to allow the user to silence all notifications when
+// sharing the screen. Only shipping on Windows and macOS out to
+// release. Enabled for all desktop platforms on Nightly.
+pref("privacy.webrtc.allowSilencingNotifications", true);
+// Set to true to use the legacy WebRTC global indicator
+pref("privacy.webrtc.legacyGlobalIndicator", false);
+pref("privacy.webrtc.hideGlobalIndicator", false);
+#else
+pref("privacy.webrtc.allowSilencingNotifications", false);
+pref("privacy.webrtc.legacyGlobalIndicator", true);
+#endif
+
+// Set to true to add toggles to the WebRTC indicator for globally
+// muting the camera and microphone.
+pref("privacy.webrtc.globalMuteToggles", false);
+
+// Set to true to enable a warning displayed when attempting
+// to switch tabs in a window that's being shared over WebRTC.
+pref("privacy.webrtc.sharedTabWarning", false);
+
+// Start the browser in e10s mode
+pref("browser.tabs.remote.autostart", true);
+pref("browser.tabs.remote.desktopbehavior", true);
+
+// Run media transport in a separate process?
+#ifdef EARLY_BETA_OR_EARLIER
+ pref("media.peerconnection.mtransport_process", true);
+#else
+ pref("media.peerconnection.mtransport_process", false);
+#endif
+
+// For speculatively warming up tabs to improve perceived
+// performance while using the async tab switcher.
+pref("browser.tabs.remote.warmup.enabled", true);
+
+// Caches tab layers to improve perceived performance
+// of tab switches.
+pref("browser.tabs.remote.tabCacheSize", 0);
+
+pref("browser.tabs.remote.warmup.maxTabs", 3);
+pref("browser.tabs.remote.warmup.unloadDelayMs", 2000);
+
+// For the about:tabcrashed page
+pref("browser.tabs.crashReporting.sendReport", true);
+pref("browser.tabs.crashReporting.includeURL", false);
+pref("browser.tabs.crashReporting.requestEmail", false);
+pref("browser.tabs.crashReporting.emailMe", false);
+pref("browser.tabs.crashReporting.email", "");
+
+// If true, unprivileged extensions may use experimental APIs on
+// nightly and developer edition.
+pref("extensions.experiments.enabled", false);
+
+#if defined(XP_WIN)
+ // Allows us to deprioritize the processes of background tabs at an OS level
+ pref("dom.ipc.processPriorityManager.enabled", true);
+#endif
+
+// Don't limit how many nodes we care about on desktop:
+pref("reader.parse-node-limit", 0);
+
+// On desktop, we want the URLs to be included here for ease of debugging,
+// and because (normally) these errors are not persisted anywhere.
+pref("reader.errors.includeURLs", true);
+
+pref("view_source.tab", true);
+
+pref("dom.serviceWorkers.enabled", true);
+
+// Enable Push API.
+pref("dom.push.enabled", true);
+
+// These are the thumbnail width/height set in about:newtab.
+// If you change this, ENSURE IT IS THE SAME SIZE SET
+// by about:newtab. These values are in CSS pixels.
+pref("toolkit.pageThumbs.minWidth", 280);
+pref("toolkit.pageThumbs.minHeight", 190);
+
+// Enable speech synthesis
+pref("media.webspeech.synth.enabled", true);
+
+pref("browser.esedbreader.loglevel", "Error");
+
+pref("browser.laterrun.enabled", false);
+
+pref("dom.ipc.processPrelaunch.enabled", true);
+
+// See comments in bug 1340115 on how we got to these numbers.
+pref("browser.migrate.chrome.history.limit", 2000);
+pref("browser.migrate.chrome.history.maxAgeInDays", 180);
+pref("browser.migrate.showBookmarksToolbarAfterMigration", true);
+
+pref("extensions.pocket.api", "api.getpocket.com");
+pref("extensions.pocket.enabled", true);
+pref("extensions.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
+pref("extensions.pocket.site", "getpocket.com");
+pref("extensions.pocket.onSaveRecs", true);
+pref("extensions.pocket.onSaveRecs.locales", "en-US,en-GB,en-CA");
+
+// Control what version of the logged out doorhanger is displayed
+// Possibilities are: `control`, `control-one-button`, `variant_a`, `variant_b`, `variant_c`
+pref("extensions.pocket.loggedOutVariant", "control");
+
+#ifdef NIGHTLY_BUILD
+pref("signon.management.page.fileImport.enabled", true);
+pref("signon.management.page.os-auth.enabled", true);
+#else
+pref("signon.management.page.fileImport.enabled", false);
+pref("signon.management.page.os-auth.enabled", false);
+#endif
+pref("signon.management.page.breach-alerts.enabled", true);
+pref("signon.management.page.vulnerable-passwords.enabled", true);
+pref("signon.management.page.sort", "name");
+// The utm_creative value is appended within the code (specific to the location on
+// where it is clicked). Be sure that if these two prefs are updated, that
+// the utm_creative param be last.
+pref("signon.management.page.breachAlertUrl",
+ "https://monitor.firefox.com/breach-details/");
+pref("signon.management.page.showPasswordSyncNotification", true);
+pref("signon.passwordEditCapture.enabled", true);
+pref("signon.showAutoCompleteFooter", true);
+pref("signon.showAutoCompleteImport", "import");
+pref("signon.suggestImportCount", 3);
+
+// Enable the "Simplify Page" feature in Print Preview. This feature
+// is disabled by default in toolkit.
+pref("print.use_simplify_page", true);
+
+// Space separated list of URLS that are allowed to send objects (instead of
+// only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
+pref("webchannel.allowObject.urlWhitelist", "https://content.cdn.mozilla.net https://support.mozilla.org https://install.mozilla.org");
+
+// Whether or not the browser should scan for unsubmitted
+// crash reports, and then show a notification for submitting
+// those reports.
+#ifdef NIGHTLY_BUILD
+ pref("browser.crashReports.unsubmittedCheck.enabled", true);
+#else
+ pref("browser.crashReports.unsubmittedCheck.enabled", false);
+#endif
+
+// chancesUntilSuppress is how many times we'll show the unsubmitted
+// crash report notification across different days and shutdown
+// without a user choice before we suppress the notification for
+// some number of days.
+pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4);
+pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);
+
+// Preferences for the form autofill system extension
+// The truthy values of "extensions.formautofill.available" are "on" and "detect",
+// any other value means autofill isn't available.
+// "detect" means it's enabled if conditions defined in the extension are met.
+pref("extensions.formautofill.available", "detect");
+pref("extensions.formautofill.addresses.enabled", true);
+pref("extensions.formautofill.addresses.capture.enabled", false);
+pref("extensions.formautofill.creditCards.available", true);
+pref("extensions.formautofill.creditCards.enabled", true);
+// Checkbox in sync options for credit card data sync service
+pref("services.sync.engine.creditcards.available", true);
+// Temporary preference to control displaying the UI elements for
+// credit card autofill used for the duration of the A/B test.
+pref("extensions.formautofill.creditCards.hideui", false);
+// Pref for shield/heartbeat to recognize users who have used Credit Card
+// Autofill. The valid values can be:
+// 0: none
+// 1: submitted a manually-filled credit card form (but didn't see the doorhanger
+// because of a duplicate profile in the storage)
+// 2: saw the doorhanger
+// 3: submitted an autofill'ed credit card form
+pref("extensions.formautofill.creditCards.used", 0);
+pref("extensions.formautofill.firstTimeUse", true);
+pref("extensions.formautofill.heuristics.enabled", true);
+// Whether the user enabled the OS re-auth dialog.
+pref("extensions.formautofill.reauth.enabled", false);
+pref("extensions.formautofill.section.enabled", true);
+pref("extensions.formautofill.loglevel", "Warn");
+
+pref("toolkit.osKeyStore.loglevel", "Warn");
+
+pref("extensions.formautofill.supportedCountries", "US,CA");
+pref("extensions.formautofill.supportRTL", false);
+
+// Whether or not to restore a session with lazy-browser tabs.
+pref("browser.sessionstore.restore_tabs_lazily", true);
+
+pref("browser.suppress_first_window_animation", true);
+
+// Preference that allows individual users to disable Screenshots.
+pref("extensions.screenshots.disabled", false);
+// Preference that allows individual users to leave Screenshots enabled, but
+// disable uploading to the server.
+pref("extensions.screenshots.upload-disabled", false);
+
+// DoH Rollout: whether to enable automatic performance-based TRR-selection.
+// This pref is controlled by a Normandy rollout so we don't overload providers.
+pref("doh-rollout.trr-selection.enabled", false);
+
+// DoH Rollout: whether to enable automatic steering to provider endpoints.
+// This pref is also controlled by a Normandy rollout.
+pref("doh-rollout.provider-steering.enabled", false);
+
+// DoH Rollout: provider details for automatic steering.
+pref("doh-rollout.provider-steering.provider-list", "[{ \"name\": \"comcast\", \"canonicalName\": \"doh-discovery.xfinity.com\", \"uri\": \"https://doh.xfinity.com/dns-query\" }]");
+
+// DoH Rollout: whether to clear the mode value at shutdown.
+pref("doh-rollout.clearModeOnShutdown", false);
+
+// URL for Learn More link for browser error logging in preferences
+pref("browser.chrome.errorReporter.infoURL",
+ "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection");
+
+// Normandy client preferences
+pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1");
+pref("app.normandy.dev_mode", false);
+pref("app.normandy.enabled", true);
+pref("app.normandy.first_run", true);
+pref("app.normandy.logging.level", 50); // Warn
+pref("app.normandy.run_interval_seconds", 21600); // 6 hours
+pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield");
+pref("app.normandy.last_seen_buildid", "");
+pref("app.normandy.onsync_skew_sec", 600);
+#ifdef MOZ_DATA_REPORTING
+ pref("app.shield.optoutstudies.enabled", true);
+#else
+ pref("app.shield.optoutstudies.enabled", false);
+#endif
+
+// Multi-lingual preferences
+#if defined(RELEASE_OR_BETA) && !defined(MOZ_DEV_EDITION)
+ pref("intl.multilingual.enabled", true);
+ pref("intl.multilingual.downloadEnabled", true);
+#else
+ pref("intl.multilingual.enabled", false);
+ // AMO only serves language packs for release and beta versions.
+ pref("intl.multilingual.downloadEnabled", false);
+#endif
+
+// Simulate conditions that will happen when the browser
+// is running with Fission enabled. This is meant to assist
+// development and testing of Fission.
+// The current simulated conditions are:
+// - Don't propagate events from subframes to JS child actors
+pref("fission.frontend.simulate-events", false);
+// - Only deliver subframe messages that specifies
+// their destination (using the BrowsingContext id).
+pref("fission.frontend.simulate-messages", false);
+
+// Coverage ping is disabled by default.
+pref("toolkit.coverage.enabled", false);
+pref("toolkit.coverage.endpoint.base", "https://coverage.mozilla.org");
+
+// Discovery prefs
+pref("browser.discovery.enabled", true);
+pref("browser.discovery.containers.enabled", true);
+pref("browser.discovery.sites", "addons.mozilla.org");
+
+pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds)
+pref("browser.engagement.downloads-button.has-used", false);
+pref("browser.engagement.fxa-toolbar-menu-button.has-used", false);
+pref("browser.engagement.home-button.has-used", false);
+pref("browser.engagement.sidebar-button.has-used", false);
+pref("browser.engagement.library-button.has-used", false);
+pref("browser.engagement.ctrlTab.has-used", false);
+
+pref("browser.aboutConfig.showWarning", true);
+
+pref("browser.toolbars.keyboard_navigation", true);
+
+// The visibility of the bookmarks toolbar.
+// "newtab": Show on the New Tab Page
+// "always": Always show
+// "never": Never show
+pref("browser.toolbars.bookmarks.visibility", "newtab");
+
+// Visibility of the "Show Other Bookmarks" menuitem in the
+// bookmarks toolbar contextmenu.
+pref("browser.toolbars.bookmarks.showOtherBookmarks", true);
+
+// When true, this pref will always show the bookmarks bar on
+// the New Tab Page, and other functionality to improve the usage of the
+// Bookmarks Toolbar.
+pref("browser.toolbars.bookmarks.2h2020", true);
+
+// Prefs to control the Firefox Account toolbar menu.
+// This pref will surface existing Firefox Account information
+// as a button next to the hamburger menu. It allows
+// quick access to sign-in and manage your Firefox Account.
+pref("identity.fxaccounts.toolbar.enabled", true);
+pref("identity.fxaccounts.toolbar.accessed", false);
+
+// Prefs for different services supported by Firefox Account
+pref("identity.fxaccounts.service.monitorLoginUrl", "https://monitor.firefox.com/");
+
+// Check bundled omni JARs for corruption.
+pref("corroborator.enabled", true);
+
+// Toolbox preferences
+pref("devtools.toolbox.footer.height", 250);
+pref("devtools.toolbox.sidebar.width", 500);
+pref("devtools.toolbox.host", "bottom");
+pref("devtools.toolbox.previousHost", "right");
+pref("devtools.toolbox.selectedTool", "inspector");
+pref("devtools.toolbox.sideEnabled", true);
+pref("devtools.toolbox.zoomValue", "1");
+pref("devtools.toolbox.splitconsoleEnabled", false);
+pref("devtools.toolbox.splitconsoleHeight", 100);
+pref("devtools.toolbox.tabsOrder", "");
+
+// The fission pref for enabling the "Multiprocess Browser Toolbox", which will
+// make it possible to debug anything in Firefox (See Bug 1570639 for more
+// information).
+#if defined(NIGHTLY_BUILD)
+pref("devtools.browsertoolbox.fission", true);
+#else
+pref("devtools.browsertoolbox.fission", false);
+#endif
+
+// This pref is also related to fission, but not only. It allows the toolbox
+// to stay open even if the debugged tab switches to another process.
+// It can happen between two documents, one running in the parent process like
+// about:sessionrestore and another one running in the content process like
+// any web page. Or between two distinct domain when running with fission turned
+// on. See bug 1565263.
+pref("devtools.target-switching.enabled", true);
+
+// Toolbox Button preferences
+pref("devtools.command-button-pick.enabled", true);
+pref("devtools.command-button-frames.enabled", true);
+pref("devtools.command-button-splitconsole.enabled", true);
+pref("devtools.command-button-paintflashing.enabled", false);
+pref("devtools.command-button-responsive.enabled", true);
+pref("devtools.command-button-screenshot.enabled", false);
+pref("devtools.command-button-rulers.enabled", false);
+pref("devtools.command-button-measure.enabled", false);
+pref("devtools.command-button-noautohide.enabled", false);
+pref("devtools.command-button-errorcount.enabled", true);
+#ifndef MOZILLA_OFFICIAL
+ pref("devtools.command-button-fission-prefs.enabled", true);
+#endif
+
+// Inspector preferences
+// Enable the Inspector
+pref("devtools.inspector.enabled", true);
+// What was the last active sidebar in the inspector
+pref("devtools.inspector.activeSidebar", "layoutview");
+pref("devtools.inspector.remote", false);
+
+// Enable the 3 pane mode in the inspector
+pref("devtools.inspector.three-pane-enabled", true);
+// Enable the 3 pane mode in the chrome inspector
+pref("devtools.inspector.chrome.three-pane-enabled", false);
+// Collapse pseudo-elements by default in the rule-view
+pref("devtools.inspector.show_pseudo_elements", false);
+// The default size for image preview tooltips in the rule-view/computed-view/markup-view
+pref("devtools.inspector.imagePreviewTooltipSize", 300);
+// Enable user agent style inspection in rule-view
+pref("devtools.inspector.showUserAgentStyles", false);
+// Show native anonymous content and user agent shadow roots
+pref("devtools.inspector.showAllAnonymousContent", false);
+// Enable the new Rules View
+pref("devtools.inspector.new-rulesview.enabled", false);
+// Enable the inline CSS compatiblity warning in inspector rule view
+pref("devtools.inspector.ruleview.inline-compatibility-warning.enabled", false);
+// Enable the compatibility tool in the inspector.
+#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION)
+pref("devtools.inspector.compatibility.enabled", true);
+#else
+pref("devtools.inspector.compatibility.enabled", false);
+#endif
+// Enable color scheme simulation in the inspector.
+pref("devtools.inspector.color-scheme-simulation.enabled", false);
+// Enable overflow debugging in the inspector.
+pref("devtools.overflow.debugging.enabled", true);
+
+// Grid highlighter preferences
+pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
+pref("devtools.gridinspector.gridOutlineMaxRows", 50);
+pref("devtools.gridinspector.showGridAreas", false);
+pref("devtools.gridinspector.showGridLineNumbers", false);
+pref("devtools.gridinspector.showInfiniteLines", false);
+// Max number of grid highlighters that can be displayed
+pref("devtools.gridinspector.maxHighlighters", 3);
+
+// Whether or not the box model panel is opened in the layout view
+pref("devtools.layout.boxmodel.opened", true);
+// Whether or not the flexbox panel is opened in the layout view
+pref("devtools.layout.flexbox.opened", true);
+// Whether or not the flexbox container panel is opened in the layout view
+pref("devtools.layout.flex-container.opened", true);
+// Whether or not the flexbox item panel is opened in the layout view
+pref("devtools.layout.flex-item.opened", true);
+// Whether or not the grid inspector panel is opened in the layout view
+pref("devtools.layout.grid.opened", true);
+
+// Enable hovering Box Model values and jumping to their source CSS rule in the
+// rule-view.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.layout.boxmodel.highlightProperty", true);
+#else
+ pref("devtools.layout.boxmodel.highlightProperty", false);
+#endif
+
+// By how many times eyedropper will magnify pixels
+pref("devtools.eyedropper.zoom", 6);
+
+// Enable to collapse attributes that are too long.
+pref("devtools.markup.collapseAttributes", true);
+// Length to collapse attributes
+pref("devtools.markup.collapseAttributeLength", 120);
+// Whether to auto-beautify the HTML on copy.
+pref("devtools.markup.beautifyOnCopy", false);
+// Whether or not the DOM mutation breakpoints context menu are enabled in the
+// markup view.
+pref("devtools.markup.mutationBreakpoints.enabled", true);
+
+// DevTools default color unit
+pref("devtools.defaultColorUnit", "authored");
+
+// Enable the Memory tools
+pref("devtools.memory.enabled", true);
+
+pref("devtools.memory.custom-census-displays", "{}");
+pref("devtools.memory.custom-label-displays", "{}");
+pref("devtools.memory.custom-tree-map-displays", "{}");
+
+pref("devtools.memory.max-individuals", 1000);
+pref("devtools.memory.max-retaining-paths", 10);
+
+// Enable the Performance tools
+pref("devtools.performance.enabled", true);
+
+// The default Performance UI settings
+pref("devtools.performance.memory.sample-probability", "0.05");
+// Can't go higher than this without causing internal allocation overflows while
+// serializing the allocations data over the RDP.
+pref("devtools.performance.memory.max-log-length", 125000);
+pref("devtools.performance.timeline.hidden-markers",
+ "[\"Composite\",\"CompositeForwardTransaction\"]");
+pref("devtools.performance.profiler.buffer-size", 10000000);
+pref("devtools.performance.profiler.sample-frequency-hz", 1000);
+pref("devtools.performance.ui.invert-call-tree", true);
+pref("devtools.performance.ui.invert-flame-graph", false);
+pref("devtools.performance.ui.flatten-tree-recursion", true);
+pref("devtools.performance.ui.show-platform-data", false);
+pref("devtools.performance.ui.show-idle-blocks", true);
+pref("devtools.performance.ui.enable-memory", false);
+pref("devtools.performance.ui.enable-allocations", false);
+pref("devtools.performance.ui.enable-framerate", true);
+pref("devtools.performance.ui.show-jit-optimizations", false);
+pref("devtools.performance.ui.show-triggers-for-gc-types",
+ "TOO_MUCH_MALLOC ALLOC_TRIGGER LAST_DITCH EAGER_ALLOC_TRIGGER");
+
+// Temporary pref disabling memory flame views
+// TODO remove once we have flame charts via bug 1148663
+pref("devtools.performance.ui.enable-memory-flame", false);
+
+// Enable experimental options in the UI only in Nightly
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.performance.ui.experimental", true);
+#else
+ pref("devtools.performance.ui.experimental", false);
+#endif
+
+// The default cache UI setting
+pref("devtools.cache.disabled", false);
+
+// The default service workers UI setting
+pref("devtools.serviceWorkers.testing.enabled", false);
+
+// Enable the Network Monitor
+pref("devtools.netmonitor.enabled", true);
+
+pref("devtools.netmonitor.features.search", true);
+pref("devtools.netmonitor.features.requestBlocking", true);
+
+// Enable the Application panel
+pref("devtools.application.enabled", true);
+
+// The default Network Monitor UI settings
+pref("devtools.netmonitor.panes-network-details-width", 550);
+pref("devtools.netmonitor.panes-network-details-height", 450);
+pref("devtools.netmonitor.panes-search-width", 550);
+pref("devtools.netmonitor.panes-search-height", 450);
+pref("devtools.netmonitor.filters", "[\"all\"]");
+pref("devtools.netmonitor.visibleColumns",
+ "[\"status\",\"method\",\"domain\",\"file\",\"initiator\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
+);
+pref("devtools.netmonitor.columnsData",
+ '[{"name":"status","minWidth":30,"width":5}, {"name":"method","minWidth":30,"width":5}, {"name":"domain","minWidth":30,"width":10}, {"name":"file","minWidth":30,"width":25}, {"name":"url","minWidth":30,"width":25},{"name":"initiator","minWidth":30,"width":10},{"name":"type","minWidth":30,"width":5},{"name":"transferred","minWidth":30,"width":10},{"name":"contentSize","minWidth":30,"width":5},{"name":"waterfall","minWidth":150,"width":15}]');
+pref("devtools.netmonitor.msg.payload-preview-height", 128);
+pref("devtools.netmonitor.msg.visibleColumns",
+ '["data", "time"]'
+);
+pref("devtools.netmonitor.msg.displayed-messages.limit", 500);
+
+pref("devtools.netmonitor.response.ui.limit", 10240);
+
+// Save request/response bodies yes/no.
+pref("devtools.netmonitor.saveRequestAndResponseBodies", true);
+
+// The default Network monitor HAR export setting
+pref("devtools.netmonitor.har.defaultLogDir", "");
+pref("devtools.netmonitor.har.defaultFileName", "%hostname_Archive [%date]");
+pref("devtools.netmonitor.har.jsonp", false);
+pref("devtools.netmonitor.har.jsonpCallback", "");
+pref("devtools.netmonitor.har.includeResponseBodies", true);
+pref("devtools.netmonitor.har.compress", false);
+pref("devtools.netmonitor.har.forceExport", false);
+pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
+pref("devtools.netmonitor.har.enableAutoExportToFile", false);
+
+pref("devtools.netmonitor.features.webSockets", true);
+
+// netmonitor audit
+pref("devtools.netmonitor.audits.slow", 500);
+
+// Enable the EventSource Inspector
+pref("devtools.netmonitor.features.serverSentEvents", true);
+
+// Enable the Storage Inspector
+pref("devtools.storage.enabled", true);
+
+// Enable the Style Editor.
+pref("devtools.styleeditor.enabled", true);
+pref("devtools.styleeditor.autocompletion-enabled", true);
+pref("devtools.styleeditor.showMediaSidebar", true);
+pref("devtools.styleeditor.mediaSidebarWidth", 238);
+pref("devtools.styleeditor.navSidebarWidth", 245);
+pref("devtools.styleeditor.transitions", true);
+
+// Screenshot Option Settings.
+pref("devtools.screenshot.clipboard.enabled", false);
+pref("devtools.screenshot.audio.enabled", true);
+
+// Make sure the DOM panel is hidden by default
+pref("devtools.dom.enabled", false);
+
+// Enable the Accessibility panel.
+pref("devtools.accessibility.enabled", true);
+
+// Web console filters
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
+pref("devtools.webconsole.filter.net", false);
+pref("devtools.webconsole.filter.netxhr", false);
+
+// Webconsole autocomplete preference
+pref("devtools.webconsole.input.autocomplete",true);
+
+// Show context selector in console input, in the browser toolbox
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.webconsole.input.context", true);
+#else
+ pref("devtools.webconsole.input.context", false);
+#endif
+
+// Show context selector in console input, in the content toolbox
+pref("devtools.contenttoolbox.webconsole.input.context", false);
+
+// Set to true to eagerly show the results of webconsole terminal evaluations
+// when they don't have side effects.
+pref("devtools.webconsole.input.eagerEvaluation", true);
+
+// Browser console filters
+pref("devtools.browserconsole.filter.error", true);
+pref("devtools.browserconsole.filter.warn", true);
+pref("devtools.browserconsole.filter.info", true);
+pref("devtools.browserconsole.filter.log", true);
+pref("devtools.browserconsole.filter.debug", true);
+pref("devtools.browserconsole.filter.css", false);
+pref("devtools.browserconsole.filter.net", false);
+pref("devtools.browserconsole.filter.netxhr", false);
+
+// Max number of inputs to store in web console history.
+pref("devtools.webconsole.inputHistoryCount", 300);
+
+// Persistent logging: |true| if you want the relevant tool to keep all of the
+// logged messages after reloading the page, |false| if you want the output to
+// be cleared each time page navigation happens.
+pref("devtools.webconsole.persistlog", false);
+pref("devtools.netmonitor.persistlog", false);
+
+// Web Console timestamp: |true| if you want the logs and instructions
+// in the Web Console to display a timestamp, or |false| to not display
+// any timestamps.
+pref("devtools.webconsole.timestampMessages", false);
+
+// Enable the webconsole sidebar toggle in Nightly builds.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.webconsole.sidebarToggle", true);
+#else
+ pref("devtools.webconsole.sidebarToggle", false);
+#endif
+
+// Saved editor mode state in the console.
+pref("devtools.webconsole.input.editor", false);
+pref("devtools.browserconsole.input.editor", false);
+
+// Editor width for webconsole and browserconsole.
+pref("devtools.webconsole.input.editorWidth", 0);
+pref("devtools.browserconsole.input.editorWidth", 0);
+
+// Display an onboarding UI for the Editor mode.
+pref("devtools.webconsole.input.editorOnboarding", true);
+
+// Disable the new performance recording panel by default
+pref("devtools.performance.new-panel-enabled", false);
+
+// Enable message grouping in the console, true by default
+pref("devtools.webconsole.groupWarningMessages", true);
+
+// Saved state of the Display content messages checkbox in the browser console.
+pref("devtools.browserconsole.contentMessages", false);
+
+// Enable client-side mapping service for source maps
+pref("devtools.source-map.client-service.enabled", true);
+
+// The number of lines that are displayed in the web console.
+pref("devtools.hud.loglimit", 10000);
+
+// The developer tools editor configuration:
+// - tabsize: how many spaces to use when a Tab character is displayed.
+// - expandtab: expand Tab characters to spaces.
+// - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
+// - autoclosebrackets: whether to permit automatic bracket/quote closing.
+// - detectindentation: whether to detect the indentation from the file
+// - enableCodeFolding: Whether to enable code folding or not.
+pref("devtools.editor.tabsize", 2);
+pref("devtools.editor.expandtab", true);
+pref("devtools.editor.keymap", "default");
+pref("devtools.editor.autoclosebrackets", true);
+pref("devtools.editor.detectindentation", true);
+pref("devtools.editor.enableCodeFolding", true);
+pref("devtools.editor.autocomplete", true);
+
+// The angle of the viewport.
+pref("devtools.responsive.viewport.angle", 0);
+// The width of the viewport.
+pref("devtools.responsive.viewport.width", 320);
+// The height of the viewport.
+pref("devtools.responsive.viewport.height", 480);
+// The pixel ratio of the viewport.
+pref("devtools.responsive.viewport.pixelRatio", 0);
+// Whether or not the viewports are left aligned.
+pref("devtools.responsive.leftAlignViewport.enabled", false);
+// Whether to reload when touch simulation is toggled
+pref("devtools.responsive.reloadConditions.touchSimulation", false);
+// Whether to reload when user agent is changed
+pref("devtools.responsive.reloadConditions.userAgent", false);
+// Whether to show the notification about reloading to apply emulation
+pref("devtools.responsive.reloadNotification.enabled", true);
+// Whether or not touch simulation is enabled.
+pref("devtools.responsive.touchSimulation.enabled", false);
+// Whether or not meta viewport is enabled, if and only if touchSimulation
+// is also enabled.
+pref("devtools.responsive.metaViewport.enabled", true);
+// The user agent of the viewport.
+pref("devtools.responsive.userAgent", "");
+
+// Show the custom user agent input only in Nightly.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.responsive.showUserAgentInput", true);
+#else
+ pref("devtools.responsive.showUserAgentInput", false);
+#endif
+
+// Show tab debug targets for This Firefox (on by default for local builds).
+#ifdef MOZILLA_OFFICIAL
+ pref("devtools.aboutdebugging.local-tab-debugging", false);
+#else
+ pref("devtools.aboutdebugging.local-tab-debugging", true);
+#endif
+
+// Show process debug targets.
+pref("devtools.aboutdebugging.process-debugging", true);
+// Stringified array of network locations that users can connect to.
+pref("devtools.aboutdebugging.network-locations", "[]");
+// Debug target pane collapse/expand settings.
+pref("devtools.aboutdebugging.collapsibilities.installedExtension", false);
+pref("devtools.aboutdebugging.collapsibilities.otherWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.serviceWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.sharedWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.tab", false);
+pref("devtools.aboutdebugging.collapsibilities.temporaryExtension", false);
+
+// about:debugging: only show system and hidden extensions in local builds by
+// default.
+#ifdef MOZILLA_OFFICIAL
+ pref("devtools.aboutdebugging.showHiddenAddons", false);
+#else
+ pref("devtools.aboutdebugging.showHiddenAddons", true);
+#endif
+
+// Map top-level await expressions in the console
+pref("devtools.debugger.features.map-await-expression", true);
+
+// This relies on javascript.options.asyncstack as well or it has no effect.
+pref("devtools.debugger.features.async-captured-stacks", true);
+pref("devtools.debugger.features.async-live-stacks", false);
+
+// Disable autohide for DevTools popups and tooltips.
+// This is currently not exposed by any UI to avoid making
+// about:devtools-toolbox tabs unusable by mistake.
+pref("devtools.popup.disable_autohide", false);
+
+// FirstStartup service time-out in ms
+pref("first-startup.timeout", 30000);
+
+// Enable the default browser agent.
+// The agent still runs as scheduled if this pref is disabled,
+// but it exits immediately before taking any action.
+#ifdef XP_WIN
+ pref("default-browser-agent.enabled", true);
+#endif
+
+// Test Prefs that do nothing for testing
+#if defined(EARLY_BETA_OR_EARLIER)
+ pref("app.normandy.test-prefs.bool", false);
+ pref("app.normandy.test-prefs.integer", 0);
+ pref("app.normandy.test-prefs.string", "");
+#endif
diff --git a/browser/app/splash.rc b/browser/app/splash.rc
new file mode 100644
index 0000000000..a5d418e812
--- /dev/null
+++ b/browser/app/splash.rc
@@ -0,0 +1,19 @@
+/* -*- 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 <windows.h>
+#include "nsNativeAppSupportWin.h"
+
+IDI_APPICON ICON FIREFOX_ICO
+IDI_DOCUMENT ICON DOCUMENT_ICO
+IDI_APPLICATION ICON FIREFOX_ICO
+IDI_NEWWINDOW ICON NEWWINDOW_ICO
+IDI_NEWTAB ICON NEWTAB_ICO
+IDI_PBMODE ICON PBMODE_ICO
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STARTMENU_APPNAME, "@MOZ_APP_DISPLAYNAME@"
+END
diff --git a/browser/app/winlauncher/DllBlocklistInit.cpp b/browser/app/winlauncher/DllBlocklistInit.cpp
new file mode 100644
index 0000000000..4384bea1bd
--- /dev/null
+++ b/browser/app/winlauncher/DllBlocklistInit.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/ImportDir.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Types.h"
+#include "mozilla/WindowsDllBlocklist.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+
+#include "DllBlocklistInit.h"
+#include "freestanding/DllBlocklist.h"
+#include "freestanding/SharedSection.h"
+
+namespace mozilla {
+
+#if defined(MOZ_ASAN) || defined(_M_ARM64)
+
+// This DLL blocking code is incompatible with ASAN because
+// it is able to execute before ASAN itself has even initialized.
+// Also, AArch64 has not been tested with this.
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess,
+ const IMAGE_THUNK_DATA*) {
+ return mozilla::Ok();
+}
+
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess) {
+ return mozilla::Ok();
+}
+
+#else
+
+static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
+ const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr,
+ const IMAGE_THUNK_DATA* aCachedNtdllThunk) {
+ CrossProcessDllInterceptor intcpt(aTransferMgr.RemoteProcess());
+ intcpt.Init(L"ntdll.dll");
+
+ bool ok = freestanding::stub_NtMapViewOfSection.SetDetour(
+ aTransferMgr, intcpt, "NtMapViewOfSection",
+ &freestanding::patched_NtMapViewOfSection);
+ if (!ok) {
+ return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError());
+ }
+
+ ok = freestanding::stub_LdrLoadDll.SetDetour(
+ aTransferMgr, intcpt, "LdrLoadDll", &freestanding::patched_LdrLoadDll);
+ if (!ok) {
+ return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError());
+ }
+
+ // Because aChildProcess has just been created in a suspended state, its
+ // dynamic linker has not yet been initialized, thus its executable has
+ // not yet been linked with ntdll.dll. If the blocklist hook intercepts a
+ // library load prior to the link, the hook will be unable to invoke any
+ // ntdll.dll functions.
+ //
+ // We know that the executable for our *current* process's binary is already
+ // linked into ntdll, so we obtain the IAT from our own executable and graft
+ // it onto the child process's IAT, thus enabling the child process's hook to
+ // safely make its ntdll calls.
+
+ const nt::PEHeaders& ourExeImage = aTransferMgr.LocalPEHeaders();
+
+ // As part of our mitigation of binary tampering, copy our import directory
+ // from the original in our executable file.
+ LauncherVoidResult importDirRestored =
+ RestoreImportDirectory(aFullImagePath, aTransferMgr);
+ if (importDirRestored.isErr()) {
+ return importDirRestored;
+ }
+
+ mozilla::nt::PEHeaders ntdllImage(::GetModuleHandleW(L"ntdll.dll"));
+ if (!ntdllImage) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
+ }
+
+ // If we have a cached IAT i.e. |aCachedNtdllThunk| is non-null, we can
+ // safely copy it to |aChildProcess| even if the local IAT has been modified.
+ // If |aCachedNtdllThunk| is null, we've failed to cache the IAT or we're in
+ // the launcher process where there is no chance to cache the IAT. In those
+ // cases, we retrieve the IAT with the boundary check to avoid a modified IAT
+ // from being copied into |aChildProcess|.
+ Maybe<Span<IMAGE_THUNK_DATA> > ntdllThunks;
+ if (aCachedNtdllThunk) {
+ ntdllThunks = ourExeImage.GetIATThunksForModule("ntdll.dll");
+ } else {
+ Maybe<Range<const uint8_t> > ntdllBoundaries = ntdllImage.GetBounds();
+ if (!ntdllBoundaries) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
+ }
+
+ // We can use GetIATThunksForModule() to check whether IAT is modified
+ // or not because no functions exported from ntdll.dll is forwarded.
+ ntdllThunks =
+ ourExeImage.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr());
+ }
+ if (!ntdllThunks) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ { // Scope for prot
+ PIMAGE_THUNK_DATA firstIatThunkDst = ntdllThunks.value().data();
+ const IMAGE_THUNK_DATA* firstIatThunkSrc =
+ aCachedNtdllThunk ? aCachedNtdllThunk : firstIatThunkDst;
+ SIZE_T iatLength = ntdllThunks.value().LengthBytes();
+
+ AutoVirtualProtect prot =
+ aTransferMgr.Protect(firstIatThunkDst, iatLength, PAGE_READWRITE);
+ if (!prot) {
+ return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError());
+ }
+
+ LauncherVoidResult writeResult =
+ aTransferMgr.Transfer(firstIatThunkDst, firstIatThunkSrc, iatLength);
+ if (writeResult.isErr()) {
+ return writeResult.propagateErr();
+ }
+ }
+
+ // Tell the mozglue blocklist that we have bootstrapped
+ uint32_t newFlags = eDllBlocklistInitFlagWasBootstrapped;
+
+ if (gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
+ // If we ourselves were bootstrapped, then we are starting a child process
+ // and need to set the appropriate flag.
+ newFlags |= eDllBlocklistInitFlagIsChildProcess;
+ }
+
+ LauncherVoidResult writeResult =
+ aTransferMgr.Transfer(&gBlocklistInitFlags, &newFlags, sizeof(newFlags));
+ if (writeResult.isErr()) {
+ return writeResult.propagateErr();
+ }
+
+ return Ok();
+}
+
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess,
+ const IMAGE_THUNK_DATA* aCachedNtdllThunk) {
+ nt::CrossExecTransferManager transferMgr(aChildProcess);
+ if (!transferMgr) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
+ }
+
+ // We come here when the browser process launches a sandbox process.
+ // If the launcher process already failed to bootstrap the browser process,
+ // we should not attempt to bootstrap a child process because it's likely
+ // to fail again. Instead, we only restore the import directory entry.
+ if (!(gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
+ return RestoreImportDirectory(aFullImagePath, transferMgr);
+ }
+
+ // Transfer a readonly handle to the child processes because all information
+ // are already written to the section by the launcher and main process.
+ LauncherVoidResult transferResult =
+ freestanding::gSharedSection.TransferHandle(transferMgr, GENERIC_READ);
+ if (transferResult.isErr()) {
+ return transferResult.propagateErr();
+ }
+
+ return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr,
+ aCachedNtdllThunk);
+}
+
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess) {
+ nt::CrossExecTransferManager transferMgr(aChildProcess);
+ if (!transferMgr) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
+ }
+
+ // The launcher process initializes a section object, whose handle is
+ // transferred to the browser process, and that transferred handle in
+ // the browser process is transferred to the sandbox processes.
+ LauncherVoidResultWithLineInfo result =
+ freestanding::gSharedSection.Init(transferMgr.LocalPEHeaders());
+ if (result.isErr()) {
+ return result.propagateErr();
+ }
+
+ // Transfer a writable handle to the main process because it needs to append
+ // dependent module paths to the section.
+ LauncherVoidResult transferResult =
+ freestanding::gSharedSection.TransferHandle(transferMgr,
+ GENERIC_READ | GENERIC_WRITE);
+ if (transferResult.isErr()) {
+ return transferResult.propagateErr();
+ }
+
+ auto clearInstance = MakeScopeExit([]() {
+ // After transfer, the launcher process does not need the object anymore.
+ freestanding::gSharedSection.Reset(nullptr);
+ });
+ return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr,
+ nullptr);
+}
+
+#endif // defined(MOZ_ASAN) || defined(_M_ARM64)
+
+} // namespace mozilla
diff --git a/browser/app/winlauncher/DllBlocklistInit.h b/browser/app/winlauncher/DllBlocklistInit.h
new file mode 100644
index 0000000000..c6b87bce91
--- /dev/null
+++ b/browser/app/winlauncher/DllBlocklistInit.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DllBlocklistInit_h
+#define mozilla_DllBlocklistInit_h
+
+#include <windows.h>
+
+#include "mozilla/WinHeaderOnlyUtils.h"
+
+namespace mozilla {
+
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess,
+ const IMAGE_THUNK_DATA* aCachedNtdllThunk);
+
+LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess);
+
+} // namespace mozilla
+
+#endif // mozilla_DllBlocklistInit_h
diff --git a/browser/app/winlauncher/ErrorHandler.cpp b/browser/app/winlauncher/ErrorHandler.cpp
new file mode 100644
index 0000000000..7b90b9c98b
--- /dev/null
+++ b/browser/app/winlauncher/ErrorHandler.cpp
@@ -0,0 +1,781 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "ErrorHandler.h"
+
+#include <utility>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WinTokenUtils.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/XREAppData.h"
+#include "mozilla/glue/WindowsDllServices.h"
+#include "mozilla/mscom/ProcessRuntime.h"
+#include "nsWindowsHelpers.h"
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+# include "mozilla/LauncherRegistryInfo.h"
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+#include <algorithm>
+#include <process.h>
+#include <sstream>
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <objbase.h>
+#include <rpc.h>
+#if !defined(__MINGW32__)
+# include <comutil.h>
+# include <iwscapi.h>
+# include <wscapi.h>
+#endif // !defined(__MINGW32__)
+#include <tlhelp32.h>
+
+#if !defined(RRF_SUBKEY_WOW6464KEY)
+# define RRF_SUBKEY_WOW6464KEY 0x00010000
+#endif // !defined(RRF_SUBKEY_WOW6464KEY)
+
+#define QUOTE_ME2(x) #x
+#define QUOTE_ME(x) QUOTE_ME2(x)
+
+#define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit"
+#define TELEMETRY_NAMESPACE L"/firefox-launcher-process"
+#define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure"
+#define TELEMETRY_LAUNCHER_PING_VERSION L"/1"
+
+static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE
+ TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/";
+static const uint32_t kGuidCharLenWithNul = 39;
+static const uint32_t kGuidCharLenNoBracesNoNul = 36;
+static const mozilla::StaticXREAppData* gAppData;
+static bool gForceEventLog = false;
+
+namespace {
+
+struct EventSourceDeleter {
+ using pointer = HANDLE;
+
+ void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); }
+};
+
+using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>;
+
+struct SerializedEventData {
+ HRESULT mHr;
+ uint32_t mLine;
+ char mFile[1];
+};
+
+} // anonymous namespace
+
+static void PostErrorToLog(const mozilla::LauncherError& aError) {
+ // This is very bare-bones; just enough to spit out an HRESULT to the
+ // Application event log.
+ EventLog log(::RegisterEventSourceW(nullptr, L"Firefox"));
+ if (!log) {
+ return;
+ }
+
+ size_t fileLen = strlen(aError.mFile);
+ size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen;
+ auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen);
+ SerializedEventData& evtData =
+ *reinterpret_cast<SerializedEventData*>(evtDataBuf.get());
+ evtData.mHr = aError.mError.AsHResult();
+ evtData.mLine = aError.mLine;
+ // Since this is binary data, we're not concerning ourselves with null
+ // terminators.
+ memcpy(evtData.mFile, aError.mFile, fileLen);
+
+ ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(),
+ nullptr, 0, dataLen, nullptr, evtDataBuf.get());
+}
+
+#if defined(MOZ_TELEMETRY_REPORTING)
+
+namespace {
+
+// This JSONWriteFunc writes directly to a temp file. By creating this file
+// with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this
+// file is short-lived. The OS will try to avoid flushing it to disk if at
+// all possible.
+class TempFileWriter final : public mozilla::JSONWriteFunc {
+ public:
+ TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) {
+ wchar_t name[MAX_PATH + 1] = {};
+ if (_wtmpnam_s(name)) {
+ mFailed = true;
+ return;
+ }
+
+ mTempFileName = name;
+
+ mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
+ CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr));
+ if (mTempFile.get() == INVALID_HANDLE_VALUE) {
+ mFailed = true;
+ }
+ }
+
+ ~TempFileWriter() {
+ if (mSuccessfulHandoff) {
+ // It is no longer our responsibility to delete the temp file if we have
+ // successfully handed it off to pingsender.
+ return;
+ }
+
+ mTempFile.reset();
+ ::DeleteFileW(mTempFileName.c_str());
+ }
+
+ explicit operator bool() const { return !mFailed; }
+
+ void Write(const mozilla::Span<const char>& aStr) override {
+ if (mFailed) {
+ return;
+ }
+
+ DWORD bytesWritten = 0;
+ if (!::WriteFile(mTempFile, aStr.data(), aStr.size(), &bytesWritten,
+ nullptr) ||
+ bytesWritten != aStr.size()) {
+ mFailed = true;
+ }
+ }
+
+ const std::wstring& GetFileName() const { return mTempFileName; }
+
+ void SetSuccessfulHandoff() { mSuccessfulHandoff = true; }
+
+ private:
+ bool mFailed;
+ bool mSuccessfulHandoff;
+ std::wstring mTempFileName;
+ nsAutoHandle mTempFile;
+};
+
+using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>;
+
+} // anonymous namespace
+
+// This is the guideline for maximum string length for telemetry intake
+static const size_t kMaxStrLen = 80;
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
+ const size_t aStrLenExclNul) {
+ // Yes, this might not handle surrogate pairs correctly. Let's just let
+ // WideCharToMultiByte fail in that unlikely case.
+ size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen);
+
+ int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0,
+ nullptr, nullptr);
+ if (!numConv) {
+ return nullptr;
+ }
+
+ // Include room for the null terminator by adding one
+ auto buf = mozilla::MakeUnique<char[]>(numConv + 1);
+
+ numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv,
+ nullptr, nullptr);
+ if (!numConv) {
+ return nullptr;
+ }
+
+ // Add null termination. numConv does not include the terminator, so we don't
+ // subtract 1 when indexing into buf.
+ buf[numConv] = 0;
+
+ return buf;
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) {
+ return WideToUTF8(aStr, wcslen(aStr));
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
+ return WideToUTF8(aStr.c_str(), aStr.length());
+}
+
+// MinGW does not support the Windows Security Center APIs.
+# if !defined(__MINGW32__)
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
+ return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
+}
+
+namespace {
+
+struct ProviderKey {
+ WSC_SECURITY_PROVIDER mProviderType;
+ const char* mKey;
+};
+
+} // anonymous namespace
+
+static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList,
+ mozilla::JSONWriter& aJson) {
+ LONG count;
+ HRESULT hr = aProdList->get_Count(&count);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ // Unlikely, but put a bound on the max length of the output array for the
+ // purposes of telemetry intake.
+ count = std::min(count, 1000L);
+
+ // Record the name(s) of each active registered product in this category
+ for (LONG index = 0; index < count; ++index) {
+ RefPtr<IWscProduct> product;
+ hr = aProdList->get_Item(index, getter_AddRefs(product));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ WSC_SECURITY_PRODUCT_STATE state;
+ hr = product->get_ProductState(&state);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ // We only care about products that are active
+ if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
+ state == WSC_SECURITY_PRODUCT_STATE_SNOOZED ||
+ state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) {
+ continue;
+ }
+
+ _bstr_t bName;
+ hr = product->get_ProductName(bName.GetAddress());
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ auto buf = WideToUTF8(bName);
+ if (!buf) {
+ return false;
+ }
+
+ aJson.StringElement(mozilla::MakeStringSpan(buf.get()));
+ }
+
+ return true;
+}
+
+static const ProviderKey gProvKeys[] = {
+ {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"},
+ {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"},
+ {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}};
+
+static bool AddWscInfo(mozilla::JSONWriter& aJson) {
+ if (!mozilla::IsWin8OrLater()) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ // We need COM for this. Using ProcessRuntime so that process-global COM
+ // configuration is done correctly
+ mozilla::mscom::ProcessRuntime mscom(
+ mozilla::mscom::ProcessRuntime::ProcessCategory::Launcher);
+ if (!mscom) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ aJson.StartObjectProperty("security");
+
+ const CLSID clsid = __uuidof(WSCProductList);
+ const IID iid = __uuidof(IWSCProductList);
+
+ for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) {
+ // NB: A separate instance of IWSCProductList is needed for each distinct
+ // security provider type; MSDN says that we cannot reuse the same object
+ // and call Initialize() to pave over the previous data.
+ RefPtr<IWSCProductList> prodList;
+ HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
+ getter_AddRefs(prodList));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ hr = prodList->Initialize(gProvKeys[index].mProviderType);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ aJson.StartArrayProperty(mozilla::MakeStringSpan(gProvKeys[index].mKey));
+
+ if (!EnumWSCProductList(prodList, aJson)) {
+ return false;
+ }
+
+ aJson.EndArray();
+ }
+
+ aJson.EndObject();
+
+ return true;
+}
+# endif // !defined(__MINGW32__)
+
+// Max array length for telemetry intake.
+static const size_t kMaxArrayLen = 1000;
+
+static bool AddModuleInfo(const nsAutoHandle& aSnapshot,
+ mozilla::JSONWriter& aJson) {
+ if (aSnapshot.get() == INVALID_HANDLE_VALUE) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ SigMap signatures;
+ size_t moduleCount = 0;
+
+ MODULEENTRY32W module = {sizeof(module)};
+ if (!::Module32FirstW(aSnapshot, &module)) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ mozilla::glue::BasicDllServices dllServices;
+
+ aJson.StartObjectProperty("modules");
+
+ // For each module, add its version number (or empty string if not present),
+ // followed by an optional index into the signatures array
+ do {
+ ++moduleCount;
+
+ wchar_t leaf[_MAX_FNAME] = {};
+ if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf,
+ mozilla::ArrayLength(leaf), nullptr, 0)) {
+ return false;
+ }
+
+ if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) {
+ return false;
+ }
+
+ auto leafUtf8 = WideToUTF8(leaf);
+ if (!leafUtf8) {
+ return false;
+ }
+
+ aJson.StartArrayProperty(mozilla::MakeStringSpan(leafUtf8.get()));
+
+ std::string version;
+ DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr);
+ if (verInfoSize) {
+ auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize);
+
+ if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize,
+ verInfoBuf.get())) {
+ VS_FIXEDFILEINFO* fixedInfo = nullptr;
+ UINT fixedInfoLen = 0;
+
+ if (::VerQueryValueW(verInfoBuf.get(), L"\\",
+ reinterpret_cast<LPVOID*>(&fixedInfo),
+ &fixedInfoLen)) {
+ std::ostringstream oss;
+ oss << HIWORD(fixedInfo->dwFileVersionMS) << '.'
+ << LOWORD(fixedInfo->dwFileVersionMS) << '.'
+ << HIWORD(fixedInfo->dwFileVersionLS) << '.'
+ << LOWORD(fixedInfo->dwFileVersionLS);
+ version = oss.str();
+ }
+ }
+ }
+
+ aJson.StringElement(version);
+
+ mozilla::Maybe<ptrdiff_t> sigIndex;
+ auto signedBy = dllServices.GetBinaryOrgName(module.szExePath);
+ if (signedBy) {
+ std::wstring strSignedBy(signedBy.get());
+ auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy);
+ if (entry == signatures.end()) {
+ mozilla::Unused << signatures.append(std::move(strSignedBy));
+ entry = &signatures.back();
+ }
+
+ sigIndex = mozilla::Some(entry - signatures.begin());
+ }
+
+ if (sigIndex) {
+ aJson.IntElement(sigIndex.value());
+ }
+
+ aJson.EndArray();
+ } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module));
+
+ aJson.EndObject();
+
+ aJson.StartArrayProperty("signatures");
+
+ // Serialize each entry in the signatures array
+ for (auto&& itr : signatures) {
+ auto sigUtf8 = WideToUTF8(itr);
+ if (!sigUtf8) {
+ continue;
+ }
+
+ aJson.StringElement(mozilla::MakeStringSpan(sigUtf8.get()));
+ }
+
+ aJson.EndArray();
+
+ return true;
+}
+
+namespace {
+
+struct PingThreadContext {
+ explicit PingThreadContext(const mozilla::LauncherError& aError,
+ const char* aProcessType)
+ : mLauncherError(aError),
+ mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)),
+ mProcessType(aProcessType ? aProcessType : "") {}
+ mozilla::LauncherError mLauncherError;
+ nsAutoHandle mModulesSnapshot;
+ std::string mProcessType;
+};
+
+} // anonymous namespace
+
+static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId,
+ mozilla::JSONWriter& aJson) {
+# if defined(DEBUG)
+ const mozilla::JSONWriter::CollectionStyle style =
+ mozilla::JSONWriter::MultiLineStyle;
+# else
+ const mozilla::JSONWriter::CollectionStyle style =
+ mozilla::JSONWriter::SingleLineStyle;
+# endif // defined(DEBUG)
+
+ aJson.Start(style);
+
+ aJson.StringProperty("type", "launcher-process-failure");
+ aJson.IntProperty("version", 1);
+
+ auto idUtf8 = WideToUTF8(aId);
+ if (idUtf8) {
+ aJson.StringProperty("id", mozilla::MakeStringSpan(idUtf8.get()));
+ }
+
+ time_t now;
+ time(&now);
+ tm gmTm;
+ if (!gmtime_s(&gmTm, &now)) {
+ char isoTimeBuf[32] = {};
+ if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z",
+ &gmTm)) {
+ aJson.StringProperty("creationDate", isoTimeBuf);
+ }
+ }
+
+ aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL));
+
+ if (gAppData) {
+ aJson.StringProperty("build_id",
+ mozilla::MakeStringSpan(gAppData->buildID));
+ aJson.StringProperty("build_version",
+ mozilla::MakeStringSpan(gAppData->version));
+ }
+
+ OSVERSIONINFOEXW osv = {sizeof(osv)};
+ if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) {
+ std::ostringstream oss;
+ oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "."
+ << osv.dwBuildNumber;
+
+ if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) {
+ // Get the "Update Build Revision" (UBR) value
+ DWORD ubrValue;
+ DWORD ubrValueLen = sizeof(ubrValue);
+ LSTATUS ubrOk =
+ ::RegGetValueW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr,
+ &ubrValue, &ubrValueLen);
+ if (ubrOk == ERROR_SUCCESS) {
+ oss << "." << ubrValue;
+ }
+ }
+
+ if (oss) {
+ aJson.StringProperty("os_version", oss.str());
+ }
+
+ bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER ||
+ osv.wProductType == VER_NT_SERVER;
+ aJson.BoolProperty("server_os", isServer);
+ }
+
+ WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {};
+ int localeNameLen =
+ ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName));
+ if (localeNameLen) {
+ auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1);
+ if (localeNameUtf8) {
+ aJson.StringProperty("os_locale",
+ mozilla::MakeStringSpan(localeNameUtf8.get()));
+ }
+ }
+
+ SYSTEM_INFO sysInfo;
+ ::GetNativeSystemInfo(&sysInfo);
+ aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture);
+ aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors);
+
+ mozilla::LauncherResult<bool> isAdminWithoutUac =
+ mozilla::IsAdminWithoutUac();
+ if (isAdminWithoutUac.isOk()) {
+ aJson.BoolProperty("is_admin_without_uac", isAdminWithoutUac.unwrap());
+ }
+
+ if (!aContext.mProcessType.empty()) {
+ aJson.StringProperty("process_type", aContext.mProcessType);
+ }
+
+ MEMORYSTATUSEX memStatus = {sizeof(memStatus)};
+ if (::GlobalMemoryStatusEx(&memStatus)) {
+ aJson.StartObjectProperty("memory");
+ aJson.IntProperty("total_phys", memStatus.ullTotalPhys);
+ aJson.IntProperty("avail_phys", memStatus.ullAvailPhys);
+ aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile);
+ aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual);
+ aJson.EndObject();
+ }
+
+ aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI);
+
+ aJson.StartObjectProperty("launcher_error", style);
+
+ std::string srcFileLeaf(aContext.mLauncherError.mFile);
+ // Obtain the leaf name of the file for privacy reasons
+ // (In case this is somebody's local build)
+ auto pos = srcFileLeaf.find_last_of("/\\");
+ if (pos != std::string::npos) {
+ srcFileLeaf = srcFileLeaf.substr(pos + 1);
+ }
+
+ aJson.StringProperty("source_file", srcFileLeaf);
+
+ aJson.IntProperty("source_line", aContext.mLauncherError.mLine);
+ aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult());
+
+# if defined(NIGHTLY_BUILD)
+ if (aContext.mLauncherError.mDetourError.isSome()) {
+ static const char* kHexMap = "0123456789abcdef";
+ char hexStr[sizeof(mozilla::DetourError::mOrigBytes) * 2 + 1];
+ int cnt = 0;
+ for (uint8_t byte : aContext.mLauncherError.mDetourError->mOrigBytes) {
+ hexStr[cnt++] = kHexMap[(byte >> 4) & 0x0f];
+ hexStr[cnt++] = kHexMap[byte & 0x0f];
+ }
+ hexStr[cnt] = 0;
+ aJson.StringProperty("detour_orig_bytes", hexStr);
+ }
+# endif // defined(NIGHTLY_BUILD)
+
+ aJson.EndObject();
+
+# if !defined(__MINGW32__)
+ if (!AddWscInfo(aJson)) {
+ return false;
+ }
+# endif // !defined(__MINGW32__)
+
+ if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) {
+ return false;
+ }
+
+ aJson.End();
+
+ return true;
+}
+
+static bool DoSendPing(const PingThreadContext& aContext) {
+ auto writeFunc = mozilla::MakeUnique<TempFileWriter>();
+ if (!(*writeFunc)) {
+ return false;
+ }
+
+ mozilla::JSONWriter json(std::move(writeFunc));
+
+ UUID uuid;
+ if (::UuidCreate(&uuid) != RPC_S_OK) {
+ return false;
+ }
+
+ wchar_t guidBuf[kGuidCharLenWithNul] = {};
+ if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) !=
+ kGuidCharLenWithNul) {
+ return false;
+ }
+
+ // Strip the curly braces off of the guid
+ std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul);
+
+ // Populate json with the ping information
+ if (!PrepPing(aContext, guidNoBraces, json)) {
+ return false;
+ }
+
+ // Obtain the name of the temp file that we have written
+ TempFileWriter& tempFile = *static_cast<TempFileWriter*>(json.WriteFunc());
+ if (!tempFile) {
+ return false;
+ }
+
+ const std::wstring& fileName = tempFile.GetFileName();
+
+ // Using the path to our executable binary, construct the path to
+ // pingsender.exe
+ mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath());
+
+ wchar_t drive[_MAX_DRIVE] = {};
+ wchar_t dir[_MAX_DIR] = {};
+ if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir,
+ mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) {
+ return false;
+ }
+
+ wchar_t pingSenderPath[MAX_PATH + 1] = {};
+ if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive,
+ dir, L"pingsender", L"exe")) {
+ return false;
+ }
+
+ // Construct the telemetry URL
+ wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {};
+ if (wcscpy_s(urlBuf, kUrl)) {
+ return false;
+ }
+
+ if (wcscat_s(urlBuf, guidNoBraces.c_str())) {
+ return false;
+ }
+
+ // Now build the command line arguments to pingsender
+ wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf,
+ const_cast<wchar_t*>(fileName.c_str())};
+
+ mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine(
+ mozilla::ArrayLength(pingSenderArgv), pingSenderArgv));
+
+ // Now start pingsender to handle the rest
+ PROCESS_INFORMATION pi;
+
+ STARTUPINFOW si = {sizeof(si)};
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+
+ if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr,
+ nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
+ return false;
+ }
+
+ tempFile.SetSuccessfulHandoff();
+
+ nsAutoHandle proc(pi.hProcess);
+ nsAutoHandle thread(pi.hThread);
+
+ return true;
+}
+
+static unsigned __stdcall SendPingThread(void* aContext) {
+ mozilla::UniquePtr<PingThreadContext> context(
+ reinterpret_cast<PingThreadContext*>(aContext));
+
+ if (!DoSendPing(*context) || gForceEventLog) {
+ PostErrorToLog(context->mLauncherError);
+ }
+
+ return 0;
+}
+
+#endif // defined(MOZ_TELEMETRY_REPORTING)
+
+static bool SendPing(const mozilla::LauncherError& aError,
+ const char* aProcessType) {
+#if defined(MOZ_TELEMETRY_REPORTING)
+# if defined(MOZ_LAUNCHER_PROCESS)
+ mozilla::LauncherRegistryInfo regInfo;
+ mozilla::LauncherResult<bool> telemetryEnabled = regInfo.IsTelemetryEnabled();
+ if (telemetryEnabled.isErr() || !telemetryEnabled.unwrap()) {
+ // Do not send anything if telemetry has been opted out
+ return false;
+ }
+# endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ // We send this ping when the launcher process fails. After we start the
+ // SendPingThread, this thread falls back from running as the launcher process
+ // to running as the browser main thread. Once this happens, it will be unsafe
+ // to set up PoisonIOInterposer (since we have already spun up a background
+ // thread).
+ mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1");
+
+ // Capture aError and our module list into context for processing on another
+ // thread.
+ auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError, aProcessType);
+
+ // The ping does a lot of file I/O. Since we want this thread to continue
+ // executing browser startup, we should gather that information on a
+ // background thread.
+ uintptr_t thdHandle =
+ _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(),
+ STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr);
+ if (!thdHandle) {
+ return false;
+ }
+
+ // We have handed off thdParam to the background thread
+ mozilla::Unused << thdParam.release();
+
+ ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle));
+ return true;
+#else
+ return false;
+#endif
+}
+
+namespace mozilla {
+
+void HandleLauncherError(const LauncherError& aError,
+ const char* aProcessType) {
+#if defined(MOZ_LAUNCHER_PROCESS)
+ LauncherRegistryInfo regInfo;
+ Unused << regInfo.DisableDueToFailure();
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ if (SendPing(aError, aProcessType) && !gForceEventLog) {
+ return;
+ }
+
+ PostErrorToLog(aError);
+}
+
+void SetLauncherErrorAppData(const StaticXREAppData& aAppData) {
+ gAppData = &aAppData;
+}
+
+void SetLauncherErrorForceEventLog() { gForceEventLog = true; }
+
+} // namespace mozilla
diff --git a/browser/app/winlauncher/ErrorHandler.h b/browser/app/winlauncher/ErrorHandler.h
new file mode 100644
index 0000000000..71fe72cf07
--- /dev/null
+++ b/browser/app/winlauncher/ErrorHandler.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ErrorHandler_h
+#define mozilla_ErrorHandler_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+
+namespace mozilla {
+
+/**
+ * All launcher process error handling should live in the implementation of
+ * this function.
+ */
+void HandleLauncherError(const LauncherError& aError,
+ const char* aProcessType = nullptr);
+
+// This function is simply a convenience overload that automatically unwraps
+// the LauncherError from the provided LauncherResult and then forwards it to
+// the main implementation.
+template <typename T>
+inline void HandleLauncherError(const LauncherResult<T>& aResult,
+ const char* aProcessType = nullptr) {
+ MOZ_ASSERT(aResult.isErr());
+ if (aResult.isOk()) {
+ return;
+ }
+
+ HandleLauncherError(aResult.inspectErr(), aProcessType);
+}
+
+// This function is simply a convenience overload that unwraps the provided
+// GenericErrorResult<LauncherError> and forwards it to the main implementation.
+inline void HandleLauncherError(
+ const GenericErrorResult<LauncherError>& aResult,
+ const char* aProcessType = nullptr) {
+ LauncherVoidResult r(aResult);
+ HandleLauncherError(r, aProcessType);
+}
+
+// Forward declaration
+struct StaticXREAppData;
+
+void SetLauncherErrorAppData(const StaticXREAppData& aAppData);
+
+void SetLauncherErrorForceEventLog();
+
+} // namespace mozilla
+
+#endif // mozilla_ErrorHandler_h
diff --git a/browser/app/winlauncher/LaunchUnelevated.cpp b/browser/app/winlauncher/LaunchUnelevated.cpp
new file mode 100644
index 0000000000..41f0d377f3
--- /dev/null
+++ b/browser/app/winlauncher/LaunchUnelevated.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "LaunchUnelevated.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/mscom/ProcessRuntime.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/ShellHeaderOnlyUtils.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "nsWindowsHelpers.h"
+
+#include <windows.h>
+
+static mozilla::LauncherResult<bool> IsHighIntegrity(
+ const nsAutoHandle& aToken) {
+ DWORD reqdLen;
+ if (!::GetTokenInformation(aToken.get(), TokenIntegrityLevel, nullptr, 0,
+ &reqdLen)) {
+ DWORD err = ::GetLastError();
+ if (err != ERROR_INSUFFICIENT_BUFFER) {
+ return LAUNCHER_ERROR_FROM_WIN32(err);
+ }
+ }
+
+ auto buf = mozilla::MakeUnique<char[]>(reqdLen);
+
+ if (!::GetTokenInformation(aToken.get(), TokenIntegrityLevel, buf.get(),
+ reqdLen, &reqdLen)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ auto tokenLabel = reinterpret_cast<PTOKEN_MANDATORY_LABEL>(buf.get());
+
+ DWORD subAuthCount = *::GetSidSubAuthorityCount(tokenLabel->Label.Sid);
+ DWORD integrityLevel =
+ *::GetSidSubAuthority(tokenLabel->Label.Sid, subAuthCount - 1);
+ return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID;
+}
+
+static mozilla::LauncherResult<HANDLE> GetMediumIntegrityToken(
+ const nsAutoHandle& aProcessToken) {
+ HANDLE rawResult;
+ if (!::DuplicateTokenEx(aProcessToken.get(), 0, nullptr,
+ SecurityImpersonation, TokenPrimary, &rawResult)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ nsAutoHandle result(rawResult);
+
+ BYTE mediumIlSid[SECURITY_MAX_SID_SIZE];
+ DWORD mediumIlSidSize = sizeof(mediumIlSid);
+ if (!::CreateWellKnownSid(WinMediumLabelSid, nullptr, mediumIlSid,
+ &mediumIlSidSize)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ TOKEN_MANDATORY_LABEL integrityLevel = {};
+ integrityLevel.Label.Attributes = SE_GROUP_INTEGRITY;
+ integrityLevel.Label.Sid = reinterpret_cast<PSID>(mediumIlSid);
+
+ if (!::SetTokenInformation(rawResult, TokenIntegrityLevel, &integrityLevel,
+ sizeof(integrityLevel))) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ return result.disown();
+}
+
+static mozilla::LauncherResult<bool> IsAdminByAppCompat(
+ HKEY aRootKey, const wchar_t* aExecutablePath) {
+ static const wchar_t kPathToLayers[] =
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
+ L"AppCompatFlags\\Layers";
+
+ DWORD dataLength = 0;
+ LSTATUS status = ::RegGetValueW(aRootKey, kPathToLayers, aExecutablePath,
+ RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY,
+ nullptr, nullptr, &dataLength);
+ if (status == ERROR_FILE_NOT_FOUND) {
+ return false;
+ } else if (status != ERROR_SUCCESS) {
+ return LAUNCHER_ERROR_FROM_WIN32(status);
+ }
+
+ auto valueData = mozilla::MakeUnique<wchar_t[]>(dataLength);
+ if (!valueData) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_OUTOFMEMORY);
+ }
+
+ status = ::RegGetValueW(aRootKey, kPathToLayers, aExecutablePath,
+ RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, nullptr,
+ valueData.get(), &dataLength);
+ if (status != ERROR_SUCCESS) {
+ return LAUNCHER_ERROR_FROM_WIN32(status);
+ }
+
+ const wchar_t kRunAsAdmin[] = L"RUNASADMIN";
+ const wchar_t kDelimiters[] = L" ";
+ wchar_t* tokenContext = nullptr;
+ const wchar_t* token = wcstok_s(valueData.get(), kDelimiters, &tokenContext);
+ while (token) {
+ if (!_wcsnicmp(token, kRunAsAdmin, mozilla::ArrayLength(kRunAsAdmin))) {
+ return true;
+ }
+ token = wcstok_s(nullptr, kDelimiters, &tokenContext);
+ }
+
+ return false;
+}
+
+namespace mozilla {
+
+// If we're running at an elevated integrity level, re-run ourselves at the
+// user's normal integrity level. We do this by locating the active explorer
+// shell, and then asking it to do a ShellExecute on our behalf. We do it this
+// way to ensure that the child process runs as the original user in the active
+// session; an elevated process could be running with different credentials than
+// those of the session.
+// See https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643
+
+LauncherVoidResult LaunchUnelevated(int aArgc, wchar_t* aArgv[]) {
+ // We need COM to talk to Explorer. Using ProcessRuntime so that
+ // process-global COM configuration is done correctly
+ mozilla::mscom::ProcessRuntime mscom(GeckoProcessType_Default);
+ if (!mscom) {
+ return LAUNCHER_ERROR_FROM_HRESULT(mscom.GetHResult());
+ }
+
+ // Omit argv[0] because ShellExecute doesn't need it in params
+ UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(aArgc - 1, aArgv + 1));
+ if (!cmdLine) {
+ return LAUNCHER_ERROR_GENERIC();
+ }
+
+ _bstr_t exe(aArgv[0]);
+ _variant_t args(cmdLine.get());
+ _variant_t operation(L"open");
+ _variant_t directory;
+ _variant_t showCmd(SW_SHOWNORMAL);
+ return ShellExecuteByExplorer(exe, args, operation, directory, showCmd);
+}
+
+LauncherResult<ElevationState> GetElevationState(
+ const wchar_t* aExecutablePath, mozilla::LauncherFlags aFlags,
+ nsAutoHandle& aOutMediumIlToken) {
+ aOutMediumIlToken.reset();
+
+ const DWORD tokenFlags = TOKEN_QUERY | TOKEN_DUPLICATE |
+ TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY;
+ HANDLE rawToken;
+ if (!::OpenProcessToken(::GetCurrentProcess(), tokenFlags, &rawToken)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ nsAutoHandle token(rawToken);
+
+ LauncherResult<TOKEN_ELEVATION_TYPE> elevationType = GetElevationType(token);
+ if (elevationType.isErr()) {
+ return elevationType.propagateErr();
+ }
+
+ Maybe<ElevationState> elevationState;
+ switch (elevationType.unwrap()) {
+ case TokenElevationTypeLimited:
+ return ElevationState::eNormalUser;
+ case TokenElevationTypeFull:
+ elevationState = Some(ElevationState::eElevated);
+ break;
+ case TokenElevationTypeDefault: {
+ // In this case, UAC is disabled. We do not yet know whether or not we
+ // are running at high integrity. If we are at high integrity, we can't
+ // relaunch ourselves in a non-elevated state via Explorer, as we would
+ // just end up in an infinite loop of launcher processes re-launching
+ // themselves.
+ LauncherResult<bool> isHighIntegrity = IsHighIntegrity(token);
+ if (isHighIntegrity.isErr()) {
+ return isHighIntegrity.propagateErr();
+ }
+
+ if (!isHighIntegrity.unwrap()) {
+ return ElevationState::eNormalUser;
+ }
+
+ elevationState = Some(ElevationState::eHighIntegrityNoUAC);
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
+ return LAUNCHER_ERROR_GENERIC();
+ }
+
+ MOZ_ASSERT(elevationState.isSome() &&
+ elevationState.value() != ElevationState::eNormalUser,
+ "Should have returned earlier for the eNormalUser case.");
+
+ LauncherResult<bool> isAdminByAppCompat =
+ IsAdminByAppCompat(HKEY_CURRENT_USER, aExecutablePath);
+ if (isAdminByAppCompat.isErr()) {
+ return isAdminByAppCompat.propagateErr();
+ }
+
+ if (isAdminByAppCompat.unwrap()) {
+ elevationState = Some(ElevationState::eHighIntegrityByAppCompat);
+ } else {
+ isAdminByAppCompat =
+ IsAdminByAppCompat(HKEY_LOCAL_MACHINE, aExecutablePath);
+ if (isAdminByAppCompat.isErr()) {
+ return isAdminByAppCompat.propagateErr();
+ }
+
+ if (isAdminByAppCompat.unwrap()) {
+ elevationState = Some(ElevationState::eHighIntegrityByAppCompat);
+ }
+ }
+
+ // A medium IL token is not needed in the following cases.
+ // 1) We keep the process elevated (= LauncherFlags::eNoDeelevate)
+ // 2) The process was elevated by UAC (= ElevationState::eElevated)
+ // AND the launcher process doesn't wait for the browser process
+ if ((aFlags & mozilla::LauncherFlags::eNoDeelevate) ||
+ (elevationState.value() == ElevationState::eElevated &&
+ !(aFlags & mozilla::LauncherFlags::eWaitForBrowser))) {
+ return elevationState.value();
+ }
+
+ LauncherResult<HANDLE> tokenResult = GetMediumIntegrityToken(token);
+ if (tokenResult.isOk()) {
+ aOutMediumIlToken.own(tokenResult.unwrap());
+ } else {
+ return tokenResult.propagateErr();
+ }
+
+ return elevationState.value();
+}
+
+} // namespace mozilla
diff --git a/browser/app/winlauncher/LaunchUnelevated.h b/browser/app/winlauncher/LaunchUnelevated.h
new file mode 100644
index 0000000000..8c6679edf3
--- /dev/null
+++ b/browser/app/winlauncher/LaunchUnelevated.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_LaunchUnelevated_h
+#define mozilla_LaunchUnelevated_h
+
+#include "LauncherProcessWin.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/Maybe.h"
+#include "nsWindowsHelpers.h"
+
+namespace mozilla {
+
+enum class ElevationState {
+ eNormalUser = 0,
+ eElevated = (1 << 0),
+ eHighIntegrityNoUAC = (1 << 1),
+ eHighIntegrityByAppCompat = (1 << 2),
+};
+
+LauncherResult<ElevationState> GetElevationState(
+ const wchar_t* aExecutablePath, LauncherFlags aFlags,
+ nsAutoHandle& aOutMediumIlToken);
+
+LauncherVoidResult LaunchUnelevated(int aArgc, wchar_t* aArgv[]);
+
+} // namespace mozilla
+
+#endif // mozilla_LaunchUnelevated_h
diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
new file mode 100644
index 0000000000..faf53eff9a
--- /dev/null
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "LauncherProcessWin.h"
+
+#include <string.h>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/glue/Debug.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/SafeMode.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsConsole.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "nsWindowsHelpers.h"
+
+#include <windows.h>
+#include <processthreadsapi.h>
+
+#include "DllBlocklistInit.h"
+#include "ErrorHandler.h"
+#include "LaunchUnelevated.h"
+#include "ProcThreadAttributes.h"
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+# include "mozilla/LauncherRegistryInfo.h"
+# include "SameBinary.h"
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+/**
+ * At this point the child process has been created in a suspended state. Any
+ * additional startup work (eg, blocklist setup) should go here.
+ *
+ * @return Ok if browser startup should proceed
+ */
+static mozilla::LauncherVoidResult PostCreationSetup(
+ const wchar_t* aFullImagePath, HANDLE aChildProcess,
+ HANDLE aChildMainThread, const bool aIsSafeMode) {
+ return mozilla::InitializeDllBlocklistOOPFromLauncher(aFullImagePath,
+ aChildProcess);
+}
+
+#if !defined( \
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON)
+# define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON \
+ (0x00000001ULL << 60)
+#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON)
+
+#if !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF)
+# define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF \
+ (0x00000002ULL << 40)
+#endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF)
+
+#if (_WIN32_WINNT < 0x0602)
+BOOL WINAPI
+SetProcessMitigationPolicy(PROCESS_MITIGATION_POLICY aMitigationPolicy,
+ PVOID aBuffer, SIZE_T aBufferLen);
+#endif // (_WIN32_WINNT >= 0x0602)
+
+/**
+ * Any mitigation policies that should be set on the browser process should go
+ * here.
+ */
+static void SetMitigationPolicies(mozilla::ProcThreadAttributes& aAttrs,
+ const bool aIsSafeMode) {
+ if (mozilla::IsWin10AnniversaryUpdateOrLater()) {
+ aAttrs.AddMitigationPolicy(
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON);
+ }
+
+#if defined(_M_ARM64)
+ // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM.
+ if (!mozilla::IsWin10Sep2018UpdateOrLater()) {
+ aAttrs.AddMitigationPolicy(
+ PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF);
+ }
+#endif // defined(_M_ARM64)
+}
+
+static mozilla::LauncherFlags ProcessCmdLine(int& aArgc, wchar_t* aArgv[]) {
+ mozilla::LauncherFlags result = mozilla::LauncherFlags::eNone;
+
+ if (mozilla::CheckArg(aArgc, aArgv, L"wait-for-browser",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::RemoveArg) ==
+ mozilla::ARG_FOUND ||
+ mozilla::CheckArg(aArgc, aArgv, L"marionette",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND ||
+ mozilla::CheckArg(aArgc, aArgv, L"headless",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND ||
+ mozilla::EnvHasValue("MOZ_AUTOMATION") ||
+ mozilla::EnvHasValue("MOZ_HEADLESS")) {
+ result |= mozilla::LauncherFlags::eWaitForBrowser;
+ }
+
+ if (mozilla::CheckArg(aArgc, aArgv, L"no-deelevate") == mozilla::ARG_FOUND) {
+ result |= mozilla::LauncherFlags::eNoDeelevate;
+ }
+
+ return result;
+}
+
+static void MaybeBreakForBrowserDebugging() {
+ if (mozilla::EnvHasValue("MOZ_DEBUG_BROWSER_PROCESS")) {
+ ::DebugBreak();
+ return;
+ }
+
+ const wchar_t* pauseLenS = _wgetenv(L"MOZ_DEBUG_BROWSER_PAUSE");
+ if (!pauseLenS || !(*pauseLenS)) {
+ return;
+ }
+
+ DWORD pauseLenMs = wcstoul(pauseLenS, nullptr, 10) * 1000;
+ printf_stderr("\n\nBROWSERBROWSERBROWSERBROWSER\n debug me @ %lu\n\n",
+ ::GetCurrentProcessId());
+ ::Sleep(pauseLenMs);
+}
+
+static bool DoLauncherProcessChecks(int& argc, wchar_t** argv) {
+ // NB: We run all tests in this function instead of returning early in order
+ // to ensure that all side effects take place, such as clearing environment
+ // variables.
+ bool result = false;
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+ // We still prefer to compare file ids. Comparing NT paths i.e. passing
+ // CompareNtPathsOnly to IsSameBinaryAsParentProcess is much faster, but
+ // we're not 100% sure that NT path comparison perfectly prevents the
+ // launching loop of the launcher process.
+ mozilla::LauncherResult<bool> isSame = mozilla::IsSameBinaryAsParentProcess();
+ if (isSame.isOk()) {
+ result = !isSame.unwrap();
+ } else {
+ HandleLauncherError(isSame.unwrapErr());
+ }
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ if (mozilla::EnvHasValue("MOZ_LAUNCHER_PROCESS")) {
+ mozilla::SaveToEnv("MOZ_LAUNCHER_PROCESS=");
+ result = true;
+ }
+
+ result |= mozilla::CheckArg(
+ argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND;
+
+ return result;
+}
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+static mozilla::Maybe<bool> RunAsLauncherProcess(
+ mozilla::LauncherRegistryInfo& aRegInfo, int& argc, wchar_t** argv) {
+#else
+static mozilla::Maybe<bool> RunAsLauncherProcess(int& argc, wchar_t** argv) {
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+ // return fast when we're a child process.
+ // (The remainder of this function has some side effects that are
+ // undesirable for content processes)
+ if (mozilla::CheckArg(argc, argv, L"contentproc",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) {
+ return mozilla::Some(false);
+ }
+
+ bool runAsLauncher = DoLauncherProcessChecks(argc, argv);
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+ bool forceLauncher =
+ runAsLauncher &&
+ mozilla::CheckArg(argc, argv, L"force-launcher",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND;
+
+ mozilla::LauncherRegistryInfo::ProcessType desiredType =
+ runAsLauncher ? mozilla::LauncherRegistryInfo::ProcessType::Launcher
+ : mozilla::LauncherRegistryInfo::ProcessType::Browser;
+
+ mozilla::LauncherRegistryInfo::CheckOption checkOption =
+ forceLauncher ? mozilla::LauncherRegistryInfo::CheckOption::Force
+ : mozilla::LauncherRegistryInfo::CheckOption::Default;
+
+ mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType>
+ runAsType = aRegInfo.Check(desiredType, checkOption);
+
+ if (runAsType.isErr()) {
+ mozilla::HandleLauncherError(runAsType);
+ return mozilla::Nothing();
+ }
+
+ runAsLauncher = runAsType.unwrap() ==
+ mozilla::LauncherRegistryInfo::ProcessType::Launcher;
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ if (!runAsLauncher) {
+ // In this case, we will be proceeding to run as the browser.
+ // We should check MOZ_DEBUG_BROWSER_* env vars.
+ MaybeBreakForBrowserDebugging();
+ }
+
+ return mozilla::Some(runAsLauncher);
+}
+
+namespace mozilla {
+
+Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
+ const StaticXREAppData& aAppData) {
+ // Note: keep in sync with nsBrowserApp.
+ const wchar_t* acceptableParams[] = {L"url", nullptr};
+ EnsureCommandlineSafe(argc, argv, acceptableParams);
+
+ SetLauncherErrorAppData(aAppData);
+
+ if (CheckArg(argc, argv, L"log-launcher-error",
+ static_cast<const wchar_t**>(nullptr),
+ mozilla::CheckArgFlag::RemoveArg) == ARG_FOUND) {
+ SetLauncherErrorForceEventLog();
+ }
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+ LauncherRegistryInfo regInfo;
+ Maybe<bool> runAsLauncher = RunAsLauncherProcess(regInfo, argc, argv);
+#else
+ Maybe<bool> runAsLauncher = RunAsLauncherProcess(argc, argv);
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+ if (!runAsLauncher || !runAsLauncher.value()) {
+#if defined(MOZ_LAUNCHER_PROCESS)
+ // Update the registry as Browser
+ LauncherVoidResult commitResult = regInfo.Commit();
+ if (commitResult.isErr()) {
+ mozilla::HandleLauncherError(commitResult);
+ }
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+ return Nothing();
+ }
+
+ // Make sure that the launcher process itself has image load policies set
+ if (IsWin10AnniversaryUpdateOrLater()) {
+ static const StaticDynamicallyLinkedFunctionPtr<decltype(
+ &SetProcessMitigationPolicy)>
+ pSetProcessMitigationPolicy(L"kernel32.dll",
+ "SetProcessMitigationPolicy");
+ if (pSetProcessMitigationPolicy) {
+ PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgLoadPol = {};
+ imgLoadPol.PreferSystem32Images = 1;
+
+ DebugOnly<BOOL> setOk = pSetProcessMitigationPolicy(
+ ProcessImageLoadPolicy, &imgLoadPol, sizeof(imgLoadPol));
+ MOZ_ASSERT(setOk);
+ }
+ }
+
+ mozilla::UseParentConsole();
+
+ if (!SetArgv0ToFullBinaryPath(argv)) {
+ HandleLauncherError(LAUNCHER_ERROR_GENERIC());
+ return Nothing();
+ }
+
+ LauncherFlags flags = ProcessCmdLine(argc, argv);
+
+ nsAutoHandle mediumIlToken;
+ LauncherResult<ElevationState> elevationState =
+ GetElevationState(argv[0], flags, mediumIlToken);
+ if (elevationState.isErr()) {
+ HandleLauncherError(elevationState);
+ return Nothing();
+ }
+
+ // If we're elevated, we should relaunch ourselves as a normal user.
+ // Note that we only call LaunchUnelevated when we don't need to wait for the
+ // browser process.
+ if (elevationState.unwrap() == ElevationState::eElevated &&
+ !(flags &
+ (LauncherFlags::eWaitForBrowser | LauncherFlags::eNoDeelevate)) &&
+ !mediumIlToken.get()) {
+ LauncherVoidResult launchedUnelevated = LaunchUnelevated(argc, argv);
+ bool failed = launchedUnelevated.isErr();
+ if (failed) {
+ HandleLauncherError(launchedUnelevated);
+ return Nothing();
+ }
+
+ return Some(0);
+ }
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+ // Update the registry as Launcher
+ LauncherVoidResult commitResult = regInfo.Commit();
+ if (commitResult.isErr()) {
+ mozilla::HandleLauncherError(commitResult);
+ return Nothing();
+ }
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ // Now proceed with setting up the parameters for process creation
+ UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(argc, argv));
+ if (!cmdLine) {
+ HandleLauncherError(LAUNCHER_ERROR_GENERIC());
+ return Nothing();
+ }
+
+ const Maybe<bool> isSafeMode =
+ IsSafeModeRequested(argc, argv, SafeModeFlag::NoKeyPressCheck);
+ if (!isSafeMode) {
+ HandleLauncherError(LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_PARAMETER));
+ return Nothing();
+ }
+
+ ProcThreadAttributes attrs;
+ SetMitigationPolicies(attrs, isSafeMode.value());
+
+ HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE),
+ ::GetStdHandle(STD_OUTPUT_HANDLE),
+ ::GetStdHandle(STD_ERROR_HANDLE)};
+
+ attrs.AddInheritableHandles(stdHandles);
+
+ DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
+
+ STARTUPINFOEXW siex;
+ LauncherResult<bool> attrsOk = attrs.AssignTo(siex);
+ if (attrsOk.isErr()) {
+ HandleLauncherError(attrsOk);
+ return Nothing();
+ }
+
+ BOOL inheritHandles = FALSE;
+
+ if (attrsOk.unwrap()) {
+ creationFlags |= EXTENDED_STARTUPINFO_PRESENT;
+
+ if (attrs.HasInheritableHandles()) {
+ siex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ siex.StartupInfo.hStdInput = stdHandles[0];
+ siex.StartupInfo.hStdOutput = stdHandles[1];
+ siex.StartupInfo.hStdError = stdHandles[2];
+
+ // Since attrsOk == true, we have successfully set the handle inheritance
+ // whitelist policy, so only the handles added to attrs will be inherited.
+ inheritHandles = TRUE;
+ }
+ }
+
+ PROCESS_INFORMATION pi = {};
+ BOOL createOk;
+
+ if (mediumIlToken.get()) {
+ createOk =
+ ::CreateProcessAsUserW(mediumIlToken.get(), argv[0], cmdLine.get(),
+ nullptr, nullptr, inheritHandles, creationFlags,
+ nullptr, nullptr, &siex.StartupInfo, &pi);
+ } else {
+ createOk = ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr,
+ inheritHandles, creationFlags, nullptr, nullptr,
+ &siex.StartupInfo, &pi);
+ }
+
+ if (!createOk) {
+ HandleLauncherError(LAUNCHER_ERROR_FROM_LAST());
+ return Nothing();
+ }
+
+ nsAutoHandle process(pi.hProcess);
+ nsAutoHandle mainThread(pi.hThread);
+
+ LauncherVoidResult setupResult = PostCreationSetup(
+ argv[0], process.get(), mainThread.get(), isSafeMode.value());
+ if (setupResult.isErr()) {
+ HandleLauncherError(setupResult);
+ ::TerminateProcess(process.get(), 1);
+ return Nothing();
+ }
+
+ if (::ResumeThread(mainThread.get()) == static_cast<DWORD>(-1)) {
+ HandleLauncherError(LAUNCHER_ERROR_FROM_LAST());
+ ::TerminateProcess(process.get(), 1);
+ return Nothing();
+ }
+
+ if (flags & LauncherFlags::eWaitForBrowser) {
+ DWORD exitCode;
+ if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 &&
+ ::GetExitCodeProcess(process.get(), &exitCode)) {
+ // Propagate the browser process's exit code as our exit code.
+ return Some(static_cast<int>(exitCode));
+ }
+ } else {
+ const DWORD timeout =
+ ::IsDebuggerPresent() ? INFINITE : kWaitForInputIdleTimeoutMS;
+
+ // Keep the current process around until the callback process has created
+ // its message queue, to avoid the launched process's windows being forced
+ // into the background.
+ mozilla::WaitForInputIdle(process.get(), timeout);
+ }
+
+ return Some(0);
+}
+
+} // namespace mozilla
diff --git a/browser/app/winlauncher/LauncherProcessWin.h b/browser/app/winlauncher/LauncherProcessWin.h
new file mode 100644
index 0000000000..4fc819f658
--- /dev/null
+++ b/browser/app/winlauncher/LauncherProcessWin.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_LauncherProcessWin_h
+#define mozilla_LauncherProcessWin_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/TypedEnumBits.h"
+
+#include <stdint.h>
+
+namespace mozilla {
+
+// Forward declaration
+struct StaticXREAppData;
+
+/**
+ * Determine whether or not the current process should be run as the launcher
+ * process, and run if so. If we are not supposed to run as the launcher
+ * process, or in the event of a launcher process failure, return Nothing, thus
+ * indicating that we should continue on the original startup code path.
+ */
+Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
+ const StaticXREAppData& aAppData);
+
+enum class LauncherFlags : uint32_t {
+ eNone = 0,
+ eWaitForBrowser = (1 << 0), // Launcher should block until browser finishes
+ eNoDeelevate = (1 << 1), // If elevated, do not attempt to de-elevate
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LauncherFlags)
+
+} // namespace mozilla
+
+#endif // mozilla_LauncherProcessWin_h
diff --git a/browser/app/winlauncher/NtLoaderAPI.cpp b/browser/app/winlauncher/NtLoaderAPI.cpp
new file mode 100644
index 0000000000..805fdc881e
--- /dev/null
+++ b/browser/app/winlauncher/NtLoaderAPI.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/LoaderAPIInterfaces.h"
+
+#include "freestanding/CheckForCaller.h"
+#include "freestanding/LoaderPrivateAPI.h"
+
+namespace mozilla {
+
+extern "C" MOZ_EXPORT nt::LoaderAPI* GetNtLoaderAPI(
+ nt::LoaderObserver* aNewObserver) {
+ const bool isCallerMozglue =
+ CheckForAddress(RETURN_ADDRESS(), L"mozglue.dll");
+ MOZ_ASSERT(isCallerMozglue);
+ if (!isCallerMozglue) {
+ return nullptr;
+ }
+
+ freestanding::EnsureInitialized();
+ freestanding::LoaderPrivateAPI& api = freestanding::gLoaderPrivateAPI;
+ api.SetObserver(aNewObserver);
+
+ return &api;
+}
+
+} // namespace mozilla
diff --git a/browser/app/winlauncher/ProcThreadAttributes.h b/browser/app/winlauncher/ProcThreadAttributes.h
new file mode 100644
index 0000000000..74d5fee06c
--- /dev/null
+++ b/browser/app/winlauncher/ProcThreadAttributes.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ProcThreadAttributes_h
+#define mozilla_ProcThreadAttributes_h
+
+#include <windows.h>
+
+#include <utility>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+
+class MOZ_RAII ProcThreadAttributes final {
+ struct ProcThreadAttributeListDeleter {
+ void operator()(LPPROC_THREAD_ATTRIBUTE_LIST aList) {
+ ::DeleteProcThreadAttributeList(aList);
+ delete[] reinterpret_cast<char*>(aList);
+ }
+ };
+
+ using ProcThreadAttributeListPtr =
+ UniquePtr<_PROC_THREAD_ATTRIBUTE_LIST, ProcThreadAttributeListDeleter>;
+
+ public:
+ ProcThreadAttributes() : mMitigationPolicies(0) {}
+
+ ~ProcThreadAttributes() = default;
+
+ ProcThreadAttributes(const ProcThreadAttributes&) = delete;
+ ProcThreadAttributes(ProcThreadAttributes&&) = delete;
+ ProcThreadAttributes& operator=(const ProcThreadAttributes&) = delete;
+ ProcThreadAttributes& operator=(ProcThreadAttributes&&) = delete;
+
+ void AddMitigationPolicy(DWORD64 aPolicy) { mMitigationPolicies |= aPolicy; }
+
+ bool AddInheritableHandle(HANDLE aHandle) {
+ DWORD type = ::GetFileType(aHandle);
+ if (type != FILE_TYPE_DISK && type != FILE_TYPE_PIPE) {
+ return false;
+ }
+
+ if (!::SetHandleInformation(aHandle, HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT)) {
+ return false;
+ }
+
+ return mInheritableHandles.append(aHandle);
+ }
+
+ template <size_t N>
+ bool AddInheritableHandles(HANDLE (&aHandles)[N]) {
+ bool ok = true;
+ for (auto handle : aHandles) {
+ ok &= AddInheritableHandle(handle);
+ }
+
+ return ok;
+ }
+
+ bool HasMitigationPolicies() const { return !!mMitigationPolicies; }
+
+ bool HasInheritableHandles() const { return !mInheritableHandles.empty(); }
+
+ /**
+ * @return false if the STARTUPINFOEXW::lpAttributeList was set to null
+ * as expected based on the state of |this|;
+ * true if the STARTUPINFOEXW::lpAttributeList was set to
+ * non-null;
+ */
+ LauncherResult<bool> AssignTo(STARTUPINFOEXW& aSiex) {
+ ZeroMemory(&aSiex, sizeof(STARTUPINFOEXW));
+
+ // We'll set the size to sizeof(STARTUPINFOW) until we determine whether the
+ // extended fields will be used.
+ aSiex.StartupInfo.cb = sizeof(STARTUPINFOW);
+
+ DWORD numAttributes = 0;
+ if (HasMitigationPolicies()) {
+ ++numAttributes;
+ }
+
+ if (HasInheritableHandles()) {
+ ++numAttributes;
+ }
+
+ if (!numAttributes) {
+ return false;
+ }
+
+ SIZE_T listSize = 0;
+ if (!::InitializeProcThreadAttributeList(nullptr, numAttributes, 0,
+ &listSize)) {
+ DWORD err = ::GetLastError();
+ if (err != ERROR_INSUFFICIENT_BUFFER) {
+ return LAUNCHER_ERROR_FROM_WIN32(err);
+ }
+ }
+
+ auto buf = MakeUnique<char[]>(listSize);
+
+ LPPROC_THREAD_ATTRIBUTE_LIST tmpList =
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buf.get());
+
+ if (!::InitializeProcThreadAttributeList(tmpList, numAttributes, 0,
+ &listSize)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ // Transfer buf to a ProcThreadAttributeListPtr - now that the list is
+ // initialized, we are no longer dealing with a plain old char array. We
+ // must now deinitialize the attribute list before deallocating the
+ // underlying buffer.
+ ProcThreadAttributeListPtr attrList(
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(buf.release()));
+
+ if (mMitigationPolicies) {
+ if (!::UpdateProcThreadAttribute(
+ attrList.get(), 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,
+ &mMitigationPolicies, sizeof(mMitigationPolicies), nullptr,
+ nullptr)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+ }
+
+ if (!mInheritableHandles.empty()) {
+ if (!::UpdateProcThreadAttribute(
+ attrList.get(), 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ mInheritableHandles.begin(),
+ mInheritableHandles.length() * sizeof(HANDLE), nullptr,
+ nullptr)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+ }
+
+ mAttrList = std::move(attrList);
+ aSiex.lpAttributeList = mAttrList.get();
+ aSiex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ return true;
+ }
+
+ private:
+ static const uint32_t kNumInline = 3; // Inline storage for the std handles
+
+ DWORD64 mMitigationPolicies;
+ Vector<HANDLE, kNumInline> mInheritableHandles;
+ ProcThreadAttributeListPtr mAttrList;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ProcThreadAttributes_h
diff --git a/browser/app/winlauncher/SameBinary.h b/browser/app/winlauncher/SameBinary.h
new file mode 100644
index 0000000000..f382e69572
--- /dev/null
+++ b/browser/app/winlauncher/SameBinary.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SameBinary_h
+#define mozilla_SameBinary_h
+
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/NativeNt.h"
+#include "nsWindowsHelpers.h"
+
+namespace mozilla {
+
+class ProcessImagePath final {
+ PathType mType;
+ LauncherVoidResult mLastError;
+
+ // Using a larger buffer because an NT path may exceed MAX_PATH.
+ WCHAR mPathBuffer[(MAX_PATH * 2) + 1];
+
+ public:
+ // Initialize with an NT path string of a given process handle
+ explicit ProcessImagePath(const nsAutoHandle& aProcess)
+ : mType(PathType::eNtPath), mLastError(Ok()) {
+ DWORD len = mozilla::ArrayLength(mPathBuffer);
+ if (!::QueryFullProcessImageNameW(aProcess.get(), PROCESS_NAME_NATIVE,
+ mPathBuffer, &len)) {
+ mLastError = LAUNCHER_ERROR_FROM_LAST();
+ return;
+ }
+ }
+
+ // Initizlize with a DOS path string of a given imagebase address
+ explicit ProcessImagePath(HMODULE aImageBase)
+ : mType(PathType::eDosPath), mLastError(Ok()) {
+ DWORD len = ::GetModuleFileNameW(aImageBase, mPathBuffer,
+ mozilla::ArrayLength(mPathBuffer));
+ if (!len || len == mozilla::ArrayLength(mPathBuffer)) {
+ mLastError = LAUNCHER_ERROR_FROM_LAST();
+ return;
+ }
+ }
+
+ bool IsError() const { return mLastError.isErr(); }
+
+ const WindowsErrorType& GetError() const { return mLastError.inspectErr(); }
+
+ FileUniqueId GetId() const { return FileUniqueId(mPathBuffer, mType); }
+
+ bool CompareNtPaths(const ProcessImagePath& aOther) const {
+ if (mLastError.isErr() || aOther.mLastError.isErr() ||
+ mType != PathType::eNtPath || aOther.mType != PathType::eNtPath) {
+ return false;
+ }
+
+ UNICODE_STRING path1, path2;
+ ::RtlInitUnicodeString(&path1, mPathBuffer);
+ ::RtlInitUnicodeString(&path2, aOther.mPathBuffer);
+ return !!::RtlEqualUnicodeString(&path1, &path2, TRUE);
+ }
+};
+
+enum class ImageFileCompareOption {
+ Default,
+ CompareNtPathsOnly,
+};
+
+static inline mozilla::LauncherResult<bool> IsSameBinaryAsParentProcess(
+ ImageFileCompareOption aOption = ImageFileCompareOption::Default) {
+ mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId();
+ if (parentPid.isErr()) {
+ return parentPid.propagateErr();
+ }
+
+ nsAutoHandle parentProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE, parentPid.unwrap()));
+ if (!parentProcess.get()) {
+ DWORD err = ::GetLastError();
+ if (err == ERROR_INVALID_PARAMETER) {
+ // The process identified by parentPid has already exited. This is a
+ // common case when the parent process is not Firefox, thus we should
+ // return false instead of erroring out.
+ return false;
+ }
+
+ return LAUNCHER_ERROR_FROM_WIN32(err);
+ }
+
+ ProcessImagePath parentExe(parentProcess);
+ if (parentExe.IsError()) {
+ return ::mozilla::Err(parentExe.GetError());
+ }
+
+ if (aOption == ImageFileCompareOption::Default) {
+ bool skipFileIdComparison = false;
+
+ FileUniqueId id1 = parentExe.GetId();
+ if (id1.IsError()) {
+ // We saw a number of Win7 users failed to call NtOpenFile with
+ // STATUS_OBJECT_PATH_NOT_FOUND for an unknown reason. In this
+ // particular case, we fall back to the logic to compare NT path
+ // strings instead of a file id which will not fail because we don't
+ // need to open a file handle.
+#if !defined(STATUS_OBJECT_PATH_NOT_FOUND)
+ constexpr NTSTATUS STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a;
+#endif
+ const LauncherError& err = id1.GetError();
+ if (err.mError !=
+ WindowsError::FromNtStatus(STATUS_OBJECT_PATH_NOT_FOUND)) {
+ return ::mozilla::Err(err);
+ }
+
+ skipFileIdComparison = true;
+ }
+
+ if (!skipFileIdComparison) {
+ ProcessImagePath ourExe(nullptr);
+ if (ourExe.IsError()) {
+ return ::mozilla::Err(ourExe.GetError());
+ }
+
+ FileUniqueId id2 = ourExe.GetId();
+ if (id2.IsError()) {
+ return ::mozilla::Err(id2.GetError());
+ }
+ return id1 == id2;
+ }
+ }
+
+ nsAutoHandle ourProcess(::GetCurrentProcess());
+ ProcessImagePath ourExeNt(ourProcess);
+ if (ourExeNt.IsError()) {
+ return ::mozilla::Err(ourExeNt.GetError());
+ }
+ return parentExe.CompareNtPaths(ourExeNt);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_SameBinary_h
diff --git a/browser/app/winlauncher/freestanding/CheckForCaller.h b/browser/app/winlauncher/freestanding/CheckForCaller.h
new file mode 100644
index 0000000000..799d16cd1f
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/CheckForCaller.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_CheckForCaller_h
+#define mozilla_freestanding_CheckForCaller_h
+
+namespace mozilla {
+
+#if defined(_MSC_VER)
+# include <intrin.h>
+# pragma intrinsic(_ReturnAddress)
+# define RETURN_ADDRESS() _ReturnAddress()
+#elif defined(__GNUC__) || defined(__clang__)
+# define RETURN_ADDRESS() \
+ __builtin_extract_return_addr(__builtin_return_address(0))
+#endif
+
+template <int N>
+bool CheckForAddress(void* aReturnAddress, const wchar_t (&aName)[N]) {
+ HMODULE callingModule;
+ if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(aReturnAddress),
+ &callingModule)) {
+ return false;
+ }
+
+ return callingModule && callingModule == ::GetModuleHandleW(aName);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_freestanding_CheckForCaller_h
diff --git a/browser/app/winlauncher/freestanding/DllBlocklist.cpp b/browser/app/winlauncher/freestanding/DllBlocklist.cpp
new file mode 100644
index 0000000000..2806cec87e
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/DllBlocklist.cpp
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/Types.h"
+#include "mozilla/WindowsDllBlocklist.h"
+
+#include "DllBlocklist.h"
+#include "LoaderPrivateAPI.h"
+#include "ModuleLoadFrame.h"
+#include "SharedSection.h"
+
+// clang-format off
+#define MOZ_LITERAL_UNICODE_STRING(s) \
+ { \
+ /* Length of the string in bytes, less the null terminator */ \
+ sizeof(s) - sizeof(wchar_t), \
+ /* Length of the string in bytes, including the null terminator */ \
+ sizeof(s), \
+ /* Pointer to the buffer */ \
+ const_cast<wchar_t*>(s) \
+ }
+// clang-format on
+
+#define DLL_BLOCKLIST_ENTRY(name, ...) \
+ {MOZ_LITERAL_UNICODE_STRING(L##name), __VA_ARGS__},
+#define DLL_BLOCKLIST_STRING_TYPE UNICODE_STRING
+
+#if defined(MOZ_LAUNCHER_PROCESS) || defined(NIGHTLY_BUILD)
+# include "mozilla/WindowsDllBlocklistLauncherDefs.h"
+#else
+# include "mozilla/WindowsDllBlocklistCommon.h"
+DLL_BLOCKLIST_DEFINITIONS_BEGIN
+DLL_BLOCKLIST_DEFINITIONS_END
+#endif
+
+class MOZ_STATIC_CLASS MOZ_TRIVIAL_CTOR_DTOR NativeNtBlockSet final {
+ struct NativeNtBlockSetEntry {
+ NativeNtBlockSetEntry() = default;
+ ~NativeNtBlockSetEntry() = default;
+ NativeNtBlockSetEntry(const UNICODE_STRING& aName, uint64_t aVersion,
+ NativeNtBlockSetEntry* aNext)
+ : mName(aName), mVersion(aVersion), mNext(aNext) {}
+ UNICODE_STRING mName;
+ uint64_t mVersion;
+ NativeNtBlockSetEntry* mNext;
+ };
+
+ public:
+ // Constructor and destructor MUST be trivial
+ constexpr NativeNtBlockSet() : mFirstEntry(nullptr) {}
+ ~NativeNtBlockSet() = default;
+
+ void Add(const UNICODE_STRING& aName, uint64_t aVersion);
+ void Write(HANDLE aFile);
+
+ private:
+ static NativeNtBlockSetEntry* NewEntry(const UNICODE_STRING& aName,
+ uint64_t aVersion,
+ NativeNtBlockSetEntry* aNextEntry);
+
+ private:
+ NativeNtBlockSetEntry* mFirstEntry;
+ mozilla::nt::SRWLock mLock;
+};
+
+NativeNtBlockSet::NativeNtBlockSetEntry* NativeNtBlockSet::NewEntry(
+ const UNICODE_STRING& aName, uint64_t aVersion,
+ NativeNtBlockSet::NativeNtBlockSetEntry* aNextEntry) {
+ return mozilla::freestanding::RtlNew<NativeNtBlockSetEntry>(aName, aVersion,
+ aNextEntry);
+}
+
+void NativeNtBlockSet::Add(const UNICODE_STRING& aName, uint64_t aVersion) {
+ mozilla::nt::AutoExclusiveLock lock(mLock);
+
+ for (NativeNtBlockSetEntry* entry = mFirstEntry; entry;
+ entry = entry->mNext) {
+ if (::RtlEqualUnicodeString(&entry->mName, &aName, TRUE) &&
+ aVersion == entry->mVersion) {
+ return;
+ }
+ }
+
+ // Not present, add it
+ NativeNtBlockSetEntry* newEntry = NewEntry(aName, aVersion, mFirstEntry);
+ if (newEntry) {
+ mFirstEntry = newEntry;
+ }
+}
+
+void NativeNtBlockSet::Write(HANDLE aFile) {
+ // NB: If this function is called, it is long after kernel32 is initialized,
+ // so it is safe to use Win32 calls here.
+ DWORD nBytes;
+ char buf[MAX_PATH];
+
+ // It would be nicer to use RAII here. However, its destructor
+ // might not run if an exception occurs, in which case we would never release
+ // the lock (MSVC warns about this possibility). So we acquire and release
+ // manually.
+ ::AcquireSRWLockExclusive(&mLock);
+
+ MOZ_SEH_TRY {
+ for (auto entry = mFirstEntry; entry; entry = entry->mNext) {
+ int convOk = ::WideCharToMultiByte(CP_UTF8, 0, entry->mName.Buffer,
+ entry->mName.Length / sizeof(wchar_t),
+ buf, sizeof(buf), nullptr, nullptr);
+ if (!convOk) {
+ continue;
+ }
+
+ // write name[,v.v.v.v];
+ if (!WriteFile(aFile, buf, convOk, &nBytes, nullptr)) {
+ continue;
+ }
+
+ if (entry->mVersion != DllBlockInfo::ALL_VERSIONS) {
+ WriteFile(aFile, ",", 1, &nBytes, nullptr);
+ uint16_t parts[4];
+ parts[0] = entry->mVersion >> 48;
+ parts[1] = (entry->mVersion >> 32) & 0xFFFF;
+ parts[2] = (entry->mVersion >> 16) & 0xFFFF;
+ parts[3] = entry->mVersion & 0xFFFF;
+ for (size_t p = 0; p < mozilla::ArrayLength(parts); ++p) {
+ ltoa(parts[p], buf, 10);
+ WriteFile(aFile, buf, strlen(buf), &nBytes, nullptr);
+ if (p != mozilla::ArrayLength(parts) - 1) {
+ WriteFile(aFile, ".", 1, &nBytes, nullptr);
+ }
+ }
+ }
+ WriteFile(aFile, ";", 1, &nBytes, nullptr);
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {}
+
+ ::ReleaseSRWLockExclusive(&mLock);
+}
+
+static NativeNtBlockSet gBlockSet;
+
+extern "C" void MOZ_EXPORT NativeNtBlockSet_Write(HANDLE aHandle) {
+ gBlockSet.Write(aHandle);
+}
+
+enum class BlockAction {
+ Allow,
+ SubstituteLSP,
+ Error,
+ Deny,
+ NoOpEntryPoint,
+};
+
+static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo,
+ const mozilla::nt::PEHeaders& aHeaders,
+ uint64_t& aVersion) {
+ aVersion = DllBlockInfo::ALL_VERSIONS;
+
+ if (aInfo->mFlags & (DllBlockInfo::BLOCK_WIN8_AND_OLDER |
+ DllBlockInfo::BLOCK_WIN7_AND_OLDER)) {
+ RTL_OSVERSIONINFOW osv = {sizeof(osv)};
+ NTSTATUS ntStatus = ::RtlGetVersion(&osv);
+ if (!NT_SUCCESS(ntStatus)) {
+ return BlockAction::Error;
+ }
+
+ if ((aInfo->mFlags & DllBlockInfo::BLOCK_WIN8_AND_OLDER) &&
+ (osv.dwMajorVersion > 6 ||
+ (osv.dwMajorVersion == 6 && osv.dwMinorVersion > 2))) {
+ return BlockAction::Allow;
+ }
+
+ if ((aInfo->mFlags & DllBlockInfo::BLOCK_WIN7_AND_OLDER) &&
+ (osv.dwMajorVersion > 6 ||
+ (osv.dwMajorVersion == 6 && osv.dwMinorVersion > 1))) {
+ return BlockAction::Allow;
+ }
+ }
+
+ if ((aInfo->mFlags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
+ !(gBlocklistInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
+ return BlockAction::Allow;
+ }
+
+ if ((aInfo->mFlags & DllBlockInfo::BROWSER_PROCESS_ONLY) &&
+ (gBlocklistInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
+ return BlockAction::Allow;
+ }
+
+ if (aInfo->mMaxVersion == DllBlockInfo::ALL_VERSIONS) {
+ return BlockAction::Deny;
+ }
+
+ if (!aHeaders) {
+ return BlockAction::Error;
+ }
+
+ if (aInfo->mFlags & DllBlockInfo::USE_TIMESTAMP) {
+ DWORD timestamp;
+ if (!aHeaders.GetTimeStamp(timestamp)) {
+ return BlockAction::Error;
+ }
+
+ if (timestamp > aInfo->mMaxVersion) {
+ return BlockAction::Allow;
+ }
+
+ return BlockAction::Deny;
+ }
+
+ // Else we try to get the file version information. Note that we don't have
+ // access to GetFileVersionInfo* APIs.
+ if (!aHeaders.GetVersionInfo(aVersion)) {
+ return BlockAction::Error;
+ }
+
+ if (aInfo->IsVersionBlocked(aVersion)) {
+ return BlockAction::Deny;
+ }
+
+ return BlockAction::Allow;
+}
+
+struct DllBlockInfoComparator {
+ explicit DllBlockInfoComparator(const UNICODE_STRING& aTarget)
+ : mTarget(&aTarget) {}
+
+ int operator()(const DllBlockInfo& aVal) const {
+ return static_cast<int>(
+ ::RtlCompareUnicodeString(mTarget, &aVal.mName, TRUE));
+ }
+
+ PCUNICODE_STRING mTarget;
+};
+
+static BOOL WINAPI NoOp_DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
+
+// This helper function checks whether a given module is included
+// in the executable's Import Table. Because an injected module's
+// DllMain may revert the Import Table to the original state, we parse
+// the Import Table every time a module is loaded without creating a cache.
+static bool IsDependentModule(
+ const UNICODE_STRING& aModuleLeafName,
+ mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) {
+ // We enable automatic DLL blocking only in Nightly for now because it caused
+ // a compat issue (bug 1682304).
+#if defined(NIGHTLY_BUILD)
+ aK32Exports.Resolve(mozilla::freestanding::gK32ExportsResolveOnce);
+ if (!aK32Exports.IsResolved()) {
+ return false;
+ }
+
+ mozilla::nt::PEHeaders exeHeaders(aK32Exports.mGetModuleHandleW(nullptr));
+ if (!exeHeaders || !exeHeaders.IsImportDirectoryTampered()) {
+ // If no tampering is detected, no need to enumerate the Import Table.
+ return false;
+ }
+
+ bool isDependent = false;
+ exeHeaders.EnumImportChunks(
+ [&isDependent, &aModuleLeafName, &exeHeaders](const char* aDepModule) {
+ // If |aDepModule| is within the PE image, it's not an injected module
+ // but a legitimate dependent module.
+ if (isDependent || exeHeaders.IsWithinImage(aDepModule)) {
+ return;
+ }
+
+ UNICODE_STRING depModuleLeafName;
+ mozilla::nt::AllocatedUnicodeString depModuleName(aDepModule);
+ mozilla::nt::GetLeafName(&depModuleLeafName, depModuleName);
+ isDependent = (::RtlCompareUnicodeString(
+ &aModuleLeafName, &depModuleLeafName, TRUE) == 0);
+ });
+ return isDependent;
+#else
+ return false;
+#endif
+}
+
+// Allowing a module to be loaded but detour the entrypoint to NoOp_DllMain
+// so that the module has no chance to interact with our code. We need this
+// technique to safely block a module injected by IAT tampering because
+// blocking such a module makes a process fail to launch.
+static bool RedirectToNoOpEntryPoint(
+ const mozilla::nt::PEHeaders& aModule,
+ mozilla::freestanding::Kernel32ExportsSolver& aK32Exports) {
+ aK32Exports.Resolve(mozilla::freestanding::gK32ExportsResolveOnce);
+ if (!aK32Exports.IsResolved()) {
+ return false;
+ }
+
+ mozilla::interceptor::WindowsDllEntryPointInterceptor interceptor(
+ aK32Exports);
+ if (!interceptor.Set(aModule, NoOp_DllMain)) {
+ return false;
+ }
+
+ return true;
+}
+
+static BlockAction DetermineBlockAction(
+ const UNICODE_STRING& aLeafName, void* aBaseAddress,
+ mozilla::freestanding::Kernel32ExportsSolver* aK32Exports) {
+ if (mozilla::nt::Contains12DigitHexString(aLeafName) ||
+ mozilla::nt::IsFileNameAtLeast16HexDigits(aLeafName)) {
+ return BlockAction::Deny;
+ }
+
+ DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
+ DECLARE_DLL_BLOCKLIST_NUM_ENTRIES(infoNumEntries);
+
+ DllBlockInfoComparator comp(aLeafName);
+
+ size_t match;
+ if (!BinarySearchIf(info, 0, infoNumEntries, comp, &match)) {
+ return BlockAction::Allow;
+ }
+
+ const DllBlockInfo& entry = info[match];
+
+ mozilla::nt::PEHeaders headers(aBaseAddress);
+ uint64_t version;
+ BlockAction checkResult = CheckBlockInfo(&entry, headers, version);
+ if (checkResult == BlockAction::Allow) {
+ return checkResult;
+ }
+
+ gBlockSet.Add(entry.mName, version);
+
+ if ((entry.mFlags & DllBlockInfo::REDIRECT_TO_NOOP_ENTRYPOINT) &&
+ aK32Exports && RedirectToNoOpEntryPoint(headers, *aK32Exports)) {
+ return BlockAction::NoOpEntryPoint;
+ }
+
+ return checkResult;
+}
+
+namespace mozilla {
+namespace freestanding {
+
+CrossProcessDllInterceptor::FuncHookType<LdrLoadDllPtr> stub_LdrLoadDll;
+RTL_RUN_ONCE gK32ExportsResolveOnce = RTL_RUN_ONCE_INIT;
+
+NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
+ PUNICODE_STRING aDllName,
+ PHANDLE aOutHandle) {
+ ModuleLoadFrame frame(aDllName);
+
+ NTSTATUS ntStatus = stub_LdrLoadDll(aDllPath, aFlags, aDllName, aOutHandle);
+
+ return frame.SetLoadStatus(ntStatus, aOutHandle);
+}
+
+CrossProcessDllInterceptor::FuncHookType<NtMapViewOfSectionPtr>
+ stub_NtMapViewOfSection;
+
+NTSTATUS NTAPI patched_NtMapViewOfSection(
+ HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
+ SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
+ SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
+ ULONG aProtectionFlags) {
+ // We always map first, then we check for additional info after.
+ NTSTATUS stubStatus = stub_NtMapViewOfSection(
+ aSection, aProcess, aBaseAddress, aZeroBits, aCommitSize, aSectionOffset,
+ aViewSize, aInheritDisposition, aAllocationType, aProtectionFlags);
+ if (!NT_SUCCESS(stubStatus)) {
+ return stubStatus;
+ }
+
+ if (aProcess != nt::kCurrentProcess) {
+ // We're only interested in mapping for the current process.
+ return stubStatus;
+ }
+
+ // Do a query to see if the memory is MEM_IMAGE. If not, continue
+ MEMORY_BASIC_INFORMATION mbi;
+ NTSTATUS ntStatus =
+ ::NtQueryVirtualMemory(aProcess, *aBaseAddress, MemoryBasicInformation,
+ &mbi, sizeof(mbi), nullptr);
+ if (!NT_SUCCESS(ntStatus)) {
+ ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ // We don't care about mappings that aren't MEM_IMAGE
+ if (!(mbi.Type & MEM_IMAGE)) {
+ return stubStatus;
+ }
+
+ // Get the section name
+ nt::MemorySectionNameBuf sectionFileName(
+ gLoaderPrivateAPI.GetSectionNameBuffer(*aBaseAddress));
+ if (sectionFileName.IsEmpty()) {
+ ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ // Find the leaf name
+ UNICODE_STRING leafOnStack;
+ nt::GetLeafName(&leafOnStack, sectionFileName);
+
+ bool isDependent = false;
+ auto resultView = freestanding::gSharedSection.GetView();
+ // Small optimization: Since loading a dependent module does not involve
+ // LdrLoadDll, we know isDependent is false if we hold a top frame.
+ if (resultView.isOk() && !ModuleLoadFrame::ExistsTopFrame()) {
+ isDependent =
+ IsDependentModule(leafOnStack, resultView.inspect()->mK32Exports);
+ }
+
+ BlockAction blockAction;
+ if (isDependent) {
+ // Add an NT dv\path to the shared section so that a sandbox process can
+ // use it to bypass CIG. In a sandbox process, this addition fails
+ // because we cannot map the section to a writable region, but it's
+ // ignorable because the paths have been added by the browser process.
+ Unused << freestanding::gSharedSection.AddDepenentModule(sectionFileName);
+
+ // For a dependent module, try redirection instead of blocking it.
+ // If we fail, we reluctantly allow the module for free.
+ mozilla::nt::PEHeaders headers(*aBaseAddress);
+ blockAction =
+ RedirectToNoOpEntryPoint(headers, resultView.inspect()->mK32Exports)
+ ? BlockAction::NoOpEntryPoint
+ : BlockAction::Allow;
+ } else {
+ // Check blocklist
+ blockAction = DetermineBlockAction(
+ leafOnStack, *aBaseAddress,
+ resultView.isOk() ? &resultView.inspect()->mK32Exports : nullptr);
+ }
+
+ ModuleLoadInfo::Status loadStatus = ModuleLoadInfo::Status::Blocked;
+
+ switch (blockAction) {
+ case BlockAction::Allow:
+ loadStatus = ModuleLoadInfo::Status::Loaded;
+ break;
+
+ case BlockAction::NoOpEntryPoint:
+ loadStatus = ModuleLoadInfo::Status::Redirected;
+ break;
+
+ case BlockAction::SubstituteLSP:
+ // The process heap needs to be available here because
+ // NotifyLSPSubstitutionRequired below copies a given string into
+ // the heap. We use a soft assert here, assuming LSP load always
+ // occurs after the heap is initialized.
+ MOZ_ASSERT(nt::RtlGetProcessHeap());
+
+ // Notify patched_LdrLoadDll that it will be necessary to perform
+ // a substitution before returning.
+ ModuleLoadFrame::NotifyLSPSubstitutionRequired(&leafOnStack);
+ break;
+
+ default:
+ break;
+ }
+
+ if (nt::RtlGetProcessHeap()) {
+ ModuleLoadFrame::NotifySectionMap(
+ nt::AllocatedUnicodeString(sectionFileName), *aBaseAddress, stubStatus,
+ loadStatus);
+ }
+
+ if (loadStatus == ModuleLoadInfo::Status::Loaded ||
+ loadStatus == ModuleLoadInfo::Status::Redirected) {
+ return stubStatus;
+ }
+
+ ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+ return STATUS_ACCESS_DENIED;
+}
+
+} // namespace freestanding
+} // namespace mozilla
diff --git a/browser/app/winlauncher/freestanding/DllBlocklist.h b/browser/app/winlauncher/freestanding/DllBlocklist.h
new file mode 100644
index 0000000000..153a1dfb28
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/DllBlocklist.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_DllBlocklist_h
+#define mozilla_freestanding_DllBlocklist_h
+
+#include "mozilla/NativeNt.h"
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+
+namespace mozilla {
+namespace freestanding {
+
+NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
+ PUNICODE_STRING aDllName, PHANDLE aOutHandle);
+
+NTSTATUS NTAPI patched_NtMapViewOfSection(
+ HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
+ SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
+ SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
+ ULONG aProtectionFlags);
+
+using LdrLoadDllPtr = decltype(&::LdrLoadDll);
+
+extern CrossProcessDllInterceptor::FuncHookType<LdrLoadDllPtr> stub_LdrLoadDll;
+
+using NtMapViewOfSectionPtr = decltype(&::NtMapViewOfSection);
+
+extern CrossProcessDllInterceptor::FuncHookType<NtMapViewOfSectionPtr>
+ stub_NtMapViewOfSection;
+
+} // namespace freestanding
+} // namespace mozilla
+
+#endif // mozilla_freestanding_DllBlocklist_h
diff --git a/browser/app/winlauncher/freestanding/Freestanding.h b/browser/app/winlauncher/freestanding/Freestanding.h
new file mode 100644
index 0000000000..03999f231e
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/Freestanding.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_Freestanding_h
+#define mozilla_freestanding_Freestanding_h
+
+/**
+ * This header is automatically included in all source code residing in the
+ * /browser/app/winlauncher/freestanding directory.
+ */
+
+#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1
+# error "This header should only be included by freestanding code"
+#endif // defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1
+
+#define MOZ_USE_LAUNCHER_ERROR
+#include "mozilla/NativeNt.h"
+
+namespace mozilla {
+namespace freestanding {
+
+/**
+ * Since this library is the only part of firefox.exe that needs special
+ * treatment with respect to the heap, we implement |RtlNew| and |RtlDelete|
+ * to be used instead of |new| and |delete| for any heap allocations inside
+ * the freestanding library.
+ */
+template <typename T, typename... Args>
+inline static T* RtlNew(Args&&... aArgs) {
+ HANDLE processHeap = nt::RtlGetProcessHeap();
+ if (!processHeap) {
+ // Handle the case where the process heap is not initialized because
+ // passing nullptr to RtlAllocateHeap crashes the process.
+ return nullptr;
+ }
+
+ void* ptr = ::RtlAllocateHeap(processHeap, 0, sizeof(T));
+ if (!ptr) {
+ return nullptr;
+ }
+
+ return new (ptr) T(std::forward<Args>(aArgs)...);
+}
+
+template <typename T>
+inline static void RtlDelete(T* aPtr) {
+ if (!aPtr) {
+ return;
+ }
+
+ aPtr->~T();
+ ::RtlFreeHeap(nt::RtlGetProcessHeap(), 0, aPtr);
+}
+
+} // namespace freestanding
+} // namespace mozilla
+
+// Initialization code for all statically-allocated data in freestanding is
+// placed into a separate section. This allows us to initialize any
+// freestanding statics without needing to initialize everything else in this
+// binary.
+#pragma init_seg(".freestd$g")
+
+#endif // mozilla_freestanding_Freestanding_h
diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
new file mode 100644
index 0000000000..527b6556a6
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "LoaderPrivateAPI.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Types.h"
+#include "mozilla/Unused.h"
+#include "../DllBlocklistInit.h"
+#include "../ErrorHandler.h"
+
+using GlobalInitializerFn = void(__cdecl*)(void);
+
+// Allocation of static initialization section for the freestanding library
+#pragma section(".freestd$a", read)
+__declspec(allocate(".freestd$a")) static const GlobalInitializerFn
+ FreeStdStart = reinterpret_cast<GlobalInitializerFn>(0);
+
+#pragma section(".freestd$z", read)
+__declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd =
+ reinterpret_cast<GlobalInitializerFn>(0);
+
+namespace mozilla {
+namespace freestanding {
+
+static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT;
+
+// The contract for this callback is identical to the InitOnceCallback from
+// Win32 land; we're just using ntdll-layer types instead.
+static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
+ PVOID* aContext) {
+ // Invoke every static initializer in the .freestd section
+ const GlobalInitializerFn* cur = &FreeStdStart + 1;
+ while (cur < &FreeStdEnd) {
+ if (*cur) {
+ (*cur)();
+ }
+
+ ++cur;
+ }
+
+ return TRUE;
+}
+
+/**
+ * This observer is only used until the mozglue observer connects itself.
+ * All we do here is accumulate the module loads into a vector.
+ * As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver
+ * to pass our vector on for further processing. This object then becomes
+ * defunct.
+ */
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final
+ : public nt::LoaderObserver {
+ public:
+ constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {}
+
+ void OnBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aRequestedDllName) final {}
+ bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) final {
+ return false;
+ }
+ void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) final;
+ void Forward(nt::LoaderObserver* aNext) final;
+ void OnForward(ModuleLoadInfoVec&& aInfo) final {
+ MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver");
+ }
+
+ private:
+ mozilla::nt::SRWLock mLock;
+ ModuleLoadInfoVec* mModuleLoads;
+};
+
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final
+ : public LoaderPrivateAPI {
+ public:
+ // LoaderAPI
+ ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) final;
+ bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) final;
+ void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) final;
+ nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
+ nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final;
+ nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final;
+
+ // LoaderPrivateAPI
+ void NotifyBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aRequestedDllName) final;
+ void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext,
+ PCUNICODE_STRING aRequestedDllName) final;
+ void SetObserver(nt::LoaderObserver* aNewObserver) final;
+ bool IsDefaultObserver() const final;
+ nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
+};
+
+static void Init() {
+ DebugOnly<NTSTATUS> ntStatus =
+ ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr);
+ MOZ_ASSERT(NT_SUCCESS(ntStatus));
+}
+
+} // namespace freestanding
+} // namespace mozilla
+
+static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver;
+static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI;
+
+static mozilla::nt::SRWLock gLoaderObserverLock;
+static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver;
+
+namespace mozilla {
+namespace freestanding {
+
+LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI;
+
+void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) {
+ // If the DLL load failed, or if the DLL was loaded by a previous request
+ // and thus was not mapped by this request, we do not save the ModuleLoadInfo.
+ if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) {
+ return;
+ }
+
+ nt::AutoExclusiveLock lock(mLock);
+ if (!mModuleLoads) {
+ mModuleLoads = RtlNew<ModuleLoadInfoVec>();
+ if (!mModuleLoads) {
+ return;
+ }
+ }
+
+ Unused << mModuleLoads->emplaceBack(
+ std::forward<ModuleLoadInfo>(aModuleLoadInfo));
+}
+
+/**
+ * Pass mModuleLoads's data off to |aNext| for further processing.
+ */
+void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) {
+ MOZ_ASSERT(aNext);
+ if (!aNext) {
+ return;
+ }
+
+ ModuleLoadInfoVec* moduleLoads = nullptr;
+
+ { // Scope for lock
+ nt::AutoExclusiveLock lock(mLock);
+ moduleLoads = mModuleLoads;
+ mModuleLoads = nullptr;
+ }
+
+ if (!moduleLoads) {
+ return;
+ }
+
+ aNext->OnForward(std::move(*moduleLoads));
+ RtlDelete(moduleLoads);
+}
+
+ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) {
+ ModuleLoadInfo loadInfo(aRequestedDllName);
+
+ NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName);
+
+ return loadInfo;
+}
+
+bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) {
+ nt::AutoSharedLock lock(gLoaderObserverLock);
+ return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
+}
+
+void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext,
+ NTSTATUS aLoadNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) {
+ aModuleLoadInfo.SetEndLoadTimeStamp();
+
+ if (NT_SUCCESS(aLoadNtStatus)) {
+ aModuleLoadInfo.CaptureBacktrace();
+ }
+
+ nt::AutoSharedLock lock(gLoaderObserverLock);
+
+ // We need to notify the observer that the DLL load has ended even when
+ // |aLoadNtStatus| indicates a failure. This is to ensure that any resources
+ // acquired by the observer during OnBeginDllLoad are cleaned up.
+ gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
+ std::move(aModuleLoadInfo));
+}
+
+nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
+ void* aSectionAddr) {
+ const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
+
+ nt::MemorySectionNameBuf buf;
+ NTSTATUS ntStatus =
+ ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
+ &buf, sizeof(buf), nullptr);
+ if (!NT_SUCCESS(ntStatus)) {
+ return nt::AllocatedUnicodeString();
+ }
+
+ return nt::AllocatedUnicodeString(&buf.mSectionFileName);
+}
+
+nt::LoaderAPI::InitDllBlocklistOOPFnPtr
+LoaderPrivateAPIImp::GetDllBlocklistInitFn() {
+ return &InitializeDllBlocklistOOP;
+}
+
+nt::LoaderAPI::HandleLauncherErrorFnPtr
+LoaderPrivateAPIImp::GetHandleLauncherErrorFn() {
+ return &HandleLauncherError;
+}
+
+nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
+ void* aSectionAddr) {
+ const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
+
+ nt::MemorySectionNameBuf buf;
+ NTSTATUS ntStatus =
+ ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
+ &buf, sizeof(buf), nullptr);
+ if (!NT_SUCCESS(ntStatus)) {
+ return nt::MemorySectionNameBuf();
+ }
+
+ return buf;
+}
+
+void LoaderPrivateAPIImp::NotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) {
+ nt::AutoSharedLock lock(gLoaderObserverLock);
+ gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
+}
+
+void LoaderPrivateAPIImp::NotifyBeginDllLoad(
+ ModuleLoadInfo& aModuleLoadInfo, void** aContext,
+ PCUNICODE_STRING aRequestedDllName) {
+ NotifyBeginDllLoad(aContext, aRequestedDllName);
+ aModuleLoadInfo.SetBeginLoadTimeStamp();
+}
+
+void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) {
+ nt::LoaderObserver* prevLoaderObserver = nullptr;
+
+ nt::AutoExclusiveLock lock(gLoaderObserverLock);
+
+ MOZ_ASSERT(aNewObserver);
+ if (!aNewObserver) {
+ // This is unlikely, but we always want a valid observer, so use the
+ // gDefaultObserver if necessary.
+ gLoaderObserver = &gDefaultObserver;
+ return;
+ }
+
+ prevLoaderObserver = gLoaderObserver;
+ gLoaderObserver = aNewObserver;
+
+ MOZ_ASSERT(prevLoaderObserver);
+ if (!prevLoaderObserver) {
+ return;
+ }
+
+ // Now that we have a new observer, the previous observer must forward its
+ // data on to the new observer for processing.
+ prevLoaderObserver->Forward(aNewObserver);
+}
+
+bool LoaderPrivateAPIImp::IsDefaultObserver() const {
+ nt::AutoSharedLock lock(gLoaderObserverLock);
+ return gLoaderObserver == &gDefaultObserver;
+}
+
+void EnsureInitialized() { Init(); }
+
+} // namespace freestanding
+} // namespace mozilla
diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h
new file mode 100644
index 0000000000..f21472d689
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_LoaderPrivateAPI_h
+#define mozilla_freestanding_LoaderPrivateAPI_h
+
+#include "mozilla/LoaderAPIInterfaces.h"
+
+namespace mozilla {
+namespace freestanding {
+
+/**
+ * This part of the API is available only to the launcher process.
+ */
+class NS_NO_VTABLE LoaderPrivateAPI : public nt::LoaderAPI {
+ public:
+ /**
+ * Notify the nt::LoaderObserver that a module load is beginning
+ */
+ virtual void NotifyBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aRequestedDllName) = 0;
+ /**
+ * Notify the nt::LoaderObserver that a module load is beginning and set the
+ * begin load timestamp on |aModuleLoadInfo|.
+ */
+ virtual void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo,
+ void** aContext,
+ PCUNICODE_STRING aRequestedDllName) = 0;
+
+ /**
+ * Set a new nt::LoaderObserver to be used by the launcher process. NB: This
+ * should only happen while the current process is still single-threaded!
+ */
+ virtual void SetObserver(nt::LoaderObserver* aNewObserver) = 0;
+
+ /**
+ * Returns true if the current nt::LoaderObserver is the launcher process's
+ * built-in observer.
+ */
+ virtual bool IsDefaultObserver() const = 0;
+
+ /**
+ * Returns the name of a given mapped section address as a local instance of
+ * nt::MemorySectionNameBuf. This does not involve heap allocation.
+ */
+ virtual nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) = 0;
+};
+
+/**
+ * Ensures that any statics in the freestanding library are initialized.
+ */
+void EnsureInitialized();
+
+extern LoaderPrivateAPI& gLoaderPrivateAPI;
+
+} // namespace freestanding
+} // namespace mozilla
+
+#endif // mozilla_freestanding_LoaderPrivateAPI_h
diff --git a/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp b/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp
new file mode 100644
index 0000000000..c734e70ed5
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/ModuleLoadFrame.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "ModuleLoadFrame.h"
+
+#include "LoaderPrivateAPI.h"
+
+namespace mozilla {
+namespace freestanding {
+
+ModuleLoadFrame::ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName)
+ : mPrev(sTopFrame.get()),
+ mContext(nullptr),
+ mLSPSubstitutionRequired(false),
+ mLoadNtStatus(STATUS_UNSUCCESSFUL),
+ mLoadInfo(aRequestedDllName) {
+ EnsureInitialized();
+ sTopFrame.set(this);
+
+ gLoaderPrivateAPI.NotifyBeginDllLoad(mLoadInfo, &mContext, aRequestedDllName);
+}
+
+ModuleLoadFrame::ModuleLoadFrame(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr, NTSTATUS aNtStatus,
+ ModuleLoadInfo::Status aLoadStatus)
+ : mPrev(sTopFrame.get()),
+ mContext(nullptr),
+ mLSPSubstitutionRequired(false),
+ mLoadNtStatus(aNtStatus),
+ mLoadInfo(std::move(aSectionName), aMapBaseAddr, aLoadStatus) {
+ sTopFrame.set(this);
+
+ gLoaderPrivateAPI.NotifyBeginDllLoad(&mContext, mLoadInfo.mSectionName);
+}
+
+ModuleLoadFrame::~ModuleLoadFrame() {
+ gLoaderPrivateAPI.NotifyEndDllLoad(mContext, mLoadNtStatus,
+ std::move(mLoadInfo));
+ sTopFrame.set(mPrev);
+}
+
+/* static */
+void ModuleLoadFrame::NotifyLSPSubstitutionRequired(
+ PCUNICODE_STRING aLeafName) {
+ ModuleLoadFrame* topFrame = sTopFrame.get();
+ if (!topFrame) {
+ return;
+ }
+
+ topFrame->SetLSPSubstitutionRequired(aLeafName);
+}
+
+void ModuleLoadFrame::SetLSPSubstitutionRequired(PCUNICODE_STRING aLeafName) {
+ MOZ_ASSERT(!mLoadInfo.mBaseAddr);
+ if (mLoadInfo.mBaseAddr) {
+ // If mBaseAddr is not null then |this| has already seen a module load. This
+ // should not be the case for a LSP substitution, so we bail.
+ return;
+ }
+
+ // Save aLeafName, as it will be used by SetLoadStatus when invoking
+ // SubstituteForLSP
+ mLoadInfo.mRequestedDllName = aLeafName;
+ mLSPSubstitutionRequired = true;
+}
+
+/* static */
+void ModuleLoadFrame::NotifySectionMap(
+ nt::AllocatedUnicodeString&& aSectionName, const void* aMapBaseAddr,
+ NTSTATUS aMapNtStatus, ModuleLoadInfo::Status aLoadStatus) {
+ ModuleLoadFrame* topFrame = sTopFrame.get();
+ if (!topFrame) {
+ // The only time that this data is useful is during initial mapping of
+ // the executable's dependent DLLs. If mozglue is present then
+ // IsDefaultObserver will return false, indicating that we are beyond
+ // initial process startup.
+ if (gLoaderPrivateAPI.IsDefaultObserver()) {
+ OnBareSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus,
+ aLoadStatus);
+ }
+ return;
+ }
+
+ topFrame->OnSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus,
+ aLoadStatus);
+}
+
+/* static */
+bool ModuleLoadFrame::ExistsTopFrame() { return !!sTopFrame.get(); }
+
+void ModuleLoadFrame::OnSectionMap(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr,
+ NTSTATUS aMapNtStatus,
+ ModuleLoadInfo::Status aLoadStatus) {
+ if (mLoadInfo.mBaseAddr) {
+ // If mBaseAddr is not null then |this| has already seen a module load. This
+ // means that we are witnessing a bare section map.
+ OnBareSectionMap(std::move(aSectionName), aMapBaseAddr, aMapNtStatus,
+ aLoadStatus);
+ return;
+ }
+
+ mLoadInfo.mSectionName = std::move(aSectionName);
+ mLoadInfo.mBaseAddr = aMapBaseAddr;
+ mLoadInfo.mStatus = aLoadStatus;
+}
+
+/* static */
+void ModuleLoadFrame::OnBareSectionMap(
+ nt::AllocatedUnicodeString&& aSectionName, const void* aMapBaseAddr,
+ NTSTATUS aMapNtStatus, ModuleLoadInfo::Status aLoadStatus) {
+ // We call the special constructor variant that is used for bare mappings.
+ ModuleLoadFrame frame(std::move(aSectionName), aMapBaseAddr, aMapNtStatus,
+ aLoadStatus);
+}
+
+NTSTATUS ModuleLoadFrame::SetLoadStatus(NTSTATUS aNtStatus,
+ PHANDLE aOutHandle) {
+ mLoadNtStatus = aNtStatus;
+
+ if (!mLSPSubstitutionRequired) {
+ return aNtStatus;
+ }
+
+ if (!gLoaderPrivateAPI.SubstituteForLSP(mLoadInfo.mRequestedDllName,
+ aOutHandle)) {
+ return aNtStatus;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+SafeThreadLocal<ModuleLoadFrame*> ModuleLoadFrame::sTopFrame;
+
+} // namespace freestanding
+} // namespace mozilla
diff --git a/browser/app/winlauncher/freestanding/ModuleLoadFrame.h b/browser/app/winlauncher/freestanding/ModuleLoadFrame.h
new file mode 100644
index 0000000000..a0dfc01b57
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/ModuleLoadFrame.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_ModuleLoadFrame_h
+#define mozilla_freestanding_ModuleLoadFrame_h
+
+#include "mozilla/LoaderAPIInterfaces.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/ThreadLocal.h"
+
+#include "SafeThreadLocal.h"
+
+namespace mozilla {
+namespace freestanding {
+
+/**
+ * This class holds information about a DLL load at a particular frame in the
+ * current thread's stack. Each instance adds itself to a thread-local linked
+ * list of ModuleLoadFrames, enabling us to query information about the
+ * previous module load on the stack.
+ */
+class MOZ_RAII ModuleLoadFrame final {
+ public:
+ /**
+ * This constructor is for use by the LdrLoadDll hook.
+ */
+ explicit ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName);
+ ~ModuleLoadFrame();
+
+ static void NotifyLSPSubstitutionRequired(PCUNICODE_STRING aLeafName);
+
+ /**
+ * This static method is called by the NtMapViewOfSection hook.
+ */
+ static void NotifySectionMap(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr, NTSTATUS aMapNtStatus,
+ ModuleLoadInfo::Status aLoadStatus);
+ static bool ExistsTopFrame();
+
+ /**
+ * Called by the LdrLoadDll hook to indicate the status of the load and for
+ * us to provide a substitute output handle if necessary.
+ */
+ NTSTATUS SetLoadStatus(NTSTATUS aNtStatus, PHANDLE aOutHandle);
+
+ ModuleLoadFrame(const ModuleLoadFrame&) = delete;
+ ModuleLoadFrame(ModuleLoadFrame&&) = delete;
+ ModuleLoadFrame& operator=(const ModuleLoadFrame&) = delete;
+ ModuleLoadFrame& operator=(ModuleLoadFrame&&) = delete;
+
+ private:
+ /**
+ * Called by OnBareSectionMap to construct a frame for a bare load.
+ */
+ ModuleLoadFrame(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr, NTSTATUS aNtStatus,
+ ModuleLoadInfo::Status aLoadStatus);
+
+ void SetLSPSubstitutionRequired(PCUNICODE_STRING aLeafName);
+ void OnSectionMap(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr, NTSTATUS aMapNtStatus,
+ ModuleLoadInfo::Status aLoadStatus);
+
+ /**
+ * A "bare" section mapping is one that was mapped without the code passing
+ * through a call to ntdll!LdrLoadDll. This method is invoked when we detect
+ * that condition.
+ */
+ static void OnBareSectionMap(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aMapBaseAddr, NTSTATUS aMapNtStatus,
+ ModuleLoadInfo::Status aLoadStatus);
+
+ private:
+ // Link to the previous frame
+ ModuleLoadFrame* mPrev;
+ // Pointer to context managed by the nt::LoaderObserver implementation
+ void* mContext;
+ // Set to |true| when we need to block a WinSock LSP
+ bool mLSPSubstitutionRequired;
+ // NTSTATUS code from the |LdrLoadDll| call
+ NTSTATUS mLoadNtStatus;
+ // Telemetry information that will be forwarded to the nt::LoaderObserver
+ ModuleLoadInfo mLoadInfo;
+
+ // Head of the linked list
+ static SafeThreadLocal<ModuleLoadFrame*> sTopFrame;
+};
+
+} // namespace freestanding
+} // namespace mozilla
+
+#endif // mozilla_freestanding_ModuleLoadFrame_h
diff --git a/browser/app/winlauncher/freestanding/SafeThreadLocal.h b/browser/app/winlauncher/freestanding/SafeThreadLocal.h
new file mode 100644
index 0000000000..e4b869f649
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/SafeThreadLocal.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_SafeThreadLocal_h
+#define mozilla_freestanding_SafeThreadLocal_h
+
+#include <type_traits>
+
+#include "mozilla/NativeNt.h"
+#include "mozilla/ThreadLocal.h"
+
+namespace mozilla {
+namespace freestanding {
+
+// We cannot fall back to the Tls* APIs because kernel32 might not have been
+// loaded yet.
+#if defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD)
+# error "This code requires the compiler to have native TLS support"
+#endif // defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD)
+
+/**
+ * This class holds data as a thread-local variable, or as a global variable
+ * if the thread local storage is not initialized yet. It should be safe
+ * because in that early stage we assume there is no more than a single thread.
+ */
+template <typename T>
+class SafeThreadLocal final {
+ static MOZ_THREAD_LOCAL(T) sThreadLocal;
+ static T sGlobal;
+ static bool sIsTlsUsed;
+
+ // In normal cases, TLS is always available and the class uses sThreadLocal
+ // without changing sMainThreadId. So sMainThreadId is likely to be 0.
+ //
+ // If TLS is not available, we use sGlobal instead and update sMainThreadId
+ // so that that thread keeps using sGlobal even after TLS is initialized
+ // later.
+ static DWORD sMainThreadId;
+
+ // Need non-inline accessors to prevent the compiler from generating code
+ // accessing sThreadLocal before checking a condition.
+ MOZ_NEVER_INLINE static void SetGlobalValue(T aValue) { sGlobal = aValue; }
+ MOZ_NEVER_INLINE static T GetGlobalValue() { return sGlobal; }
+
+ public:
+ static void set(T aValue) {
+ static_assert(std::is_pointer_v<T>,
+ "SafeThreadLocal must be used with a pointer");
+
+ if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) {
+ SetGlobalValue(aValue);
+ } else if (sIsTlsUsed) {
+ MOZ_ASSERT(mozilla::nt::RtlGetThreadLocalStoragePointer(),
+ "Once TLS is used, TLS should be available till the end.");
+ sThreadLocal.set(aValue);
+ } else if (mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ sIsTlsUsed = true;
+ sThreadLocal.set(aValue);
+ } else {
+ MOZ_ASSERT(sMainThreadId == 0,
+ "A second thread cannot be created before TLS is available.");
+ sMainThreadId = mozilla::nt::RtlGetCurrentThreadId();
+ SetGlobalValue(aValue);
+ }
+ }
+
+ static T get() {
+ if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) {
+ return GetGlobalValue();
+ } else if (sIsTlsUsed) {
+ return sThreadLocal.get();
+ }
+ return GetGlobalValue();
+ }
+};
+
+template <typename T>
+MOZ_THREAD_LOCAL(T)
+SafeThreadLocal<T>::sThreadLocal;
+
+template <typename T>
+T SafeThreadLocal<T>::sGlobal = nullptr;
+
+template <typename T>
+bool SafeThreadLocal<T>::sIsTlsUsed = false;
+
+template <typename T>
+DWORD SafeThreadLocal<T>::sMainThreadId = 0;
+
+} // namespace freestanding
+} // namespace mozilla
+
+#endif // mozilla_freestanding_SafeThreadLocal_h
diff --git a/browser/app/winlauncher/freestanding/SharedSection.cpp b/browser/app/winlauncher/freestanding/SharedSection.cpp
new file mode 100644
index 0000000000..66891ac80f
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/SharedSection.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "SharedSection.h"
+
+#include "CheckForCaller.h"
+
+namespace {
+
+bool AddString(void* aBuffer, size_t aBufferSize, const UNICODE_STRING& aStr) {
+ size_t offset = 0;
+ while (offset < aBufferSize) {
+ UNICODE_STRING uniStr;
+ ::RtlInitUnicodeString(&uniStr,
+ reinterpret_cast<wchar_t*>(
+ reinterpret_cast<uintptr_t>(aBuffer) + offset));
+
+ if (uniStr.Length == 0) {
+ // Reached to the array's last item.
+ break;
+ }
+
+ if (::RtlCompareUnicodeString(&uniStr, &aStr, TRUE) == 0) {
+ // Already included in the array.
+ return true;
+ }
+
+ // Go to the next string.
+ offset += uniStr.MaximumLength;
+ }
+
+ // Ensure enough space including the last empty string at the end.
+ if (offset + aStr.MaximumLength + sizeof(wchar_t) > aBufferSize) {
+ return false;
+ }
+
+ auto newStr = reinterpret_cast<uint8_t*>(aBuffer) + offset;
+ memcpy(newStr, aStr.Buffer, aStr.Length);
+ memset(newStr + aStr.Length, 0, sizeof(wchar_t));
+ return true;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace freestanding {
+
+SharedSection gSharedSection;
+
+bool Kernel32ExportsSolver::IsInitialized() const {
+ return mState == State::Initialized || IsResolved();
+}
+
+bool Kernel32ExportsSolver::IsResolved() const {
+ return mState == State::Resolved;
+}
+
+// Why don't we use ::GetProcAddress?
+// If the export table of kernel32.dll is tampered in the current process,
+// we cannot transfer an RVA because the function pointed by the RVA may not
+// exist in a target process.
+// We can use ::GetProcAddress with additional check to detect tampering, but
+// FindExportAddressTableEntry fits perfectly here because it returns nullptr
+// if the target entry is outside the image, which means it's tampered or
+// forwarded to another DLL.
+#define INIT_FUNCTION(exports, name) \
+ do { \
+ auto rvaToFunction = exports.FindExportAddressTableEntry(#name); \
+ if (!rvaToFunction) { \
+ return; \
+ } \
+ m##name = reinterpret_cast<decltype(m##name)>(*rvaToFunction); \
+ } while (0)
+
+#define RESOLVE_FUNCTION(base, name) \
+ m##name = reinterpret_cast<decltype(m##name)>( \
+ base + reinterpret_cast<uintptr_t>(m##name))
+
+void Kernel32ExportsSolver::Init() {
+ if (mState == State::Initialized || mState == State::Resolved) {
+ return;
+ }
+
+ interceptor::MMPolicyInProcess policy;
+ auto k32Exports = nt::PEExportSection<interceptor::MMPolicyInProcess>::Get(
+ ::GetModuleHandleW(L"kernel32.dll"), policy);
+ if (!k32Exports) {
+ return;
+ }
+
+ // Please make sure these functions are not forwarded to another DLL.
+ INIT_FUNCTION(k32Exports, FlushInstructionCache);
+ INIT_FUNCTION(k32Exports, GetModuleHandleW);
+ INIT_FUNCTION(k32Exports, GetSystemInfo);
+ INIT_FUNCTION(k32Exports, VirtualProtect);
+
+ mState = State::Initialized;
+}
+
+void Kernel32ExportsSolver::ResolveInternal() {
+ if (mState == State::Resolved) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mState == State::Initialized);
+
+ UNICODE_STRING k32Name;
+ ::RtlInitUnicodeString(&k32Name, L"kernel32.dll");
+
+ // We cannot use GetModuleHandleW because this code can be called
+ // before IAT is resolved.
+ auto k32Module = nt::GetModuleHandleFromLeafName(k32Name);
+ MOZ_RELEASE_ASSERT(k32Module.isOk());
+
+ uintptr_t k32Base =
+ nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap());
+
+ RESOLVE_FUNCTION(k32Base, FlushInstructionCache);
+ RESOLVE_FUNCTION(k32Base, GetModuleHandleW);
+ RESOLVE_FUNCTION(k32Base, GetSystemInfo);
+ RESOLVE_FUNCTION(k32Base, VirtualProtect);
+
+ mState = State::Resolved;
+}
+
+/* static */
+ULONG NTAPI Kernel32ExportsSolver::ResolveOnce(PRTL_RUN_ONCE aRunOnce,
+ PVOID aParameter, PVOID*) {
+ reinterpret_cast<Kernel32ExportsSolver*>(aParameter)->ResolveInternal();
+ return TRUE;
+}
+
+void Kernel32ExportsSolver::Resolve(RTL_RUN_ONCE& aRunOnce) {
+ ::RtlRunOnceExecuteOnce(&aRunOnce, &ResolveOnce, this, nullptr);
+}
+
+HANDLE SharedSection::sSectionHandle = nullptr;
+void* SharedSection::sWriteCopyView = nullptr;
+
+void SharedSection::Reset(HANDLE aNewSecionObject) {
+ if (sWriteCopyView) {
+ nt::AutoMappedView view(sWriteCopyView);
+ sWriteCopyView = nullptr;
+ }
+
+ if (sSectionHandle != aNewSecionObject) {
+ if (sSectionHandle) {
+ ::CloseHandle(sSectionHandle);
+ }
+ sSectionHandle = aNewSecionObject;
+ }
+}
+
+void SharedSection::ConvertToReadOnly() {
+ if (!sSectionHandle) {
+ return;
+ }
+
+ HANDLE readonlyHandle;
+ if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
+ nt::kCurrentProcess, &readonlyHandle, GENERIC_READ,
+ FALSE, 0)) {
+ return;
+ }
+
+ Reset(readonlyHandle);
+}
+
+LauncherVoidResult SharedSection::Init(const nt::PEHeaders& aPEHeaders) {
+ static_assert(
+ kSharedViewSize >= sizeof(Layout),
+ "kSharedViewSize is too small to represent SharedSection::Layout.");
+
+ HANDLE section =
+ ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
+ kSharedViewSize, nullptr);
+ if (!section) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+ Reset(section);
+
+ // The initial contents of the pages in a file mapping object backed by
+ // the operating system paging file are 0 (zero). No need to zero it out
+ // ourselves.
+ // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw
+ nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
+ if (!writableView) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ SharedSection::Layout* view = writableView.as<SharedSection::Layout>();
+ view->mK32Exports.Init();
+
+ return Ok();
+}
+
+LauncherVoidResult SharedSection::AddDepenentModule(PCUNICODE_STRING aNtPath) {
+ nt::AutoMappedView writableView(sSectionHandle, PAGE_READWRITE);
+ if (!writableView) {
+ return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
+ }
+
+ SharedSection::Layout* view = writableView.as<SharedSection::Layout>();
+ if (!AddString(view->mModulePathArray,
+ kSharedViewSize - sizeof(Kernel32ExportsSolver), *aNtPath)) {
+ return LAUNCHER_ERROR_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ return Ok();
+}
+
+LauncherResult<SharedSection::Layout*> SharedSection::GetView() {
+ if (!sWriteCopyView) {
+ nt::AutoMappedView view(sSectionHandle, PAGE_WRITECOPY);
+ if (!view) {
+ return LAUNCHER_ERROR_FROM_WIN32(::RtlGetLastWin32Error());
+ }
+ sWriteCopyView = view.release();
+ }
+ return reinterpret_cast<SharedSection::Layout*>(sWriteCopyView);
+}
+
+LauncherVoidResult SharedSection::TransferHandle(
+ nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
+ HANDLE* aDestinationAddress) {
+ HANDLE remoteHandle;
+ if (!::DuplicateHandle(nt::kCurrentProcess, sSectionHandle,
+ aTransferMgr.RemoteProcess(), &remoteHandle,
+ aDesiredAccess, FALSE, 0)) {
+ return LAUNCHER_ERROR_FROM_LAST();
+ }
+
+ return aTransferMgr.Transfer(aDestinationAddress, &remoteHandle,
+ sizeof(remoteHandle));
+}
+
+// This exported function is invoked by SandboxBroker of xul.dll
+// in order to add dependent modules to the CIG exception list.
+extern "C" MOZ_EXPORT const wchar_t* GetDependentModulePaths() {
+ // We enable pre-spawn CIG only in Nightly for now because it caused
+ // a compat issue (bug 1682304).
+#if defined(NIGHTLY_BUILD)
+ const bool isCallerXul = CheckForAddress(RETURN_ADDRESS(), L"xul.dll");
+ MOZ_ASSERT(isCallerXul);
+ if (!isCallerXul) {
+ return nullptr;
+ }
+
+ // Remap a write-copy section to take the latest update in mModulePathArray.
+ gSharedSection.Reset();
+
+ LauncherResult<SharedSection::Layout*> resultView = gSharedSection.GetView();
+ if (resultView.isErr()) {
+ return nullptr;
+ }
+
+ return resultView.inspect()->mModulePathArray;
+#else
+ return nullptr;
+#endif
+}
+
+} // namespace freestanding
+} // namespace mozilla
diff --git a/browser/app/winlauncher/freestanding/SharedSection.h b/browser/app/winlauncher/freestanding/SharedSection.h
new file mode 100644
index 0000000000..0724cfc05d
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/SharedSection.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_freestanding_SharedSection_h
+#define mozilla_freestanding_SharedSection_h
+
+#include "mozilla/NativeNt.h"
+#include "mozilla/interceptor/MMPolicies.h"
+
+namespace mozilla {
+namespace freestanding {
+
+// This class calculates RVAs of kernel32's functions and transfers them
+// to a target process, where the transferred RVAs are resolved into
+// function addresses so that the target process can use them after
+// kernel32.dll is loaded and before IAT is resolved.
+class MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final
+ : public interceptor::MMPolicyInProcessEarlyStage::Kernel32Exports {
+ enum class State {
+ Uninitialized,
+ Initialized,
+ Resolved,
+ } mState;
+
+ static ULONG NTAPI ResolveOnce(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
+ PVOID*);
+ void ResolveInternal();
+
+ public:
+ Kernel32ExportsSolver() = default;
+
+ Kernel32ExportsSolver(const Kernel32ExportsSolver&) = delete;
+ Kernel32ExportsSolver(Kernel32ExportsSolver&&) = delete;
+ Kernel32ExportsSolver& operator=(const Kernel32ExportsSolver&) = delete;
+ Kernel32ExportsSolver& operator=(Kernel32ExportsSolver&&) = delete;
+
+ bool IsInitialized() const;
+ bool IsResolved() const;
+
+ void Init();
+ void Resolve(RTL_RUN_ONCE& aRunOnce);
+};
+
+// This class manages a section which is created in the launcher process and
+// mapped in the browser process and the sandboxed processes. The section's
+// layout is represented as SharedSection::Layout.
+//
+// (1) Kernel32's functions required for MMPolicyInProcessEarlyStage
+// Formatted as Kernel32ExportsSolver.
+//
+// (2) Array of NT paths of the executable's dependent modules
+// Formatted as a null-delimited wide-character string set ending with
+// an empty string.
+//
+// +--------------------------------------------------------------+
+// | (1) | FlushInstructionCache |
+// | | GetModuleHandleW |
+// | | GetSystemInfo |
+// | | VirtualProtect |
+// | | State [Uninitialized|Initialized|Resolved] |
+// +--------------------------------------------------------------+
+// | (2) | L"NT path 1" |
+// | | L"NT path 2" |
+// | | ... |
+// | | L"" |
+// +--------------------------------------------------------------+
+class MOZ_TRIVIAL_CTOR_DTOR SharedSection final {
+ // As we define a global variable of this class and use it in our blocklist
+ // which is excuted in a process's early stage. If we have a complex dtor,
+ // the static initializer tries to register that dtor with onexit() of
+ // ucrtbase.dll which is not loaded yet, resulting in crash. Thus, we have
+ // a raw handle and a pointer as a static variable and manually release them
+ // by calling Reset() where possible.
+ static HANDLE sSectionHandle;
+ static void* sWriteCopyView;
+
+ static constexpr size_t kSharedViewSize = 0x1000;
+
+ public:
+ struct Layout final {
+ Kernel32ExportsSolver mK32Exports;
+ wchar_t mModulePathArray[1];
+
+ Layout() = delete; // disallow instantiation
+ };
+
+ // Replace |sSectionHandle| with a given handle.
+ static void Reset(HANDLE aNewSecionObject = sSectionHandle);
+
+ // Replace |sSectionHandle| with a new readonly handle.
+ static void ConvertToReadOnly();
+
+ // Create a new writable section and initialize the Kernel32ExportsSolver
+ // part.
+ static LauncherVoidResult Init(const nt::PEHeaders& aPEHeaders);
+
+ // Append a new string to the |sSectionHandle|
+ static LauncherVoidResult AddDepenentModule(PCUNICODE_STRING aNtPath);
+
+ // Map |sSectionHandle| to a copy-on-write page and return its address.
+ static LauncherResult<Layout*> GetView();
+
+ // Transfer |sSectionHandle| to a process associated with |aTransferMgr|.
+ static LauncherVoidResult TransferHandle(
+ nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
+ HANDLE* aDestinationAddress = &sSectionHandle);
+};
+
+extern SharedSection gSharedSection;
+extern RTL_RUN_ONCE gK32ExportsResolveOnce;
+
+} // namespace freestanding
+} // namespace mozilla
+
+#endif // mozilla_freestanding_SharedSection_h
diff --git a/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py b/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py
new file mode 100644
index 0000000000..a69261cc3a
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/gen_ntdll_freestanding_lib.py
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+from __future__ import absolute_import
+
+import os
+import subprocess
+import tempfile
+
+
+def main(output_fd, def_file, llvm_dlltool, *llvm_dlltool_args):
+ # llvm-dlltool can't output to stdout, so we create a temp file, use that
+ # to write out the lib, and then copy it over to output_fd
+ (tmp_fd, tmp_output) = tempfile.mkstemp()
+ os.close(tmp_fd)
+
+ try:
+ cmd = [llvm_dlltool]
+ cmd.extend(llvm_dlltool_args)
+ cmd += ["-d", def_file, "-l", tmp_output]
+
+ subprocess.check_call(cmd)
+
+ with open(tmp_output, "rb") as tmplib:
+ output_fd.write(tmplib.read())
+ finally:
+ os.remove(tmp_output)
diff --git a/browser/app/winlauncher/freestanding/moz.build b/browser/app/winlauncher/freestanding/moz.build
new file mode 100644
index 0000000000..7a43d9a104
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/moz.build
@@ -0,0 +1,56 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Library("winlauncher-freestanding")
+
+FORCE_STATIC_LIB = True
+
+# Our patched NtMapViewOfSection can be called before the process's import
+# table is populated. Don't let the compiler insert any instrumentation
+# that might call an import.
+NO_PGO = True
+
+UNIFIED_SOURCES += [
+ "DllBlocklist.cpp",
+ "LoaderPrivateAPI.cpp",
+ "ModuleLoadFrame.cpp",
+ "SharedSection.cpp",
+]
+
+# This library must be compiled in a freestanding environment, as its code must
+# not assume that it has access to any runtime libraries.
+if CONFIG["CC_TYPE"] == "clang-cl":
+ CXXFLAGS += ["-Xclang"]
+
+CXXFLAGS += [
+ "-ffreestanding",
+]
+
+# Forcibly include Freestanding.h into all source files in this library.
+if CONFIG["CC_TYPE"] == "clang-cl":
+ CXXFLAGS += ["-FI"]
+else:
+ CXXFLAGS += ["-include"]
+
+CXXFLAGS += [SRCDIR + "/Freestanding.h"]
+
+OS_LIBS += [
+ "ntdll",
+ "ntdll_freestanding",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"] and CONFIG["LLVM_DLLTOOL"]:
+ GeneratedFile(
+ "%sntdll_freestanding.%s" % (CONFIG["LIB_PREFIX"], CONFIG["LIB_SUFFIX"]),
+ script="gen_ntdll_freestanding_lib.py",
+ inputs=["ntdll_freestanding.def"],
+ flags=[CONFIG["LLVM_DLLTOOL"]] + CONFIG["LLVM_DLLTOOL_FLAGS"],
+ )
+
+DisableStlWrapping()
+
+with Files("**"):
+ BUG_COMPONENT = ("Firefox", "Launcher Process")
diff --git a/browser/app/winlauncher/freestanding/ntdll_freestanding.def b/browser/app/winlauncher/freestanding/ntdll_freestanding.def
new file mode 100644
index 0000000000..6e5e2685fe
--- /dev/null
+++ b/browser/app/winlauncher/freestanding/ntdll_freestanding.def
@@ -0,0 +1,25 @@
+; 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/.
+
+LIBRARY ntdll
+
+; When we compile with -freestanding, the compiler still requires implementation
+; of the four functions listed below.
+;
+; We could implement our own naive versions of these functions, but that
+; solution is less than ideal since the implementations must be extern and are
+; thus picked up by the entire firefox.exe binary. This denies the rest of
+; firefox.exe the benefit of optimized implementations. On Windows the
+; sandbox is linked into firefox.exe, so we cannot just shrug and
+; assume that a naive implementation will not have any effect on anything.
+;
+; There are, however, optimized implementations of these functions that are
+; exported by ntdll.dll. OTOH, they are not included in the ntdll.lib
+; import library. This .def file is used to build an import library that "fills
+; in the blanks" and allows us to link into the ntdll implementations.
+EXPORTS
+ memcmp
+ memcpy
+ memmove
+ memset
diff --git a/browser/app/winlauncher/moz.build b/browser/app/winlauncher/moz.build
new file mode 100644
index 0000000000..585e883547
--- /dev/null
+++ b/browser/app/winlauncher/moz.build
@@ -0,0 +1,51 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Library("winlauncher")
+
+FORCE_STATIC_LIB = True
+
+UNIFIED_SOURCES += [
+ "/ipc/mscom/ProcessRuntime.cpp",
+ "/widget/windows/WindowsConsole.cpp",
+ "DllBlocklistInit.cpp",
+ "ErrorHandler.cpp",
+ "LauncherProcessWin.cpp",
+ "LaunchUnelevated.cpp",
+ "NtLoaderAPI.cpp",
+]
+
+OS_LIBS += [
+ "oleaut32",
+ "ole32",
+ "rpcrt4",
+ "version",
+]
+
+DIRS += [
+ "freestanding",
+]
+
+USE_LIBS += [
+ "winlauncher-freestanding",
+]
+
+TEST_DIRS += [
+ "test",
+]
+
+if CONFIG["MOZ_LAUNCHER_PROCESS"]:
+ UNIFIED_SOURCES += [
+ "/toolkit/xre/LauncherRegistryInfo.cpp",
+ "/toolkit/xre/WinTokenUtils.cpp",
+ ]
+ for var in ("MOZ_APP_BASENAME", "MOZ_APP_VENDOR"):
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+DisableStlWrapping()
+
+with Files("**"):
+ BUG_COMPONENT = ("Firefox", "Launcher Process")
diff --git a/browser/app/winlauncher/test/TestCrossProcessWin.cpp b/browser/app/winlauncher/test/TestCrossProcessWin.cpp
new file mode 100644
index 0000000000..0c5c57aaaa
--- /dev/null
+++ b/browser/app/winlauncher/test/TestCrossProcessWin.cpp
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "freestanding/SharedSection.cpp"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/NativeNt.h"
+
+const wchar_t kChildArg[] = L"--child";
+const char kTestStrings[][6] = {
+ "a b c", "A B C", "a b c", "A B C", "X Y Z",
+};
+const wchar_t kTestStringsMerged[] =
+ L"a b c"
+ L"\0"
+ L"X Y Z"
+ L"\0";
+
+using namespace mozilla;
+using namespace mozilla::freestanding;
+
+template <typename T, int N>
+void PrintLauncherError(const LauncherResult<T>& aResult,
+ const char (&aMsg)[N]) {
+ const LauncherError& err = aResult.inspectErr();
+ printf("TEST-FAILED | TestCrossProcessWin | %s - %lx at %s:%d\n", aMsg,
+ err.mError.AsHResult(), err.mFile, err.mLine);
+}
+
+#define VERIFY_FUNCTION_RESOLVED(mod, exports, name) \
+ do { \
+ if (reinterpret_cast<FARPROC>(exports.m##name) != \
+ ::GetProcAddress(mod, #name)) { \
+ printf( \
+ "TEST-FAILED | TestCrossProcessWin | " \
+ "Kernel32ExportsSolver::" #name " did not match.\n"); \
+ return false; \
+ } \
+ } while (0)
+
+static bool VerifySharedSection(SharedSection& aSharedSection) {
+ LauncherResult<SharedSection::Layout*> resultView = aSharedSection.GetView();
+ if (resultView.isErr()) {
+ PrintLauncherError(resultView, "Failed to map a shared section");
+ return false;
+ }
+
+ SharedSection::Layout* view = resultView.unwrap();
+
+ // Use a local variable of RTL_RUN_ONCE to resolve Kernel32Exports every time
+ RTL_RUN_ONCE sRunEveryTime = RTL_RUN_ONCE_INIT;
+ view->mK32Exports.Resolve(sRunEveryTime);
+
+ HMODULE k32mod = ::GetModuleHandleW(L"kernel32.dll");
+ VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, FlushInstructionCache);
+ VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetModuleHandleW);
+ VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetSystemInfo);
+ VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, VirtualProtect);
+
+ bool matched = memcmp(view->mModulePathArray, kTestStringsMerged,
+ sizeof(kTestStringsMerged)) == 0;
+ if (!matched) {
+ // Print actual strings on error
+ for (const wchar_t* p = view->mModulePathArray; *p;) {
+ printf("%p: %ls\n", p, p);
+ while (*p) {
+ ++p;
+ }
+ ++p;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static bool TestAddString() {
+ wchar_t testBuffer[3] = {0};
+ UNICODE_STRING ustr;
+
+ // This makes |testBuffer| full.
+ ::RtlInitUnicodeString(&ustr, L"a");
+ if (!AddString(testBuffer, sizeof(testBuffer), ustr)) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "AddString failed.\n");
+ return false;
+ }
+
+ // Adding a string to a full buffer should fail.
+ ::RtlInitUnicodeString(&ustr, L"b");
+ if (AddString(testBuffer, sizeof(testBuffer), ustr)) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "AddString caused OOB memory access.\n");
+ return false;
+ }
+
+ bool matched = memcmp(testBuffer, L"a\0", sizeof(testBuffer)) == 0;
+ if (!matched) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "AddString wrote wrong values.\n");
+ return false;
+ }
+
+ return true;
+}
+
+class ChildProcess final {
+ nsAutoHandle mChildProcess;
+ nsAutoHandle mChildMainThread;
+ DWORD mProcessId;
+
+ public:
+ // The following variables are updated from the parent process via
+ // WriteProcessMemory while the process is suspended as a part of
+ // TestWithChildProcess().
+ //
+ // Having both a non-const and a const is important because a constant
+ // is separately placed in the .rdata section which is read-only, so
+ // the region's attribute needs to be changed before modifying data via
+ // WriteProcessMemory.
+ // The keyword "volatile" is needed for a constant, otherwise the compiler
+ // evaluates a constant as a literal without fetching data from memory.
+ static HMODULE sExecutableImageBase;
+ static volatile const DWORD sReadOnlyProcessId;
+
+ static int Main() {
+ if (sExecutableImageBase != ::GetModuleHandle(nullptr)) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "sExecutableImageBase is expected to be %p, but actually was %p.\n",
+ ::GetModuleHandle(nullptr), sExecutableImageBase);
+ return 1;
+ }
+
+ if (sReadOnlyProcessId != ::GetCurrentProcessId()) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "sReadOnlyProcessId is expected to be %lx, but actually was %lx.\n",
+ ::GetCurrentProcessId(), sReadOnlyProcessId);
+ return 1;
+ }
+
+ auto getDependentModulePaths =
+ reinterpret_cast<const wchar_t (*)()>(::GetProcAddress(
+ ::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
+ if (!getDependentModulePaths) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "Failed to get a pointer to GetDependentModulePaths - %08lx.\n",
+ ::GetLastError());
+ return 1;
+ }
+
+#if !defined(DEBUG)
+ // GetDependentModulePaths does not allow a caller other than xul.dll.
+ // Skip on Debug build because it hits MOZ_ASSERT.
+ if (getDependentModulePaths()) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "GetDependentModulePaths should return zero if the caller is "
+ "not xul.dll.\n");
+ return 1;
+ }
+#endif // !defined(DEBUG)
+
+ if (!VerifySharedSection(gSharedSection)) {
+ return 1;
+ }
+
+ // Test a scenario to transfer a transferred section as a readonly handle
+ static HANDLE copiedHandle = nullptr;
+ nt::CrossExecTransferManager tansferToSelf(::GetCurrentProcess());
+ LauncherVoidResult result = gSharedSection.TransferHandle(
+ tansferToSelf, GENERIC_READ, &copiedHandle);
+ if (result.isErr()) {
+ PrintLauncherError(result, "SharedSection::TransferHandle(self) failed");
+ return 1;
+ }
+
+ gSharedSection.Reset(copiedHandle);
+
+ UNICODE_STRING ustr;
+ ::RtlInitUnicodeString(&ustr, L"test");
+ result = gSharedSection.AddDepenentModule(&ustr);
+ if (result.inspectErr() !=
+ WindowsError::FromWin32Error(ERROR_ACCESS_DENIED)) {
+ PrintLauncherError(result, "The readonly section was writable");
+ return 1;
+ }
+
+ if (!VerifySharedSection(gSharedSection)) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption)
+ : mProcessId(0) {
+ const wchar_t* childArgv[] = {aExecutable, aOption};
+ auto cmdLine(
+ mozilla::MakeCommandLine(mozilla::ArrayLength(childArgv), childArgv));
+
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi;
+ BOOL ok =
+ ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, FALSE,
+ CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
+ if (!ok) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "CreateProcessW falied - %08lx.\n",
+ GetLastError());
+ return;
+ }
+
+ mProcessId = pi.dwProcessId;
+
+ mChildProcess.own(pi.hProcess);
+ mChildMainThread.own(pi.hThread);
+ }
+
+ ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); }
+
+ operator HANDLE() const { return mChildProcess; }
+ DWORD GetProcessId() const { return mProcessId; }
+
+ bool ResumeAndWaitUntilExit() {
+ if (::ResumeThread(mChildMainThread) == 0xffffffff) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "ResumeThread failed - %08lx\n",
+ GetLastError());
+ return false;
+ }
+
+ if (::WaitForSingleObject(mChildProcess, 60000) != WAIT_OBJECT_0) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "Unexpected result from WaitForSingleObject\n");
+ return false;
+ }
+
+ DWORD exitCode;
+ if (!::GetExitCodeProcess(mChildProcess, &exitCode)) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "GetExitCodeProcess failed - %08lx\n",
+ GetLastError());
+ return false;
+ }
+
+ return exitCode == 0;
+ }
+};
+
+HMODULE ChildProcess::sExecutableImageBase = 0;
+volatile const DWORD ChildProcess::sReadOnlyProcessId = 0;
+
+int wmain(int argc, wchar_t* argv[]) {
+ printf("Process: %-8lx Base: %p\n", ::GetCurrentProcessId(),
+ ::GetModuleHandle(nullptr));
+
+ if (argc == 2 && wcscmp(argv[1], kChildArg) == 0) {
+ return ChildProcess::Main();
+ }
+
+ ChildProcess childProcess(argv[0], kChildArg);
+ if (!childProcess) {
+ return 1;
+ }
+
+ if (!TestAddString()) {
+ return 1;
+ }
+
+ LauncherResult<HMODULE> remoteImageBase =
+ nt::GetProcessExeModule(childProcess);
+ if (remoteImageBase.isErr()) {
+ PrintLauncherError(remoteImageBase, "nt::GetProcessExeModule failed");
+ return 1;
+ }
+
+ nt::CrossExecTransferManager transferMgr(childProcess);
+ if (!transferMgr) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "CrossExecTransferManager instantiation failed.\n");
+ return 1;
+ }
+
+ LauncherVoidResult result =
+ transferMgr.Transfer(&ChildProcess::sExecutableImageBase,
+ &remoteImageBase.inspect(), sizeof(HMODULE));
+ if (result.isErr()) {
+ PrintLauncherError(result, "ChildProcess::WriteData(Imagebase) failed");
+ return 1;
+ }
+
+ DWORD childPid = childProcess.GetProcessId();
+
+ DWORD* readOnlyData = const_cast<DWORD*>(&ChildProcess::sReadOnlyProcessId);
+ result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
+ if (result.isOk()) {
+ printf(
+ "TEST-UNEXPECTED | TestCrossProcessWin | "
+ "A constant was located in a writable section.");
+ return 1;
+ }
+
+ AutoVirtualProtect prot =
+ transferMgr.Protect(readOnlyData, sizeof(uint32_t), PAGE_READWRITE);
+ if (!prot) {
+ printf(
+ "TEST-FAILED | TestCrossProcessWin | "
+ "VirtualProtect failed - %08lx\n",
+ prot.GetError().AsHResult());
+ return 1;
+ }
+
+ result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
+ if (result.isErr()) {
+ PrintLauncherError(result, "ChildProcess::WriteData(PID) failed");
+ return 1;
+ }
+
+ {
+ // Define a scope for |sharedSection| to resume the child process after
+ // the section is deleted in the parent process.
+ result = gSharedSection.Init(transferMgr.LocalPEHeaders());
+ if (result.isErr()) {
+ PrintLauncherError(result, "SharedSection::Init failed");
+ return 1;
+ }
+
+ for (const auto& testString : kTestStrings) {
+ nt::AllocatedUnicodeString ustr(testString);
+ result = gSharedSection.AddDepenentModule(ustr);
+ if (result.isErr()) {
+ PrintLauncherError(result, "SharedSection::AddDepenentModule failed");
+ return 1;
+ }
+ }
+
+ result = gSharedSection.TransferHandle(transferMgr,
+ GENERIC_READ | GENERIC_WRITE);
+ if (result.isErr()) {
+ PrintLauncherError(result, "SharedSection::TransferHandle failed");
+ return 1;
+ }
+ }
+
+ if (!childProcess.ResumeAndWaitUntilExit()) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/browser/app/winlauncher/test/TestSafeThreadLocal.cpp b/browser/app/winlauncher/test/TestSafeThreadLocal.cpp
new file mode 100644
index 0000000000..5aa122e16e
--- /dev/null
+++ b/browser/app/winlauncher/test/TestSafeThreadLocal.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "freestanding/SafeThreadLocal.h"
+
+#include "mozilla/NativeNt.h"
+#include "nsWindowsHelpers.h"
+
+#include <process.h>
+#include <stdio.h>
+
+// Need a non-inline function to bypass compiler optimization that the thread
+// local storage pointer is cached in a register before accessing a thread-local
+// variable.
+MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) {
+ auto oldValue = mozilla::nt::RtlGetThreadLocalStoragePointer();
+ mozilla::nt::RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue);
+ return oldValue;
+}
+
+static mozilla::freestanding::SafeThreadLocal<int*> gTheStorage;
+
+static unsigned int __stdcall TestNonMainThread(void* aArg) {
+ for (int i = 0; i < 100; ++i) {
+ gTheStorage.set(&i);
+ if (gTheStorage.get() != &i) {
+ printf(
+ "TEST-FAILED | TestSafeThreadLocal | "
+ "A value is not correctly stored in the thread-local storage.\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+extern "C" int wmain(int argc, wchar_t* argv[]) {
+ int dummy = 0x1234;
+
+ auto origHead = SwapThreadLocalStoragePointer(nullptr);
+ // Setting gTheStorage when TLS is null.
+ gTheStorage.set(&dummy);
+ SwapThreadLocalStoragePointer(origHead);
+
+ nsAutoHandle handles[8];
+ for (auto& handle : handles) {
+ handle.own(reinterpret_cast<HANDLE>(
+ ::_beginthreadex(nullptr, 0, TestNonMainThread, nullptr, 0, nullptr)));
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ if (gTheStorage.get() != &dummy) {
+ printf(
+ "TEST-FAILED | TestSafeThreadLocal | "
+ "A value is not correctly stored in the global scope.\n");
+ return 1;
+ }
+ }
+
+ for (auto& handle : handles) {
+ ::WaitForSingleObject(handle, INFINITE);
+
+ DWORD exitCode;
+ if (!::GetExitCodeThread(handle, &exitCode) || exitCode) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/browser/app/winlauncher/test/TestSameBinary.cpp b/browser/app/winlauncher/test/TestSameBinary.cpp
new file mode 100644
index 0000000000..2cb45f546f
--- /dev/null
+++ b/browser/app/winlauncher/test/TestSameBinary.cpp
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <utility>
+
+#include "SameBinary.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Vector.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "nsWindowsHelpers.h"
+
+#define EXPECT_SAMEBINARY_IS(expected, option, message) \
+ do { \
+ mozilla::LauncherResult<bool> isSame = \
+ mozilla::IsSameBinaryAsParentProcess(option); \
+ if (isSame.isErr()) { \
+ PrintLauncherError(isSame, \
+ "IsSameBinaryAsParentProcess returned error " \
+ "when we were expecting success."); \
+ return 1; \
+ } \
+ if (isSame.unwrap() != expected) { \
+ PrintErrorMsg(message); \
+ return 1; \
+ } \
+ } while (0)
+
+/**
+ * This test involves three processes:
+ * 1. The "Monitor" process, which is executed by |MonitorMain|. This process
+ * is responsible for integrating with the test harness, so it spawns the
+ * "Parent" process (2), and then waits for the other two processes to
+ * finish.
+ * 2. The "Parent" process, which is executed by |ParentMain|. This process
+ * creates the "Child" process (3) and then waits indefinitely.
+ * 3. The "Child" process, which is executed by |ChildMain| and carries out
+ * the actual test. It terminates the Parent process during its execution,
+ * using the Child PID as the Parent process's exit code. This serves as a
+ * hacky yet effective way to signal to the Monitor process which PID it
+ * should wait on to ensure that the Child process has exited.
+ */
+
+static const char kMsgStart[] = "TEST-FAILED | SameBinary | ";
+
+inline void PrintErrorMsg(const char* aMsg) {
+ printf("%s%s\n", kMsgStart, aMsg);
+}
+
+inline void PrintWinError(const char* aMsg) {
+ mozilla::WindowsError err(mozilla::WindowsError::FromLastError());
+ printf("%s%s: %S\n", kMsgStart, aMsg, err.AsString().get());
+}
+
+template <typename T>
+inline void PrintLauncherError(const mozilla::LauncherResult<T>& aResult,
+ const char* aMsg = nullptr) {
+ const char* const kSep = aMsg ? ": " : "";
+ const char* msg = aMsg ? aMsg : "";
+ const mozilla::LauncherError& err = aResult.inspectErr();
+ printf("%s%s%s%S (%s:%d)\n", kMsgStart, msg, kSep,
+ err.mError.AsString().get(), err.mFile, err.mLine);
+}
+
+static int ChildMain(DWORD aExpectedParentPid) {
+ mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId();
+ if (parentPid.isErr()) {
+ PrintLauncherError(parentPid);
+ return 1;
+ }
+
+ if (parentPid.inspect() != aExpectedParentPid) {
+ PrintErrorMsg("Unexpected mismatch of parent PIDs");
+ return 1;
+ }
+
+ const DWORD kAccess = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE;
+ nsAutoHandle parentProcess(
+ ::OpenProcess(kAccess, FALSE, parentPid.inspect()));
+ if (!parentProcess) {
+ PrintWinError("Unexpectedly failed to call OpenProcess on parent");
+ return 1;
+ }
+
+ EXPECT_SAMEBINARY_IS(
+ true, mozilla::ImageFileCompareOption::Default,
+ "IsSameBinaryAsParentProcess returned incorrect result for identical "
+ "binaries");
+ EXPECT_SAMEBINARY_IS(
+ true, mozilla::ImageFileCompareOption::CompareNtPathsOnly,
+ "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect "
+ "result for identical binaries");
+
+ // Total hack, but who cares? We'll set the parent's exit code as our PID
+ // so that the monitor process knows who to wait for!
+ if (!::TerminateProcess(parentProcess.get(), ::GetCurrentProcessId())) {
+ PrintWinError("Unexpected failure in TerminateProcess");
+ return 1;
+ }
+
+ // Close our handle to the parent process so that no references are held.
+ ::CloseHandle(parentProcess.disown());
+
+ // Querying a pid on a terminated process may still succeed some time after
+ // that process has been terminated. For the purposes of this test, we'll poll
+ // the OS until we cannot succesfully open the parentPid anymore.
+ const uint32_t kMaxAttempts = 100;
+ uint32_t curAttempt = 0;
+ while (HANDLE p = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
+ parentPid.inspect())) {
+ ::CloseHandle(p);
+ ::Sleep(100);
+ ++curAttempt;
+ if (curAttempt >= kMaxAttempts) {
+ PrintErrorMsg(
+ "Exhausted retry attempts waiting for parent pid to become invalid");
+ return 1;
+ }
+ }
+
+ EXPECT_SAMEBINARY_IS(
+ false, mozilla::ImageFileCompareOption::Default,
+ "IsSameBinaryAsParentProcess returned incorrect result for dead parent "
+ "process");
+ EXPECT_SAMEBINARY_IS(
+ false, mozilla::ImageFileCompareOption::CompareNtPathsOnly,
+ "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect "
+ "result for dead parent process");
+
+ return 0;
+}
+
+static nsReturnRef<HANDLE> CreateSelfProcess(int argc, wchar_t* argv[]) {
+ nsAutoHandle empty;
+
+ DWORD myPid = ::GetCurrentProcessId();
+
+ wchar_t strPid[11] = {};
+#if defined(__MINGW32__)
+ _ultow(myPid, strPid, 16);
+#else
+ if (_ultow_s(myPid, strPid, 16)) {
+ PrintErrorMsg("_ultow_s failed");
+ return empty.out();
+ }
+#endif // defined(__MINGW32__)
+
+ wchar_t* extraArgs[] = {strPid};
+
+ auto cmdLine = mozilla::MakeCommandLine(
+ argc, argv, mozilla::ArrayLength(extraArgs), extraArgs);
+ if (!cmdLine) {
+ PrintErrorMsg("MakeCommandLine failed");
+ return empty.out();
+ }
+
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi;
+ BOOL ok =
+ ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, FALSE,
+ CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &si, &pi);
+ if (!ok) {
+ PrintWinError("CreateProcess failed");
+ return empty.out();
+ }
+
+ nsAutoHandle proc(pi.hProcess);
+ nsAutoHandle thd(pi.hThread);
+
+ return proc.out();
+}
+
+static int ParentMain(int argc, wchar_t* argv[]) {
+ nsAutoHandle childProc(CreateSelfProcess(argc, argv));
+ if (!childProc) {
+ return 1;
+ }
+
+ if (::WaitForSingleObject(childProc.get(), INFINITE) != WAIT_OBJECT_0) {
+ PrintWinError(
+ "Unexpected result from WaitForSingleObject on child process");
+ return 1;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("This process should be terminated by now");
+ return 0;
+}
+
+static int MonitorMain(int argc, wchar_t* argv[]) {
+ // In this process, "parent" means the process that will be running
+ // ParentMain, which is our child process (confusing, I know...)
+ nsAutoHandle parentProc(CreateSelfProcess(argc, argv));
+ if (!parentProc) {
+ return 1;
+ }
+
+ if (::WaitForSingleObject(parentProc.get(), 60000) != WAIT_OBJECT_0) {
+ PrintWinError("Unexpected result from WaitForSingleObject on parent");
+ return 1;
+ }
+
+ DWORD childPid;
+ if (!::GetExitCodeProcess(parentProc.get(), &childPid)) {
+ PrintWinError("GetExitCodeProcess failed");
+ return 1;
+ }
+
+ nsAutoHandle childProc(::OpenProcess(SYNCHRONIZE, FALSE, childPid));
+ if (!childProc) {
+ // Nothing to wait on anymore, which is OK.
+ return 0;
+ }
+
+ // We want no more references to parentProc
+ ::CloseHandle(parentProc.disown());
+
+ if (::WaitForSingleObject(childProc.get(), 60000) != WAIT_OBJECT_0) {
+ PrintWinError("Unexpected result from WaitForSingleObject on child");
+ return 1;
+ }
+
+ return 0;
+}
+
+extern "C" int wmain(int argc, wchar_t* argv[]) {
+ if (argc == 3) {
+ return ChildMain(wcstoul(argv[2], nullptr, 16));
+ }
+
+ if (!mozilla::SetArgv0ToFullBinaryPath(argv)) {
+ return 1;
+ }
+
+ if (argc == 1) {
+ return MonitorMain(argc, argv);
+ }
+
+ if (argc == 2) {
+ return ParentMain(argc, argv);
+ }
+
+ PrintErrorMsg("Unexpected argc");
+ return 1;
+}
diff --git a/browser/app/winlauncher/test/moz.build b/browser/app/winlauncher/test/moz.build
new file mode 100644
index 0000000000..a8503ddf55
--- /dev/null
+++ b/browser/app/winlauncher/test/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DisableStlWrapping()
+
+GeckoCppUnitTests(
+ [
+ "TestCrossProcessWin",
+ "TestSafeThreadLocal",
+ "TestSameBinary",
+ ],
+ linkage=None,
+)
+
+LOCAL_INCLUDES += [
+ "/browser/app/winlauncher",
+]
+
+OS_LIBS += [
+ "ntdll",
+]
+
+if CONFIG["CC_TYPE"] in ("gcc", "clang"):
+ # This allows us to use wmain as the entry point on mingw
+ LDFLAGS += [
+ "-municode",
+ ]