summaryrefslogtreecommitdiffstats
path: root/browser/installer/windows
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /browser/installer/windows
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/installer/windows')
-rw-r--r--browser/installer/windows/Makefile.in77
-rw-r--r--browser/installer/windows/app.tag4
-rw-r--r--browser/installer/windows/docs/FullConfig.rst83
-rw-r--r--browser/installer/windows/docs/FullInstaller.rst14
-rw-r--r--browser/installer/windows/docs/Helper.rst39
-rw-r--r--browser/installer/windows/docs/InstallerBuild.rst55
-rw-r--r--browser/installer/windows/docs/MSI.rst9
-rw-r--r--browser/installer/windows/docs/MSIX.rst226
-rw-r--r--browser/installer/windows/docs/StubArch.rst18
-rw-r--r--browser/installer/windows/docs/StubConfig.rst36
-rw-r--r--browser/installer/windows/docs/StubGUI.rst52
-rw-r--r--browser/installer/windows/docs/StubInstaller.rst13
-rw-r--r--browser/installer/windows/docs/WebBrowserAPI.rst35
-rw-r--r--browser/installer/windows/docs/index.rst21
-rw-r--r--browser/installer/windows/moz.build19
-rw-r--r--browser/installer/windows/msi/installer.wxs109
-rw-r--r--browser/installer/windows/msix/AppxManifest.xml.in139
-rw-r--r--browser/installer/windows/msix/Resources.pribin0 -> 2128 bytes
-rw-r--r--browser/installer/windows/msix/distribution/distribution.ini12
-rw-r--r--browser/installer/windows/msix/msix-all-locales361
-rw-r--r--browser/installer/windows/msix/priconfig.xml38
-rw-r--r--browser/installer/windows/nsis/content/installing.html47
-rw-r--r--browser/installer/windows/nsis/content/installing.js56
-rw-r--r--browser/installer/windows/nsis/content/profile_cleanup.html38
-rw-r--r--browser/installer/windows/nsis/content/profile_cleanup.js28
-rw-r--r--browser/installer/windows/nsis/content/stub_common.css37
-rw-r--r--browser/installer/windows/nsis/content/stub_common.js22
-rw-r--r--browser/installer/windows/nsis/defines.nsi.in154
-rw-r--r--browser/installer/windows/nsis/extensionsLocale.nsh20
-rwxr-xr-xbrowser/installer/windows/nsis/installer.nsi1967
-rw-r--r--browser/installer/windows/nsis/maintenanceservice_installer.nsi343
-rwxr-xr-xbrowser/installer/windows/nsis/shared.nsh1826
-rw-r--r--browser/installer/windows/nsis/stub.nsi1755
-rwxr-xr-xbrowser/installer/windows/nsis/uninstaller.nsi1175
-rw-r--r--browser/installer/windows/nsis/updater_append.ini12
-rw-r--r--browser/installer/windows/stub.tag4
36 files changed, 8844 insertions, 0 deletions
diff --git a/browser/installer/windows/Makefile.in b/browser/installer/windows/Makefile.in
new file mode 100644
index 0000000000..279e25158f
--- /dev/null
+++ b/browser/installer/windows/Makefile.in
@@ -0,0 +1,77 @@
+# 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 $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
+
+CONFIG_DIR = instgen
+ifeq ($(TARGET_CPU), aarch64)
+SFX_MODULE = $(topsrcdir)/other-licenses/7zstub/firefox/7zSD.ARM64.sfx
+else
+SFX_MODULE = $(topsrcdir)/other-licenses/7zstub/firefox/7zSD.Win32.sfx
+endif
+
+INSTALLER_FILES = \
+ app.tag \
+ nsis/content/installing.html \
+ nsis/content/installing.js \
+ nsis/content/profile_cleanup.html \
+ nsis/content/profile_cleanup.js \
+ nsis/content/stub_common.css \
+ nsis/content/stub_common.js \
+ nsis/installer.nsi \
+ nsis/uninstaller.nsi \
+ nsis/stub.nsi \
+ nsis/shared.nsh \
+ stub.tag \
+ $(NULL)
+
+ifdef MOZ_MAINTENANCE_SERVICE
+INSTALLER_FILES += \
+ nsis/maintenanceservice_installer.nsi \
+ $(NULL)
+endif
+
+BRANDING_FILES = \
+ branding.nsi \
+ firefox64.ico \
+ stubinstaller/bgstub.jpg \
+ stubinstaller/installing_page.css \
+ stubinstaller/profile_cleanup_page.css \
+ wizHeader.bmp \
+ wizHeaderRTL.bmp \
+ wizWatermark.bmp \
+ $(NULL)
+
+include $(topsrcdir)/config/config.mk
+
+ifdef IS_LANGUAGE_REPACK
+PPL_LOCALE_ARGS = \
+ --l10n-dir=$(REAL_LOCALE_MERGEDIR)/browser/installer \
+ --l10n-dir=$(call EXPAND_LOCALE_SRCDIR,browser/locales)/installer \
+ --l10n-dir=$(topsrcdir)/browser/locales/en-US/installer \
+ $(NULL)
+else
+PPL_LOCALE_ARGS=$(call EXPAND_LOCALE_SRCDIR,browser/locales)/installer
+endif
+
+$(CONFIG_DIR)/setup.exe::
+ $(RM) -r $(CONFIG_DIR)
+ $(MKDIR) $(CONFIG_DIR)
+ $(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
+ $(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR)
+ $(call py_action,preprocessor defines.nsi,-Fsubstitution $(DEFINES) $(ACDEFINES) \
+ $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
+ $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+ --preprocess-locale $(topsrcdir) \
+ $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
+ $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+ --preprocess-single-file $(topsrcdir) \
+ $(PPL_LOCALE_ARGS) $(CONFIG_DIR) \
+ nsisstrings.properties nsisstrings.nlf
+ $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+ --convert-utf8-utf16le \
+ $(srcdir)/nsis/extensionsLocale.nsh $(CONFIG_DIR)/extensionsLocale.nsh
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/makensis.mk
diff --git a/browser/installer/windows/app.tag b/browser/installer/windows/app.tag
new file mode 100644
index 0000000000..479d9f714a
--- /dev/null
+++ b/browser/installer/windows/app.tag
@@ -0,0 +1,4 @@
+;!@Install@!UTF-8!
+Title="Mozilla Firefox"
+RunProgram="setup.exe"
+;!@InstallEnd@! \ No newline at end of file
diff --git a/browser/installer/windows/docs/FullConfig.rst b/browser/installer/windows/docs/FullConfig.rst
new file mode 100644
index 0000000000..37dd9e8f0c
--- /dev/null
+++ b/browser/installer/windows/docs/FullConfig.rst
@@ -0,0 +1,83 @@
+============================
+Full Installer Configuration
+============================
+
+.. note::
+
+ This page applies to the ``.exe`` full installer. If you want to run or deploy the :doc:`MSI package <MSI>`, refer to the instructions on `the support web page for it <https://support.mozilla.org/kb/deploy-firefox-msi-installers>`_ instead; the command-line options documented here won't work.
+
+Command-line Options
+--------------------
+
+The full installer provides a number of options that can be used either from the GUI or from the command line. The following command line options are accepted. The list is valid for Firefox 62 and later. Prior to Firefox 62, only ``/S`` and ``/INI`` are accepted, and ``/StartMenuShortcut`` is not available in INI files, only the plural ``/StartMenuShortcuts`` works (even though only one shortcut is created).
+
+The presence of **any** command-line option implicitly enables silent mode (see the ``/S`` option).
+
+Each option must start with a ``/`` as shown, ``-`` or ``--`` are not supported. Short names for the options are not provided; all names must be spelled out.
+
+For options that accept ``true`` or ``false``, ``=true`` can be left off to get the same effect. That is, ``/DesktopShortcut`` and ``/DesktopShortcut=true`` both enable the desktop shortcut.
+
+``/S``
+ Silent installation. This option doesn't open the GUI, instead running the installation in the background using all the default settings. It's useful as part of a script for configuring a new system, for example.
+
+ For backwards compatibility, this option can also be spelled ``-ms``.
+
+``/InstallDirectoryPath=[path]``
+ Absolute path specifying the complete install location. This directory does not need to exist already (but it can).
+
+ If ``InstallDirectoryName`` is set, then this setting will be ignored.
+
+``/InstallDirectoryName=[name]``
+ Name of the installation directory to create within Program Files. For example, if ``InstallDirectoryName`` is set to ``Firefox Release``, then the installation path will be something like ``C:\Program Files\Firefox Release``. The Program Files path used will be the correct one for the architecture of the application being installed and the locale/configuration of the machine; this setting is mainly useful to keep you from having to worry about those differences.
+
+ If this is set, then ``InstallDirectoryPath`` will be ignored.
+
+``/TaskbarShortcut={true,false}``
+ Set to ``false`` to disable pinning a shortcut to the taskbar. ``true`` by default.
+
+``/DesktopShortcut={true,false}``
+ Set to ``false`` to disable creating a shortcut on the desktop. ``true`` by default.
+
+``/StartMenuShortcut={true,false}``
+ Set to ``false`` to disable creating a Start menu shortcut. ``true`` by default.
+
+ For backwards compatibility, this option can also be spelled ``/StartMenuShortcuts`` (plural), however only one shortcut is ever created in the Start menu per installation.
+
+``/PrivateBrowsingShortcut={true,false}``
+ Set to ``false`` to disable creating a private browsing shortcut in the start menu. ``true`` by default.
+
+``/MaintenanceService={true,false}``
+ Set to ``false`` to disable installing the Mozilla Maintenance Service. This will effectively prevent users from installing Firefox updates if they do not have write permissions to the installation directory. ``true`` by default.
+
+``/RemoveDistributionDir={true,false}``
+ Set to ``false`` to disable removing the ``distribution`` directory from an existing installation that's being paved over. By default this is ``true`` and the directory is removed.
+
+``/PreventRebootRequired={true,false}``
+ Set to ``true`` to keep the installer from taking actions that would require rebooting the machine to complete, normally because files are in use. This should not be needed under normal circumstances because no such actions should be required unless you're paving over a copy of Firefox that was running while the installer was trying to run, and setting this option in that case may result in an incomplete installation. ``false`` by default.
+
+``/OptionalExtensions={true,false}``
+ Set to ``false`` to disable installing any bundled extensions that are present. Normally none of these exist, except in special distributions of Firefox such as the one produced by Mozilla China or by other partner organizations. ``true`` by default.
+
+``/RegisterDefaultAgent={true,false}``
+ Set to ``false`` to disable creating a recurring scheduled task to run the default browser agent. There are other ways (a policy and a pref) to disable the actions that the agent takes; this option is provided for tightly-controlled environments where even a
+ scheduled task that simply exits immediately is undesirable.
+
+``/INI=[absolute path to .ini file]``
+ Read configuration from an .ini file. All settings should be placed into one section, called ``[Install]``, and use the standard INI syntax. All settings are optional; they can be included or left out in any combination. Order does not matter.
+
+ The available settings in the .ini file are the same as the command line options, except for ``/S`` and ``/INI`` (of course). They should be set with the same syntax described above for command line use.
+
+ For any option provided both in an .ini file and on the command line, the value found on the command line will be used. This allows command line options to override .ini settings.
+
+ Here's an example of a valid .ini file for use with this option::
+
+ [Install]
+ ; Semicolons can be used to add comments
+ InstallDirectoryName=Firefox Release
+ DesktopShortcut=false
+ StartMenuShortcuts=true
+ MaintenanceService=false
+ OptionalExtensions=false
+
+``/ExtractDir=[directory]``
+ Extract the application files to the given directory and exit, without actually running the installer. No other options may be supplied along with ``ExtractDir``, and ``ExtractDir`` is not available for use in .ini files.
diff --git a/browser/installer/windows/docs/FullInstaller.rst b/browser/installer/windows/docs/FullInstaller.rst
new file mode 100644
index 0000000000..fcfa60371e
--- /dev/null
+++ b/browser/installer/windows/docs/FullInstaller.rst
@@ -0,0 +1,14 @@
+==============
+Full Installer
+==============
+
+The full installer is actually responsible for installing the browser; it's what the stub launches in order to do the "real" installing work, but it's also available separately. It uses a traditional "wizard" interface design, as is (somewhat) natively supported by NSIS. It can also be :doc:`configured <FullConfig>` to launch in a silent mode, suitable for scripting or managed deployments.
+
+The full installer's main script is `installer.nsi <https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/installer.nsi>`_, but most of the heavy lifting is done by the shared functions in `common.nsh <https://searchfox.org/mozilla-central/source/toolkit/mozapps/installer/windows/nsis/common.nsh>`_.
+
+If it was not launched by the :doc:`StubInstaller`, an :ref:`Install Ping` is sent when the installer exits.
+
+The installer writes ``installation_telemetry.json`` to the install location, this is read by Firefox in order to send a telemetry event, see the event definition in `Events.yaml <https://searchfox.org/mozilla-central/source/toolkit/components/telemetry/Events.yaml>`_ (category ``installation``, event name ``first_seen``) for a description of the properties. There is also an ``install_timestamp`` property, which is saved in the profile to determine whether there has been a new installation; this is not sent as part of the ping.
+
+.. toctree::
+ FullConfig
diff --git a/browser/installer/windows/docs/Helper.rst b/browser/installer/windows/docs/Helper.rst
new file mode 100644
index 0000000000..de69e12e27
--- /dev/null
+++ b/browser/installer/windows/docs/Helper.rst
@@ -0,0 +1,39 @@
+======
+Helper
+======
+helper.exe contains the uninstaller, plus a routine that's run by the application updater after it applies an update, as well as a few utilities used for default browser handling and shortcut maintenance. It mainly consists of two files, uninstaller.nsi_, which is the main script and contains the entry point and the uninstall logic, and shared.nsh_, which contains most of the logic for the other functions.
+
+Uninstaller
+-----------
+The uninstaller may be the most straightforward of the installer components. The only complexity comes from a need to avoid accidentally removing any user files that may have found their way into the installation directory; the general philosophy of the uninstaller is to remove everything that the installer creates, and nothing that it doesn't.
+
+* Any registry entries the installer would have created are removed, even ones only used by very old installer versions.
+* All the files that the uninstaller knows were created by the installer or are owned by the application are deleted, or flagged for deletion on reboot if any are in use. There are a few hard-coded directories that are known to be safe to delete (for example, the distribution directory, and any temporary directories created by the updater). For a list of application files that are safe to uninstall, we read a file from the application directory called ``precomplete``. This file is mainly used to tell the updater what it should do to clean out the directory when applying a complete update (one that replaces all application files), but that means it contains a handy auto-generated list of all application files, so it can be reused for uninstallation.
+* If the application directory is empty after that, then it is removed, but it's left alone if any files are still present.
+* If the copy of Firefox that was just uninstalled is the only one that was using the maintenance service, the maintenance service uninstaller is also run.
+* Any BITS jobs from this installation of Firefox are cancelled.
+* The first :ref:`"uninstall" ping` found is uploaded, and all uninstall pings are deleted (for this installation).
+
+Note that profiles and any other user-generated files (e.g., crash reports) are specifically not uninstalled.
+
+PostUpdate
+----------
+At the end of an application update cycle, after the new files are in place, the updater invokes the helper with the ``/PostUpdate`` command-line switch. The PostUpdate function fills a grab bag of responsibilities which are all focused around maintaining system integration objects created by the installer. For example, a number of registry entries contain the version number, so that has to be changed on updates. If the branding name of the application changes and shortcuts have to be renamed, that's done here as well. For one counterexample, changing icons does not require any code in PostUpdate, or anywhere else; new icons are automatically picked up by the shell. The PostUpdate function also keeps the maintenance service up to date.
+
+It's important to remember that PostUpdate is, indeed, post-update. It doesn't run until after its own code has already been updated. This makes it really the only phase of the update process where changes can go into affect immediately in the first build that contains a patch, instead of having to wait for the next update after that. This makes it a good place to put anything that needs to be done before the new version of the application can run; this includes things like registering DLL's, which the installer also handles, but that PostUpdate has to take care of for existing installations.
+
+PostUpdate actually runs two times after each update: once in an elevated context, and once as a regular user who performed the update. Both runs do similar work - the main difference being that we update HKLM registry entries are updated by the elevated instance, while HKCU ones are updated by the non-elevated instance.
+
+It's also important to remember that PostUpdate, being part of the installer code, only exists on Windows, so it can't be used to fix things up on other platforms the same way.
+
+Default Browser and Shortcut Handling
+-------------------------------------
+Windows versions older than 10 contain a control panel called Set Program Access and Defaults, or SPAD. As the name suggests, this was the UI for setting default programs for classes of activities ("web browser" or "e-mail client" for example), as the Windows 10 default program settings page is, but it also controls program "access," which is typically defined as whether or not shortcuts for the program exist. To support this interface, an application has to register a set of commands that the interface can invoke to hide or show the shortcuts, and to have the application make itself the default. We implement these actions in helper.exe; they're triggered by invoking it with the command-line switches ``/ShowShortcuts``, ``/HideShortcuts``, or ``/SetAsDefaultAppGlobal``.
+
+The helper also implements the ``/SetAsDefaultAppUser`` switch, which is invoked by the "Make Default" button in the Firefox preferences UI.
+
+On Windows 10 neither SetAsDefaultAppUser nor SetAsDefaultAppGlobal is effective because the default programs settings can only be modified by the Windows settings app. However they do still write the registry entries that are needed to get us an entry in the system default browser menu, should those entries not already exist (the installer always creates them, but running Firefox without having run the installer is supported). ShowShortcuts and HideShortcuts are never called on Windows 10 because the SPAD control panel no longer exists.
+
+
+.. _uninstaller.nsi: https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/uninstaller.nsi
+.. _shared.nsh: https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/shared.nsh
diff --git a/browser/installer/windows/docs/InstallerBuild.rst b/browser/installer/windows/docs/InstallerBuild.rst
new file mode 100644
index 0000000000..a98e819d8c
--- /dev/null
+++ b/browser/installer/windows/docs/InstallerBuild.rst
@@ -0,0 +1,55 @@
+===============
+Installer Build
+===============
+
+How to build the installers
+---------------------------
+
+The easiest way to build an installer in your local tree is to run ``mach package``. The finished installers will be in ``$OBJDIR/dist/install/sea/``. You have to have a build of the application already done before that will work, but `artifact builds <https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Artifact_builds>`_ are supported, so that can save you a lot of time if you're only doing installer work.
+
+You'll also need to be on a Windows machine; the installer build depends on tools that aren't available for other platforms.
+
+Stub installer
+~~~~~~~~~~~~~~
+
+The stub installer probably won't be built by default in a local tree; normally unless the build has been set to use one of the official update channels, only the full installer is built. If you need to work on the stub installer, you can override the default and get one built along with the full installer by adding ``export MOZ_STUB_INSTALLER=1`` to your mozconfig.
+
+Uninstaller
+~~~~~~~~~~~
+
+The uninstaller is built as part of the main application build, not the installer target, so ``mach build`` is what will get you an uninstaller. You'll find it at ``$OBJDIR/dist/bin/uninstaller/helper.exe``.
+
+Branding
+~~~~~~~~
+
+By default local builds use "unofficial" branding, which somewhat resembles a previous version of the Nightly branding, but is designed not to resemble any official channel too closely.
+
+But sometimes you'll need to test installers that are built using one or more of the official channel branding configurations, perhaps to try out different strings, make sure different sets of art look good, or to test behavior around installing multiple channels at the same time.
+
+You can build installers (and the entire application) with official branding by adding ``ac_add_options --with-branding=browser/branding/{nightly|aurora|official}`` to your mozconfig (the default branding is ``browser/branding/unofficial``).
+
+Build process
+-------------
+
+.. note::
+
+ If you intend to distribute your build to others, you'll want to add
+ ``export WIN32_REDIST_DIR=<CRT_LOCATION>`` in your ``mozconfig``. The CRT location
+ will vary depending on your Visual Studio version. At the time of writing, this would look like:
+ ``export WIN32_REDIST_DIR="/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Redist/MSVC/14.28.29325/x86/Microsoft.VC142.CRT"``.
+
+Both the full and stub installers are built through a similar process, which is summarized here along with references to the relevant bits of code.
+
+Most of this procedure is done in `makensis.mk <https://searchfox.org/mozilla-central/source/toolkit/mozapps/installer/windows/nsis/makensis.mk>`_ and in the `mach repackage <https://searchfox.org/mozilla-central/rev/2b9779c59390ecc47be7a70d99753653d8eb5afc/python/mozbuild/mozbuild/mach_commands.py#2166>`_ command.
+
+0. A prerequisite is for the application to be in a packaged state, so ``mach package`` first creates a release-style package and puts it in ``$OBJDIR/dist/firefox``.
+1. All required files are copied into the instgen directory. This includes .nsi and .nsh script files, plugin DLL files, image and icon files, and the 7-zip SFX module and its configuration files.
+2. The NSIS scripts are compiled, resulting in setup.exe and setup-stub.exe (if building the stub is enabled).
+3. The 7-zip SFX module is run through UPX.
+4. The application files and the full installer setup.exe are compressed together into one 7-zip file.
+5. The stub installer is compressed into its own 7-zip file.
+6. The (UPX-packed) 7-zip SFX module, the correct configuration data, and the 7-zip file containing the application files and setup.exe are concatenated together. This results in the final full installer.
+7. The (still UPX-packed) 7-zip SFX module, the correct configuration data, and the 7-zip file containing the stub installer are concatenated together. This results in the final stub installer.
+
+
+If this is an official build running on Mozilla automation infrastructure, then after this the installers will be signed, like other build products. Release engineering owns that process, it's not within the scope of this documentation.
diff --git a/browser/installer/windows/docs/MSI.rst b/browser/installer/windows/docs/MSI.rst
new file mode 100644
index 0000000000..9e5f30a711
--- /dev/null
+++ b/browser/installer/windows/docs/MSI.rst
@@ -0,0 +1,9 @@
+===========
+MSI Package
+===========
+
+We provide a `Windows Installer <https://wikipedia.org/wiki/Windows_Installer>`_ MSI package for the convenience of administrators deploying Firefox in their organizations. The primary documentation for how to obtain and use these packages is hosted on `the Mozilla support web site <https://support.mozilla.org/kb/deploy-firefox-msi-installers>`_.
+
+Our MSI packages are not "true" Windows Installer packages; they don't actually contain any installable components and don't register any product. They simply wrap the :doc:`full installer <FullInstaller>`, which performs the installation just as if it had been run normally. The main reason for this is the difficulty of adapting Firefox's existing update mechanisms to fit the way that Windows Installer would expect things to be done.
+
+The MSI package is built for every release along with the other package formats using the command ``./mach repackage msi``. That command invokes the `WiX tools <https://wixtoolset.org/>`_ to build the package from `a normal WiX XML file <https://searchfox.org/mozilla-central/source/browser/installer/windows/msi/installer.wxs>`_, after filling in a few variables to customize the package for that particular Firefox build. The majority of the WiX file is concerned with passing parameters through from MSI properties to command-line arguments for the full installer to use; the rest is boilerplate to satisfy the minimum requirements for something that WiX will build and the Windows Installer system will load and run without errors.
diff --git a/browser/installer/windows/docs/MSIX.rst b/browser/installer/windows/docs/MSIX.rst
new file mode 100644
index 0000000000..771ea6c061
--- /dev/null
+++ b/browser/installer/windows/docs/MSIX.rst
@@ -0,0 +1,226 @@
+MSIX Package
+============
+
+Firefox MSIX packages are full participants in the "modern" Windows
+app packaging system. They are distributed, installed, updated,
+repaired, and uninstalled entirely using that system. This gives
+administrators lots of deployment options, and also grants complete
+control over when and how application updates are rolled out
+(Firefox's built-in updater is always fully disabled in MSIX
+packages). This stands in contrast to Firefox MSI packages, which
+mostly work against the Windows Installer framework rather than with
+it, and therefore are missing a lot of important functionality; for
+example, tools that install MSI packages generally cannot uninstall
+Firefox [#]_. This means the MSIX package may well be the better
+option for deploying to Windows 10 and up.
+
+In automation
+-------------
+
+The ``repackage-msix`` and ``repackage-shippable-l10n-msix`` tasks
+repackage the ZIP packages produced by signed build tasks into MSIX
+packages. The ``shippable-l10n`` variants depend on Linux64 builds and
+localization tasks to produce signed langpacks, which are incorporated
+into the MSIX package as distribution extensions. (This is the same
+approach taken by ``snap`` and ``flatpak`` Linux packages.)
+
+The ``repackage-signing-msix`` and
+``repackage-signing-shippable-l10n-msix`` tasks sign the MSIX packages
+produced by the repackage tasks.
+
+Signing in automation
+~~~~~~~~~~~~~~~~~~~~~
+
+MSIX packages are signed by the same certificates that sign binaries for
+other jobs. In practice, this means that try builds are signed with the
+```Mozilla Fake CA``
+certificate `MozFakeCA_2017-10-13.cer <https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/3493a608bf700b68a54ff2fd506f33373bb87a04/userdata/Configuration/Mozilla%20Maintenance%20Service/MozFakeCA_2017-10-13.cer>`__.
+In order to install try builds locally, you must trust this certificate.
+**For your own security, it's best to do this in Windows Sandbox or a
+Virtual Machine**. To do so run the following in an elevated
+(administrator) Powershell:
+
+::
+
+ $ Import-Certificate -FilePath "MozFakeCA_2017-10-13.cer" -Cert Cert:\LocalMachine\Root\
+ ...
+ Thumbprint Subject
+ ---------- -------
+ FA056CEBEFF3B1D0500A1FB37C2BD2F9CE4FB5D8 CN=Mozilla Fake CA
+
+The ``shippable-l10n`` MSIX variants incorporate signed langpacks. These
+in turn are signed with the same certificate. Firefox knows about this
+certificate but does not trust it by default. To trust it, set the
+hidden Gecko boolean preference
+
+::
+
+ xpinstall.signatures.dev-root=true
+
+Sadly, it's not possible to set preferences via a ``distribution.ini``
+early enough to impact loading the signed langpacks (see `Bug
+1721764 <https://bugzilla.mozilla.org/show_bug.cgi?id=1721764>`__), and
+once the signed langpacks fail to load once, they will not be reloaded
+(see `Bug
+1721763 <https://bugzilla.mozilla.org/show_bug.cgi?id=1721763>`__). This
+make testing the first-run experience challenging. What can be done is
+to install the MSIX package (perhaps using
+``Add-AppxPackage -Path ...``) and determine the profile directory
+(using ``about:support``). Uninstall the MSIX package (perhaps using
+``Get-AppxPackage | Where -Property Name -like "Mozilla.*" | Remove-AppxPackage``).
+Delete the contents of the profile directory entirely, but add a file
+``user.js`` containing:
+
+::
+
+ user_pref("xpinstall.signatures.dev-root", true);
+ user_pref("extensions.logging.enabled", true);
+
+Reinstall the MSIX package and the signed langpacks should now be loaded
+(slowly!) and available after first startup.
+
+Local developer builds
+----------------------
+
+``mach repackage msix`` lets you repackage a Firefox package (or
+directory) into an MSIX/App Package. The main complication is that an
+App Package contains channel-specific paths and assets, and therefore
+needs to be branding-aware, much as an Android package needs to be
+branding-aware.
+
+Usage
+~~~~~
+
+The tool is designed to repackage ZIP archives produced in automation.
+Start looking for official builds at locations like:
+
+========== ==========================================================================================================================
+Channel URL
+========== ==========================================================================================================================
+Release https://archive.mozilla.org/pub/firefox/candidates/88.0.1-candidates/build1/win64/en-US/firefox-88.0.1.zip
+Beta https://archive.mozilla.org/pub/firefox/candidates/89.0b15-candidates/build1/win64/en-US/firefox-89.0b15.zip
+Devedition https://archive.mozilla.org/pub/devedition/candidates/89.0b15-candidates/build1/win64/en-US/firefox-89.0b15.zip
+Nightly https://archive.mozilla.org/pub/firefox/nightly/2021/05/2021-05-21-09-57-54-mozilla-central/firefox-90.0a1.en-US.win64.zip
+========== ==========================================================================================================================
+
+Repackage using commands like:
+
+::
+
+ $ ./mach repackage msix \
+ --input firefox-88.0.1.zip \
+ --channel=official \
+ --arch=x86_64 \
+ --verbose
+
+Or package a local developer build directly with ``mach repackage msix``:
+
+::
+
+ $ ./mach repackage msix
+
+This command will do its best to guess your channel and other necessary
+information. You can override these with options like ``--channel``
+(see the ``--help`` text for all supported options).
+
+Paths to tools can be set via environment variables. In order, searched
+first to searched last:
+
+1. the tool name, like ``MAKEAPPX`` or ``SIGNTOOL``
+2. searching on ``PATH``
+3. searching under ``WINDOWSSDKDIR``
+4. searching under ``C:/Program Files (x86)/Windows Kits/10``
+
+If you are cross compiling from Linux or macOS you will need a
+compiled version of `Mozilla's fork of Microsoft's msix-packaging
+<https://github.com/mozilla/msix-packaging/tree/johnmcpms/signing>`__
+tools.
+
+Linux users can obtain a prebuilt version with:
+
+::
+
+ $ ./mach artifact toolchain --from-build linux64-msix-packaging
+
+After `bug 1743036 <https://bugzilla.mozilla.org/show_bug.cgi?id=1743036>`__
+is fixed, macOS and Windows users will have a similar option.
+
+Signing locally
+~~~~~~~~~~~~~~~
+
+The repackaged MSIX files produced are not signed by default. In
+automation, Mozilla's signing service signs the repackaged MSIX files.
+For local testing, you can sign them with a self-signed certificate by
+adding ``--sign`` to ``mach repackage msix``, or with commands like:
+
+::
+
+ $ ./mach repackage sign-msix --input test.msix --verbose
+
+Or sign them yourself following `Microsoft's self-signed certificate
+instructions <https://docs.microsoft.com/en-us/windows/msix/package/create-certificate-package-signing#create-a-self-signed-certificate>`__.
+
+Signing Certificates
+^^^^^^^^^^^^^^^^^^^^
+
+Mach will create the necessary signing keys and certificates for you
+and reuse them for subsequent signings. Before your locally signed
+builds can be installed you will need to install the correct
+certificate to the Windows Root Store. This can be done with a command
+like:
+
+::
+
+ $ powershell -c 'Import-Certificate -FilePath mycert.cer -Cert Cert:\LocalMachine\Root\'
+
+The exact command to run will be shown if you run ``./mach repackage``
+with ``--verbose``.
+
+You _may_ choose to sign in a different manner, with a key and certificate
+you create yourself, but Windows requires that the Subject of the certificate
+match the Publisher found in the MSIX's AppxManifest.xml. If you choose
+to go this route, ensure that you pass ``--publisher`` to
+``./mach repackage msix`` to set that correctly.
+
+For developers
+~~~~~~~~~~~~~~
+
+Updating the MSIX template
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MSIX is an "open format" in one sense: the MSIX container format is
+specified at https://github.com/Microsoft/msix-packaging. It is
+categorically *not* an open format in another sense: many of the
+contained files are proprietary binary formats (``.reg`` -- registry
+hive files) or undocumented (``.pri`` files -- resource index files).
+
+Generally the MSIX packaging code tries to avoid requiring such files
+that can't be built from sources. Where they are truly required, it
+tries to use a single such file independent of branding and other
+configuration, checked into the source tree.
+
+resources.pri
+'''''''''''''
+
+Generate a new ``resources.pri`` file on a Windows machine using
+``makepri.exe`` from the Windows SDK, like:
+
+::
+
+ $ makepri.exe new \
+ -IndexName firefox \
+ -ConfigXml browser/installer/windows/msix/priconfig.xml \
+ -ProjectRoot browser/branding/official/msix \
+ -OutputFile browser/installer/windows/msix/resources.pri \
+ -Overwrite
+
+The choice of channel (i.e.,
+``browser/branding/{official,aurora,beta,nightly,unofficial}``) should
+not matter.
+
+.. [#] The MSI has to be limited in this way because of the difficulty
+ of migrating existing installations into MSI and adding support
+ for it to Firefox's update pipeline. MSIX does not have these
+ constraints, because the partially virtualized file system that
+ these kinds of apps run in makes install migration impossible
+ and unnecessary.
diff --git a/browser/installer/windows/docs/StubArch.rst b/browser/installer/windows/docs/StubArch.rst
new file mode 100644
index 0000000000..2ab8bc65dc
--- /dev/null
+++ b/browser/installer/windows/docs/StubArch.rst
@@ -0,0 +1,18 @@
+===========================
+Stub Installer Architecture
+===========================
+
+The stub is called a stub because it doesn't actually install anything. It's just a GUI frontend that downloads and runs the full installer in silent mode. The exact full installer that will be downloaded isn't baked into the stub; the channel and the locale (which are baked in) are sent in a request to the Bouncer service, which uses that information to redirect to the URL of the specific full installer file.
+
+The main stub installer source code file is `stub.nsi <https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/stub.nsi>`_. Even though the stub installer doesn't install anything, it's still built on NSIS. This means the structure of stub.nsi is a bit odd for an NSIS script. There's only one section, and it's empty, and there are no predefined pages used (including no instfiles page, which we get a warning from the compiler about). The work is all done in two custom pages, createProfileCleanup and createInstall, and the functions called by those two pages. Also, the stub's dialogs are built using HTML and the Windows WebBrowser control (based on Internet Explorer); see :doc:`this page <StubGUI>` for details about that.
+
+The basic execution flow is this:
+
+1. .onInit checks basic system requirements, determines whether we should install a 64-bit or a 32-bit build, looks for an existing installation that we should try to pave over, displays a UAC prompt, and initializes lots of variables and GUI objects.
+2. createProfileCleanup determines if a profile cleanup prompt should be offered (see the ShouldPromptForProfileCleanup function), and calls the web control to draw the UI for that if so.
+3. createInstall draws the UI for the download/install page, again using the web control, kicks off the periodic timer that swaps out the blurb text every few seconds (if the branding includes that), and runs StartDownload.
+4. StartDownload invokes the InetBgDl plugin to begin the full installer download on a background thread. It then starts a periodic timer for the OnDownload function.
+5. Every time OnDownload runs (every 200 ms), it checks the status of the background download, retries or restarts if the download has failed, updates the progress bar if the download is still running, and verifies and runs the full installer if the download is complete.
+6. CheckInstall waits for the full installer to exit, then deletes the file.
+7. FinishInstall copies the post-signing data, then waits for the application to launch, requesting it perform profile cleanup if that option was selected.
+8. Once the installed application is running and has shown a window (or if another copy was already running), the stub sends its ping and then exits.
diff --git a/browser/installer/windows/docs/StubConfig.rst b/browser/installer/windows/docs/StubConfig.rst
new file mode 100644
index 0000000000..caf414a5fd
--- /dev/null
+++ b/browser/installer/windows/docs/StubConfig.rst
@@ -0,0 +1,36 @@
+============================
+Stub Installer Configuration
+============================
+
+The stub installer doesn't offer many options to the user, most of its configuration is automatic and implicit. But there are two things the user can control, and here we'll discuss those and the automated settings.
+
+Architecture
+------------
+The stub installer automatically selects whether to install a 32-bit or a 64-bit build of Firefox. 64-bit will be selected only if these conditions all pass:
+
+1. The operating system is a 64-bit build.
+2. The system has enough physical memory installed. Currently an amount strictly greater than 2 GB is required (that is, exactly 2 GB is not considered enough).
+3. No third-party software that is incompatible with the 64-bit build is present on the system.
+
+If any of the above conditions is not satisfied, a 32-bit build is installed.
+
+Scope
+-----
+We support creating the installation in either a machine or a per-user scope. This affects whether application files, shortcuts, and registry entries are created in locations that are accessible to any user on the machine or locations that are specific to a particular user. Even in the full installer there is no UI for configuring which scope is selected; instead both installers automatically select a scope based on whether they have the privileges needed to perform a machine scope installation. This means a user with those privileges can effectively control the scope by the selection they make in the Windows UAC prompt; rejecting it will mean that the installer doesn't see that user as an administrator and will perform a per-user installation.
+
+Install Location
+----------------
+The user doesn't have any control over the install location used by the stub. To determine the location that will be used, first the stub looks for any existing installations of the same channel of Firefox. If it finds one of those and the architecture of that installation is the same as the one that's been selected for the new one, then the path for the new install will be set to the same as that install, effectively overwriting it. This allows the stub to be used as a repair method for broken installations. If there isn't any existing installation fitting those criteria, then a hard-coded default is selected based on the architecture, scope, and channel.
+
+Profile Cleanup
+---------------
+If the stub installer detects that this could be a helpful thing to do, it will start off the installation by offering to clean up the user's Firefox profile. This is the same cleanup that's triggered by the "refresh" button in about:support, and it's performed by the same Firefox code (the stub really just asks the browser to do the cleanup using a command-line parameter, it doesn't try to perform the operation itself). The user can control whether this cleanup is done by toggling a checkbox that's shown before the installation begins.
+
+There are two variations of the profile cleanup prompt that can be shown to the user, one that appears if the new installation would overwrite an existing one (the "paveover" prompt) and another if it would be a new installation (the "reinstall" prompt). The only difference is the text that the user sees, the two function identically.
+
+This is the procedure that determines when one of the two cleanup prompts is shown:
+
+1. Look for the current default Firefox profile. If there are no profiles or none is set as the default, don't show either prompt.
+2. Look for an existing installation by searching the registry for any copies of Firefox that are registered for potential file type associations. If none exist, show the reinstall prompt.
+3. Check if the existing installation is for the same channel that's being installed now. If not, don't show either prompt.
+4. Check the version of Firefox that the default profile we found in step 1 was last used with. This information comes from the profile's compatibility.ini file. If that version is more than 2 versions behind the current version, show the paveover prompt. Otherwise, don't show either prompt. Information about the current version is taken from `<https://product-details.mozilla.org/1.0/firefox_versions.json>`_.
diff --git a/browser/installer/windows/docs/StubGUI.rst b/browser/installer/windows/docs/StubGUI.rst
new file mode 100644
index 0000000000..4e285f5e2e
--- /dev/null
+++ b/browser/installer/windows/docs/StubGUI.rst
@@ -0,0 +1,52 @@
+==================
+Stub Installer GUI
+==================
+
+The stub installer's primary GUI is built using the web platform, rendered by the native Windows browser control ``IWebBrowser2``, which is based on Internet Explorer (Microsoft never updated it to use Edge [#1]_).
+
+This is all driven by a custom NSIS plugin, called WebBrowser. The plugin's source code can be found in the directory ``other-licenses/nsis/Contrib/WebBrowser``, and its public API is documented :doc:`here <WebBrowserAPI>`. The plugin implements the COM interfaces needed to host the IWebBrowser2 object and embed it using OLE into the dialog provided by NSIS. It also provides an implementation of timers [#2]_, and supports making NSIS functions callable from JavaScript running inside the web page.
+
+Note that not 100% of the stub UI is built using WebBrowser; we also have message boxes which use `the Windows TaskDialog function <https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-taskdialog>`_, invoked from NSIS code using the standard System plugin.
+
+Changing the UI
+===============
+
+For each of the two main pages, there is one HTML file (which also contains the JavaScript for the page) and one CSS file.
+
+The HTML file for each page is always the same regardless of the branding, so it's kept in the ``installer`` directory next to ``stub.nsi``. These files should be updated if elements need to be added or removed from the page, or if features requiring JavaScript are being implemented or changed.
+
+Each branding directory (``/browser/branding/*``) contains a CSS file for each page, which specifies how the page should be laid out and rendered. These are normal CSS files and can easily be modified to change or redesign one or both pages for any or all brandings. Currently there are two completely different layouts, one for release and another for all other brandings, and the two differ only in this CSS file and in the background image file (also in the branding directory).
+
+Constraints
+===========
+
+The most severe constraint on the web UI is that the browser engine must be configured to run in Internet Explorer 8 mode. This is because the installer must run correctly on an unpatched Windows 7 installation with no service packs or other updates, and that is the version of IE that such an installation provides. That means this constraint can only be relaxed if Firefox's OS support changes to exclude unpatched Windows 7 (i.e., either dropping Windows 7 entirely or requiring Service Pack 1).
+
+Image assets (meaning only the background image at the moment) can be any format IE8 can open, including JPEG, GIF, or PNG, but any newer formats like WebP won't work. SVG isn't supported until IE9.
+
+Fonts should be limited to those that a default Windows 7+ installation provides, which may vary based on locale (it is possible to specify a specific font to use for a certain locale).
+
+The size of the window is difficult to change, and doing so would affect all brandings and require redesigning the background art, so changing that should be avoided.
+
+Care should be taken to make sure that accessibility is maintained, and best practices for web accessibility should be followed. For example, the installing page contains a progress bar. Normally, and in keeping with best practices, this would be implemented using a ``<progress>`` element, but IE8 doesn't support those, so it has to be built manually out of divs instead. Because this is an inherently inaccessible design, appropriate ARIA attributes are used so that the control is presented to accessibility tools as a real progress bar.
+
+Technical Details
+=================
+
+Custom Functions
+----------------
+
+Custom functions provide a foreign function interface that allows JavaScript running in the web page to invoke NSIS functions. Since the installer logic is all handled in NSIS, this is necessary for the UI to be able to determine the state of the installation, relay any settings to the installer, etc. Custom functions always take a single argument on the NSIS stack, and return a single value on the NSIS stack (this design is mostly for convenience, to avoid needing the C++ glue layer to handle different numbers of intended arguments or return values).
+
+The implementation of custom functions is basically glue between the NSIS ExecuteCodeSegment mechanism (which allows executing NSIS functions from native plugin code) and the web browser control's support for IDispatch (which allows invoking native code from JavaScript). Connecting those two things together allows us to run NSIS code from within JavaScript.
+
+To make an NSIS function available to JavaScript, the NSIS script must call the plugin's ``RegisterCustomFunction`` export, passing it the address (as obtained from the ``GetFunctionAddress`` instruction) and the name that the function should be exposed to JavaScript under. This call has to happen after the call to ``Init`` and before ``ShowPage``. The plugin wraps that address in a native C++ function, and passes it to the WebBrowser control to register with its IDispatch implementation. That code allocates a ``DISPID`` for the new function and associates it with the given name. Later, when JavaScript invokes a function by that name, our ``IDispatch::Invoke`` calls the wrapper function with the argument that was passed in by JavaScript (if any), and makes sure the return value gets relayed back to JavaScript as well.
+
+
+
+.. [#1] There is currently a new control called `WebView2 <https://docs.microsoft.com/en-us/microsoft-edge/hosting/webview2>`_ in development which uses the Chrome-based Edge engine, but it's unfinished as of this writing and would only work on machines with Chrome-based Edge installed anyway.
+
+.. [#2] Previously, the stub installer's UI was based on nsDialogs, and used its timer implementation, so when nsDialogs was removed an alternative was needed. The replacement timers don't strictly need to be provided by the WebBrowser plugin, but it was the convenient place to put those functions.
+
+.. toctree::
+ WebBrowserAPI
diff --git a/browser/installer/windows/docs/StubInstaller.rst b/browser/installer/windows/docs/StubInstaller.rst
new file mode 100644
index 0000000000..112c036d24
--- /dev/null
+++ b/browser/installer/windows/docs/StubInstaller.rst
@@ -0,0 +1,13 @@
+==============
+Stub Installer
+==============
+
+The stub installer is the default installer interface that most users installing Firefox will see. It's a tiny download (200-300 KB), so it gets the user into the product experience quickly. It's also a highly streamlined experience; there are no options or prompts offered, except in the case of a returning user (see Profile Cleanup). Running the stub installer immediately starts downloading and installing the browser, and automatically runs the new installation and exits when it's done.
+
+An :ref:`Install Ping` is sent when the stub exits.
+
+
+.. toctree::
+ StubConfig
+ StubArch
+ StubGUI
diff --git a/browser/installer/windows/docs/WebBrowserAPI.rst b/browser/installer/windows/docs/WebBrowserAPI.rst
new file mode 100644
index 0000000000..2b16ea0178
--- /dev/null
+++ b/browser/installer/windows/docs/WebBrowserAPI.rst
@@ -0,0 +1,35 @@
+=====================
+WebBrowser Plugin API
+=====================
+
+This page lists all the functions exported from the WebBrowser plugin and explains how to use them.
+
+``ShowPage``
+ This function takes one parameter: the URL or file path to be displayed. It returns nothing.
+
+ ``ShowPage`` will block until the page is closed, either by closing the window or by something like sending the parent dialog a "go to page" message.
+
+ Dialog item 1018 will be taken over as the control to display the web page in, so if you're using a custom dialog template, be sure it contains a control with that ID.
+
+ ``file:`` URL's are supported, but passing in just the file path also works. Relative paths are supported but should be avoided because the stub installer's working directory isn't necessarily predictable or meaningful. Web (HTTP or HTTPS) URL's can be used, but the security restrictions the browser control is configured with will prevent most resource files from being downloaded, so they are unlikely to be very useful.
+
+``RegisterCustomFunction``
+ This function allows an NSIS function to be called from JavaScript.
+
+ This function takes two parameters, the function address, as obtained from the ``GetFunctionAddress`` instruction, and then a string containing the name of the function to be exposed to JavaScript. It returns nothing.
+
+ ``RegisterCustomFunction`` should be called before ``ShowPage`` (which will block anyway).
+
+ JavaScript can invoke this function by the assigned name under the global ``external`` object (that's part of the design of the control and isn't something we can change). The NSIS function will receive a single parameter on the NSIS stack (whether it needs one or not) and is expected to return a single value on the NSIS stack (whether it needs to or not) which will become the return value from the JavaScript function.
+
+ Currently only strings, integers, and booleans are supported as parameter types; anything else will result in the NSIS function being passed an empty string. More types could be added if needed. The return type will always be passed back to JavaScript directly as a string, and any conversions needed must be performed in JS.
+
+``CreateTimer``
+ This function creates an interval timer and invokes a callback at each interval.
+
+ This function takes two parameters, the function address of the timer callback, as obtained from the ``GetFunctionAddress`` instruction, and then the timer interval in milliseconds. It returns a handle which can later be passed to ``CancelTimer``.
+
+``CancelTimer``
+ This function stops running a timer that was started by ``CreateTimer``.
+
+ It takes the handle that ``CreateTimer`` returned, and returns nothing.
diff --git a/browser/installer/windows/docs/index.rst b/browser/installer/windows/docs/index.rst
new file mode 100644
index 0000000000..5cc72902d9
--- /dev/null
+++ b/browser/installer/windows/docs/index.rst
@@ -0,0 +1,21 @@
+=========
+Installer
+=========
+
+The main role of the Firefox installer is to get a user running Firefox as quickly and reliably as possible, while taking all necessary steps to make Firefox an integrated part of the system.
+
+It turns out that the only platform where we need an installer in order to accomplish all of that is Windows. So we only develop and ship and installer for Windows, and on other platforms we distribute a package in a non-executable format typical of applications distributed on that platform.
+
+Currently, the installers are built on the `NSIS <http://nsis.sourceforge.net/Main_Page>`_ installer framework. This is a Windows-only framework which compiles scripts written in its custom language (along with some native plugins) into an executable installer.
+
+We build four different kinds of installers, the :doc:`Stub Installer <StubInstaller>`, the :doc:`Full Installer <FullInstaller>`, an :doc:`MSI package <MSI>` which wraps the full installer, and an :doc:`MSIX package <MSIX>`. The stub is the default installer intended for most individual users, the full installer and MSI are aimed at power users and administrators who need more control, and the MSIX is aimed at enterprise users who need to control deployment.
+
+There's also another installer-related program, which is called :doc:`helper.exe <Helper>`. It's also written in NSIS and has a few different jobs that involve maintaining things that the installer sets up, including the uninstaller and a post-update routine.
+
+.. toctree::
+ InstallerBuild
+ StubInstaller
+ FullInstaller
+ MSI
+ MSIX
+ Helper
diff --git a/browser/installer/windows/moz.build b/browser/installer/windows/moz.build
new file mode 100644
index 0000000000..42eddc26c0
--- /dev/null
+++ b/browser/installer/windows/moz.build
@@ -0,0 +1,19 @@
+# -*- 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/.
+
+DEFINES["APP_VERSION"] = CONFIG["MOZ_APP_VERSION"]
+
+DEFINES["MOZ_APP_NAME"] = CONFIG["MOZ_APP_NAME"]
+DEFINES["MOZ_APP_DISPLAYNAME"] = CONFIG["MOZ_APP_DISPLAYNAME"]
+DEFINES["MOZILLA_VERSION"] = CONFIG["MOZILLA_VERSION"]
+
+# Turn `firefox` into `Firefox`.
+DEFINES["MOZ_TOAST_APP_NAME"] = "%s" % CONFIG["MOZ_APP_NAME"].title()
+
+if CONFIG["MOZ_DEFAULT_BROWSER_AGENT"]:
+ DEFINES["MOZ_DEFAULT_BROWSER_AGENT"] = CONFIG["MOZ_DEFAULT_BROWSER_AGENT"]
+
+SPHINX_TREES["installer"] = "docs"
diff --git a/browser/installer/windows/msi/installer.wxs b/browser/installer/windows/msi/installer.wxs
new file mode 100644
index 0000000000..0b5994c4c1
--- /dev/null
+++ b/browser/installer/windows/msi/installer.wxs
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+
+<!-- The version field only supports MSI version numbers, which cannot include
+ letters, and therefore cannot represent our version numbers. Set it to all
+ zeros to show it isn't valid, and add the real version to the Name. -->
+<Product Name="$(var.BrandFullName) $(var.Version) $(var.Architecture) $(var.AB_CD)"
+ Manufacturer="$(var.Vendor)" Language="0" Codepage="1252"
+ Version="$(var.EmbeddedVersionCode)" Id="1294a4c5-9977-480f-9497-c0ea1e630130"
+ UpgradeCode="3118ab4c-b433-4fbb-b9fa-8f9ca4b5c103" >
+
+ <Package Id="*" InstallerVersion="200" Compressed="yes"
+ Platform="$(var.Architecture)" InstallScope ="perMachine"/>
+
+ <!-- We need a CAB to avoid failing an ICE, even though we have no payload. -->
+ <Media Id="1" Cabinet="setup.cab" EmbedCab="yes" />
+
+ <!-- We need a component and feature, or msiexec will refuse to load us. -->
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="TempFolder">
+ <Component Id="EmptyComponent" Guid="55a76b76-7496-4b47-a7a6-c5fbdd5e51a4">
+ <CreateFolder />
+ </Component>
+ </Directory>
+ </Directory>
+
+ <!-- Setting the feature to level 0 marks it hidden, so it can't be installed.
+ That prevents getting this MSI registered as an installed product,
+ because it has no features of its own to install. -->
+ <Feature Id="EmptyFeature" Level="0">
+ <ComponentRef Id="EmptyComponent" />
+ </Feature>
+
+ <!-- Embed the installer we want to run directly into the MSI database. -->
+ <Binary Id="WrappedExe" SourceFile="$(var.ExeSourcePath)" />
+
+ <!-- User-configurable properties. One of these corresponds to each documented
+ command-line parameter. Properties cannot be present without a value,
+ so use a conspicuous and difficult to mistake string for the parameters
+ that have no real default values. -->
+ <Property Id="INSTALL_DIRECTORY_PATH" Value="__DEFAULT__" />
+ <Property Id="INSTALL_DIRECTORY_NAME" Value="__DEFAULT__" />
+ <Property Id="TASKBAR_SHORTCUT" Value="true" />
+ <Property Id="DESKTOP_SHORTCUT" Value="true" />
+ <Property Id="START_MENU_SHORTCUT" Value="true" />
+ <Property Id="PRIVATE_BROWSING_SHORTCUT" Value="true" />
+ <Property Id="INSTALL_MAINTENANCE_SERVICE" Value="true" />
+ <Property Id="REMOVE_DISTRIBUTION_DIR" Value="true" />
+ <Property Id="PREVENT_REBOOT_REQUIRED" Value="false" />
+ <Property Id="OPTIONAL_EXTENSIONS" Value="true" />
+ <Property Id="REGISTER_DEFAULT_AGENT" Value="true" />
+ <Property Id="EXTRACT_DIR" Value="__DEFAULT__" />
+
+ <!-- Always include all of the boolean options on the command line, so we don't
+ have to conditionally decide when to include each one of them. For the
+ directory settings though, we can't put them on the command line with the
+ default values those properties have, so we need a separate action for
+ each possible configuration of those settings, and conditions to select
+ the right action to use based on which properties are configured.
+ WiX throws warning LGHT1076 complaining that these command strings are
+ too long, but they actually work just fine, the warning is spurious. -->
+ <CustomAction Id="RunInstallNoDir" Return="check" Execute="deferred"
+ HideTarget="no" Impersonate="no" BinaryKey="WrappedExe"
+ ExeCommand="/S /TaskbarShortcut=[TASKBAR_SHORTCUT] /DesktopShortcut=[DESKTOP_SHORTCUT] /StartMenuShortcut=[START_MENU_SHORTCUT] /PrivateBrowsingShortcut=[PRIVATE_BROWSING_SHORTCUT] /MaintenanceService=[INSTALL_MAINTENANCE_SERVICE] /RemoveDistributionDir=[REMOVE_DISTRIBUTION_DIR] /PreventRebootRequired=[PREVENT_REBOOT_REQUIRED] /OptionalExtensions=[OPTIONAL_EXTENSIONS] /RegisterDefaultAgent=[REGISTER_DEFAULT_AGENT] /LaunchedFromMSI" />
+ <CustomAction Id="RunInstallDirPath" Return="check" Execute="deferred"
+ HideTarget="no" Impersonate="no" BinaryKey="WrappedExe"
+ ExeCommand="/S /InstallDirectoryPath=[INSTALL_DIRECTORY_PATH] /TaskbarShortcut=[TASKBAR_SHORTCUT] /DesktopShortcut=[DESKTOP_SHORTCUT] /StartMenuShortcut=[START_MENU_SHORTCUT] /PrivateBrowsingShortcut=[PRIVATE_BROWSING_SHORTCUT] /MaintenanceService=[INSTALL_MAINTENANCE_SERVICE] /RemoveDistributionDir=[REMOVE_DISTRIBUTION_DIR] /PreventRebootRequired=[PREVENT_REBOOT_REQUIRED] /OptionalExtensions=[OPTIONAL_EXTENSIONS] /RegisterDefaultAgent=[REGISTER_DEFAULT_AGENT] /LaunchedFromMSI" />
+ <CustomAction Id="RunInstallDirName" Return="check" Execute="deferred"
+ HideTarget="no" Impersonate="no" BinaryKey="WrappedExe"
+ ExeCommand="/S /InstallDirectoryName=[INSTALL_DIRECTORY_NAME] /TaskbarShortcut=[TASKBAR_SHORTCUT] /DesktopShortcut=[DESKTOP_SHORTCUT] /StartMenuShortcut=[START_MENU_SHORTCUT] /PrivateBrowsingShortcut=[PRIVATE_BROWSING_SHORTCUT] /MaintenanceService=[INSTALL_MAINTENANCE_SERVICE] /RemoveDistributionDir=[REMOVE_DISTRIBUTION_DIR] /PreventRebootRequired=[PREVENT_REBOOT_REQUIRED] /OptionalExtensions=[OPTIONAL_EXTENSIONS] /RegisterDefaultAgent=[REGISTER_DEFAULT_AGENT] /LaunchedFromMSI" />
+ <CustomAction Id="RunExtractOnly" Return="check" Execute="deferred"
+ HideTarget="no" Impersonate="no" BinaryKey="WrappedExe"
+ ExeCommand="/ExtractDir=[EXTRACT_DIR]" />
+
+ <!-- When we run the custom actions is kind of arbitrary; this sequencing gets
+ us the least confusing message showing in the MSI progress dialog while
+ the installer runs. Our actions don't need to be sequenced relative
+ to one another because only one will ever run. -->
+ <InstallExecuteSequence>
+ <Custom Action="RunInstallNoDir" After="ProcessComponents">
+ <![CDATA[
+ (INSTALL_DIRECTORY_PATH = "__DEFAULT__") AND
+ (INSTALL_DIRECTORY_NAME = "__DEFAULT__") AND
+ (EXTRACT_DIR = "__DEFAULT__")
+ ]]>
+ </Custom>
+ <Custom Action="RunInstallDirPath" After="ProcessComponents">
+ <![CDATA[
+ (INSTALL_DIRECTORY_PATH <> "__DEFAULT__") AND
+ (INSTALL_DIRECTORY_NAME = "__DEFAULT__") AND
+ (EXTRACT_DIR = "__DEFAULT__")
+ ]]>
+ </Custom>
+ <Custom Action="RunInstallDirName" After="ProcessComponents">
+ <![CDATA[
+ (INSTALL_DIRECTORY_NAME <> "__DEFAULT__") AND
+ (EXTRACT_DIR = "__DEFAULT__")
+ ]]>
+ </Custom>
+ <Custom Action="RunExtractOnly" After="ProcessComponents">
+ <![CDATA[
+ EXTRACT_DIR <> "__DEFAULT__"
+ ]]>
+ </Custom>
+ </InstallExecuteSequence>
+</Product>
+
+</Wix>
diff --git a/browser/installer/windows/msix/AppxManifest.xml.in b/browser/installer/windows/msix/AppxManifest.xml.in
new file mode 100644
index 0000000000..0f591fe43e
--- /dev/null
+++ b/browser/installer/windows/msix/AppxManifest.xml.in
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<!-- #filter substitution -->
+<Package
+ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+ xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
+ xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
+ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+ xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
+ xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
+ xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
+ xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
+ IgnorableNamespaces="uap uap2 uap3 uap10 rescap">
+
+ <Identity Name="@APPX_IDENTITY@" Publisher="@APPX_PUBLISHER@" Version="@APPX_VERSION@" ProcessorArchitecture="@APPX_ARCH@" />
+ <Properties>
+ <DisplayName>@APPX_DISPLAYNAME@</DisplayName>
+ <PublisherDisplayName>@APPX_PUBLISHER_DISPLAY_NAME@</PublisherDisplayName>
+ <Description>@APPX_DESCRIPTION@</Description>
+ <Logo>Assets\StoreLogo.png</Logo>
+ <uap10:PackageIntegrity>
+ <uap10:Content Enforcement="on" />
+ </uap10:PackageIntegrity>
+ </Properties>
+ <Resources>
+@APPX_RESOURCE_LANGUAGE_LIST@
+ </Resources>
+ <Dependencies>
+ <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22621.1555" />
+ </Dependencies>
+ <Capabilities>
+ <rescap:Capability Name="runFullTrust" />
+ </Capabilities>
+ <Applications>
+ <Application Id="App" Executable="VFS\ProgramFiles\@APPX_INSTDIR@\@MOZ_APP_NAME@.exe" EntryPoint="Windows.FullTrustApplication">
+ <uap:VisualElements BackgroundColor="#20123A" DisplayName="@MOZ_APP_DISPLAYNAME@" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="@MOZ_APP_DISPLAYNAME@">
+ <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square310x310Logo="Assets\LargeTile.png" Square71x71Logo="Assets\SmallTile.png">
+ <uap:ShowNameOnTiles>
+ <uap:ShowOn Tile="square150x150Logo"/>
+ <uap:ShowOn Tile="wide310x150Logo"/>
+ <uap:ShowOn Tile="square310x310Logo"/>
+ </uap:ShowNameOnTiles>
+ </uap:DefaultTile>
+ </uap:VisualElements>
+ <Extensions>
+ <uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication" Executable="VFS\ProgramFiles\@APPX_INSTDIR@\@MOZ_APP_NAME@.exe">
+ <uap3:AppExecutionAlias>
+ <desktop:ExecutionAlias Alias="@MOZ_APP_NAME@.exe" />
+ </uap3:AppExecutionAlias>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.fileTypeAssociation">
+ <uap3:FileTypeAssociation Name="htm">
+ <uap:SupportedFileTypes>
+ <!-- Keep synchronized with
+ https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/shared.nsh
+ and `os.environment.launched_to_handle` and `os.environment.invoked_to_handle` telemetry in
+ https://searchfox.org/mozilla-central/source/browser/components/BrowserContentHandler.sys.mjs. -->
+ <uap:FileType>.avif</uap:FileType>
+ <uap:FileType>.htm</uap:FileType>
+ <uap:FileType>.html</uap:FileType>
+ <uap:FileType>.pdf</uap:FileType>
+ <uap:FileType>.shtml</uap:FileType>
+ <uap:FileType>.xht</uap:FileType>
+ <uap:FileType>.xhtml</uap:FileType>
+ <uap:FileType>.svg</uap:FileType>
+ <uap:FileType>.webp</uap:FileType>
+ </uap:SupportedFileTypes>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ <uap2:SupportedVerbs>
+ <uap3:Verb Id="open" Parameters="-osint -url &quot;%1&quot;">open</uap3:Verb>
+ </uap2:SupportedVerbs>
+ </uap3:FileTypeAssociation>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.protocol">
+ <uap3:Protocol Name="http" Parameters="-osint -url &quot;%1&quot;">
+ <uap:DisplayName>http</uap:DisplayName>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ </uap3:Protocol>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.protocol">
+ <uap3:Protocol Name="https" Parameters="-osint -url &quot;%1&quot;">
+ <uap:DisplayName>https</uap:DisplayName>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ </uap3:Protocol>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.protocol">
+ <uap3:Protocol Name="mailto" Parameters="-osint -url &quot;%1&quot;">
+ <uap:DisplayName>mailto</uap:DisplayName>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ </uap3:Protocol>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.protocol">
+ <uap3:Protocol Name="firefox" Parameters="-osint -url &quot;%1&quot;">
+ <uap:DisplayName>Firefox Protocol</uap:DisplayName>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ </uap3:Protocol>
+ </uap3:Extension>
+ <uap3:Extension Category="windows.protocol">
+ <uap3:Protocol Name="firefox-private" Parameters="-osint -private-window &quot;%1&quot;">
+ <uap:DisplayName>Firefox Private Browsing Protocol</uap:DisplayName>
+ <uap:Logo>Assets\Document44x44.png</uap:Logo>
+ </uap3:Protocol>
+ </uap3:Extension>
+ <!-- COM registrations for the notification server. -->
+ <com:Extension Category="windows.comServer">
+ <com:ComServer>
+ <com:SurrogateServer DisplayName="NotificationServer"
+ AppId="@MOZ_INOTIFICATIONACTIVATION_CLSID@">
+ <com:Class Id="@MOZ_INOTIFICATIONACTIVATION_CLSID@"
+ Path="VFS\ProgramFiles\@APPX_INSTDIR@\notificationserver.dll"
+ ThreadingModel="Both" />
+ </com:SurrogateServer>
+ </com:ComServer>
+ </com:Extension>
+ <desktop:Extension Category="windows.toastNotificationActivation">
+ <desktop:ToastNotificationActivation ToastActivatorCLSID="@MOZ_INOTIFICATIONACTIVATION_CLSID@" />
+ </desktop:Extension>
+ </Extensions>
+ </Application>
+ </Applications>
+ <Extensions>
+ <!-- These COM registrations allow Windows/MSAA to access Firefox accessibility features. -->
+ <com:Extension Category="windows.comInterface">
+ <com:ComInterface>
+ <com:ProxyStub DisplayName="AccessibleMarshal"
+ Id="1814ceeb-49e2-407f-af99-fa755a7d2607"
+ Path="VFS\ProgramFiles\@APPX_INSTDIR@\AccessibleMarshal.dll" />
+ <com:Interface Id="4e747be5-2052-4265-8af0-8ecad7aad1c0"
+ ProxyStubClsid="1814ceeb-49e2-407f-af99-fa755a7d2607" />
+ <com:Interface Id="1814ceeb-49e2-407f-af99-fa755a7d2607"
+ ProxyStubClsid="1814ceeb-49e2-407f-af99-fa755a7d2607" />
+ <com:Interface Id="0d68d6d0-d93d-4d08-a30d-f00dd1f45b24"
+ ProxyStubClsid="1814ceeb-49e2-407f-af99-fa755a7d2607" />
+ </com:ComInterface>
+ </com:Extension>
+ </Extensions>
+</Package>
diff --git a/browser/installer/windows/msix/Resources.pri b/browser/installer/windows/msix/Resources.pri
new file mode 100644
index 0000000000..887097ea3e
--- /dev/null
+++ b/browser/installer/windows/msix/Resources.pri
Binary files differ
diff --git a/browser/installer/windows/msix/distribution/distribution.ini b/browser/installer/windows/msix/distribution/distribution.ini
new file mode 100644
index 0000000000..b39f8df795
--- /dev/null
+++ b/browser/installer/windows/msix/distribution/distribution.ini
@@ -0,0 +1,12 @@
+# Partner Distribution Configuration File
+# Author: Mozilla
+# Date: 2021-07-19
+
+[Global]
+id=mozilla-MSIX
+version=1.0
+about=Windows MSIX package
+
+[Preferences]
+intl.locale.requested=""
+intl.multilingual.enabled=true
diff --git a/browser/installer/windows/msix/msix-all-locales b/browser/installer/windows/msix/msix-all-locales
new file mode 100644
index 0000000000..8d73ee6312
--- /dev/null
+++ b/browser/installer/windows/msix/msix-all-locales
@@ -0,0 +1,361 @@
+# Locale codes supported by Microsoft Windows and MSIX packages.
+#
+# From https://docs.microsoft.com/en-us/windows/uwp/publish/supported-languages.
+# Fetched on September 15, 2021.
+#
+# The following codes are listed as supported but for reasons unknown, Windows won't install
+# packages advertising them:
+#
+# sr* (Serbian)
+# uz* (Uzbek)
+
+af
+af-za
+am
+am-et
+ar
+ar-ae
+ar-bh
+ar-dz
+ar-eg
+ar-iq
+ar-jo
+ar-kw
+ar-lb
+ar-ly
+ar-ma
+ar-om
+ar-qa
+ar-sa
+ar-sy
+ar-tn
+ar-ye
+as
+as-in
+az-arab
+az-arab-az
+az-cyrl
+az-cyrl-az
+az-latn
+az-latn-az
+be
+be-by
+bg
+bg-bg
+bn
+bn-bd
+bn-in
+bs
+bs-cyrl
+bs-cyrl-ba
+bs-latn
+bs-latn-ba
+ca
+ca-es
+ca-es-valencia
+chr-cher
+chr-cher-us
+chr-latn
+cs
+cs-cz
+cy
+cy-gb
+da
+da-dk
+de
+de-at
+de-ch
+de-de
+de-li
+de-lu
+el
+el-gr
+en
+en-011
+en-014
+en-018
+en-021
+en-029
+en-053
+en-au
+en-bz
+en-ca
+en-gb
+en-hk
+en-id
+en-ie
+en-in
+en-jm
+en-kz
+en-mt
+en-my
+en-nz
+en-ph
+en-pk
+en-sg
+en-tt
+en-us
+en-vn
+en-za
+en-zw
+es
+es-019
+es-419
+es-ar
+es-bo
+es-cl
+es-co
+es-cr
+es-do
+es-ec
+es-es
+es-gt
+es-hn
+es-mx
+es-ni
+es-pa
+es-pe
+es-pr
+es-py
+es-sv
+es-us
+es-uy
+es-ve
+et
+et-ee
+eu
+eu-es
+fa
+fa-ir
+fi
+fi-fi
+fil
+fil-latn
+fil-ph
+fr
+fr-011
+fr-015
+fr-021
+fr-029
+fr-155
+fr-be
+fr-ca
+fr-cd
+fr-ch
+fr-ci
+fr-cm
+fr-fr
+fr-ht
+fr-lu
+fr-ma
+fr-mc
+fr-ml
+fr-re
+frc-latn
+frp-latn
+ga
+ga-ie
+gd-gb
+gd-latn
+gl
+gl-es
+gu
+gu-in
+ha
+ha-latn
+ha-latn-ng
+he
+he-il
+hi
+hi-in
+hr
+hr-ba
+hr-hr
+hu
+hu-hu
+hy
+hy-am
+id
+id-id
+ig-latn
+ig-ng
+is
+is-is
+it
+it-ch
+it-it
+iu-cans
+iu-latn
+iu-latn-ca
+ja
+ja-jp
+ka
+ka-ge
+kk
+kk-kz
+km
+km-kh
+kn
+kn-in
+ko
+ko-kr
+kok
+kok-in
+ku-arab
+ku-arab-iq
+ky-cyrl
+ky-kg
+lb
+lb-lu
+lo
+lo-la
+lt
+lt-lt
+lv
+lv-lv
+mi
+mi-latn
+mi-nz
+mk
+mk-mk
+ml
+ml-in
+mn-cyrl
+mn-mn
+mn-mong
+mn-phag
+mr
+mr-in
+ms
+ms-bn
+ms-my
+mt
+mt-mt
+nb
+nb-no
+ne
+ne-np
+nl
+nl-be
+nl-nl
+nn
+nn-no
+no
+no-no
+nso
+nso-za
+or
+or-in
+pa
+pa-arab
+pa-arab-pk
+pa-deva
+pa-in
+pl
+pl-pl
+prs
+prs-af
+prs-arab
+pt
+pt-br
+pt-pt
+quc-latn
+qut-gt
+qut-latn
+quz
+quz-bo
+quz-ec
+quz-pe
+ro
+ro-ro
+ru
+ru-ru
+rw
+rw-rw
+sd-arab
+sd-arab-pk
+sd-deva
+si
+si-lk
+sk
+sk-sk
+sl
+sl-si
+sq
+sq-al
+# sr # For reasons unknown, Windows won't install packages advertising these locales.
+# sr-Latn
+# sr-cyrl
+# sr-cyrl-ba
+# sr-cyrl-cs
+# sr-cyrl-me
+# sr-cyrl-rs
+# sr-latn-ba
+# sr-latn-cs
+# sr-latn-me
+# sr-latn-rs
+sv
+sv-fi
+sv-se
+sw
+sw-ke
+ta
+ta-in
+te
+te-in
+tg-arab
+tg-cyrl
+tg-cyrl-tj
+tg-latn
+th
+th-th
+ti
+ti-et
+tk-cyrl
+tk-cyrl-tr
+tk-latn
+tk-latn-tr
+tk-tm
+tn
+tn-bw
+tn-za
+tr
+tr-tr
+tt-arab
+tt-cyrl
+tt-latn
+tt-ru
+ug-arab
+ug-cn
+ug-cyrl
+ug-latn
+uk
+uk-ua
+ur
+ur-pk
+# uz # For reasons unknown, Windows won't install packages advertising these locales.
+# uz-cyrl
+# uz-latn
+# uz-latn-uz
+vi
+vi-vn
+wo
+wo-sn
+xh
+xh-za
+yo-latn
+yo-ng
+zh-Hans
+zh-Hant
+zh-cn
+zh-hans-cn
+zh-hans-sg
+zh-hant-hk
+zh-hant-mo
+zh-hant-tw
+zh-hk
+zh-mo
+zh-sg
+zh-tw
+zu
+zu-za
diff --git a/browser/installer/windows/msix/priconfig.xml b/browser/installer/windows/msix/priconfig.xml
new file mode 100644
index 0000000000..62f0668d5f
--- /dev/null
+++ b/browser/installer/windows/msix/priconfig.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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/. -->
+
+<resources targetOsVersion="10.0.0" majorVersion="1">
+ <!-- Commenting this out yields a single `.pri` declaring all scales. See
+ https://stackoverflow.com/a/42975692. -->
+ <!-- <packaging> -->
+ <!-- <autoResourcePackage qualifier="Language"/> -->
+ <!-- <autoResourcePackage qualifier="Scale"/> -->
+ <!-- <autoResourcePackage qualifier="DXFeatureLevel"/> -->
+ <!-- </packaging> -->
+ <index root="\" startIndexAt="\">
+ <default>
+ <qualifier name="Language" value="en-US"/>
+ <qualifier name="Contrast" value="standard"/>
+ <qualifier name="Scale" value="100"/>
+ <qualifier name="HomeRegion" value="001"/>
+ <qualifier name="TargetSize" value="256"/>
+ <qualifier name="LayoutDirection" value="LTR"/>
+ <qualifier name="Theme" value="dark"/>
+ <qualifier name="AlternateForm" value=""/>
+ <qualifier name="DXFeatureLevel" value="DX9"/>
+ <qualifier name="Configuration" value=""/>
+ <qualifier name="DeviceFamily" value="Universal"/>
+ <qualifier name="Custom" value=""/>
+ </default>
+ <indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="."/>
+ <indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/>
+ <indexer-config type="resjson" initialPath=""/>
+ <indexer-config type="PRI"/>
+ </index>
+ <!--<index startIndexAt="Start Index Here" root="Root Here">-->
+ <!-- <indexer-config type="resfiles" qualifierDelimiter="."/>-->
+ <!-- <indexer-config type="priinfo" emitStrings="true" emitPaths="true" emitEmbeddedData="true"/>-->
+ <!--</index>-->
+</resources>
diff --git a/browser/installer/windows/nsis/content/installing.html b/browser/installer/windows/nsis/content/installing.html
new file mode 100644
index 0000000000..3fcc783636
--- /dev/null
+++ b/browser/installer/windows/nsis/content/installing.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+<!-- 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/. -->
+
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=8" />
+
+ <link rel="stylesheet" href="stub_common.css" />
+ <link rel="stylesheet" href="installing_page.css" />
+
+ <script src="stub_common.js"></script>
+ <script src="installing.js"></script>
+ </head>
+ <body>
+ <img id="background" src="bgstub.jpg" alt="" role="presentation" />
+
+ <div id="text_column">
+ <div id="text_column_container">
+ <h1 id="header"></h1>
+ <div id="content"></div>
+ </div>
+ </div>
+
+ <div id="installing">
+ <div id="label" tabindex="0"></div>
+ <div id="progress_background">
+ <div
+ id="progress_bar"
+ role="progressbar"
+ aria-labelledby="label"
+ aria-valuemin="0"
+ aria-valuemax="100"
+ aria-valuenow="0"
+ tabindex="0"
+ ></div>
+ </div>
+ </div>
+
+ <div id="blurb"></div>
+
+ <div id="footer"></div>
+ </body>
+</html>
diff --git a/browser/installer/windows/nsis/content/installing.js b/browser/installer/windows/nsis/content/installing.js
new file mode 100644
index 0000000000..97105cf6df
--- /dev/null
+++ b/browser/installer/windows/nsis/content/installing.js
@@ -0,0 +1,56 @@
+// 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/.
+
+// Length of time (milliseconds) that one blurb stays up before we switch to
+// displaying the next one.
+var BLURB_CYCLE_MS = 20000;
+
+// How frequently we should update the progress bar state, in milliseconds.
+var PROGRESS_BAR_INTERVAL_MS = 250;
+
+window.attachEvent("onload", function () {
+ // Set direction on the two components of the layout.
+ var direction = external.getTextDirection();
+ document.getElementById("text_column").style.direction = direction;
+ document.getElementById("installing").style.direction = direction;
+
+ // Get this page's static strings.
+ var label = document.getElementById("label");
+ label.innerText = external.getUIString("installing_label");
+ document.getElementById("header").innerText =
+ external.getUIString("installing_header");
+ document.getElementById("content").innerText =
+ external.getUIString("installing_content");
+
+ // Poll and update the progress bar percentage.
+ setInterval(function () {
+ var percent = external.getProgressBarPercent();
+ var progressBar = document.getElementById("progress_bar");
+ progressBar.setAttribute("aria-valuenow", percent);
+ progressBar.style.width = percent + "%";
+ }, PROGRESS_BAR_INTERVAL_MS);
+
+ // Get the blurb strings and initialize the blurb rotation.
+ var currentBlurb = 0;
+ // IE8 adds undefined to the array if there is a trailing comma in an
+ // array literal, so don't allow prettier to add one here.
+ // prettier-ignore
+ var blurbStrings = [
+ external.getUIString("installing_blurb_0"),
+ external.getUIString("installing_blurb_1"),
+ external.getUIString("installing_blurb_2")
+ ];
+ function rotateBlurb() {
+ document.getElementById("blurb").innerText = blurbStrings[currentBlurb];
+ currentBlurb = (currentBlurb + 1) % blurbStrings.length;
+ }
+ rotateBlurb();
+ setInterval(rotateBlurb, BLURB_CYCLE_MS);
+
+ // Focus the label, in order to get the focus in the web page, to
+ // assist screen readers. On Win 7's IE8 this causes the focus rectangle
+ // to be immediately visible, so also hide that here.
+ label.className += " no-focus-outline";
+ label.focus();
+});
diff --git a/browser/installer/windows/nsis/content/profile_cleanup.html b/browser/installer/windows/nsis/content/profile_cleanup.html
new file mode 100644
index 0000000000..77fd1e7be8
--- /dev/null
+++ b/browser/installer/windows/nsis/content/profile_cleanup.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<!-- 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/. -->
+
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=8" />
+
+ <link rel="stylesheet" href="stub_common.css" />
+ <link rel="stylesheet" href="profile_cleanup_page.css" />
+
+ <script src="stub_common.js"></script>
+ <script src="profile_cleanup.js"></script>
+ </head>
+ <body>
+ <img id="background" src="bgstub.jpg" alt="" role="presentation" />
+
+ <form id="profileRefreshForm">
+ <div id="profileRefreshContainer">
+ <h1 id="header"></h1>
+
+ <div id="refreshCheckboxContainer">
+ <input id="refreshCheckbox" type="checkbox" checked />
+ <label id="checkboxLabel" for="refreshCheckbox"></label>
+ </div>
+
+ <div id="refreshButtonContainer">
+ <button type="submit" id="refreshButton"></button>
+ </div>
+ </div>
+ </form>
+
+ <div id="footer"></div>
+ </body>
+</html>
diff --git a/browser/installer/windows/nsis/content/profile_cleanup.js b/browser/installer/windows/nsis/content/profile_cleanup.js
new file mode 100644
index 0000000000..8c90b9fca8
--- /dev/null
+++ b/browser/installer/windows/nsis/content/profile_cleanup.js
@@ -0,0 +1,28 @@
+// 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/.
+
+window.attachEvent("onload", function () {
+ // Set text direction.
+ var direction = external.getTextDirection();
+ var profileRefreshForm = document.getElementById("profileRefreshForm");
+ profileRefreshForm.style.direction = direction;
+ var checkboxLabel = document.getElementById("checkboxLabel");
+ checkboxLabel.className += " checkboxLabel-" + direction;
+
+ // Get this page's static strings.
+ document.getElementById("header").innerText =
+ external.getUIString("cleanup_header");
+ document.getElementById("refreshButton").innerText =
+ external.getUIString("cleanup_button");
+ checkboxLabel.innerText = external.getUIString("cleanup_checkbox");
+
+ // Set up the confirmation button.
+ profileRefreshForm.attachEvent("onsubmit", function () {
+ var doProfileCleanup = document.getElementById("refreshCheckbox").checked;
+ external.gotoInstallPage(doProfileCleanup);
+ return false;
+ });
+
+ document.getElementById("refreshButton").focus();
+});
diff --git a/browser/installer/windows/nsis/content/stub_common.css b/browser/installer/windows/nsis/content/stub_common.css
new file mode 100644
index 0000000000..a8d15512b5
--- /dev/null
+++ b/browser/installer/windows/nsis/content/stub_common.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+body {
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+
+ font-family: "Segoe UI", sans-serif;
+}
+
+/* This is an <img> rather than using background-image because IE8
+ * does not support background-size. */
+#background {
+ min-height: 100%;
+ min-width: 100%;
+
+ width: 100%;
+ height: auto;
+
+ position: fixed;
+ top: 0;
+ left: 0;
+
+ z-index: -1;
+}
+
+body.high-contrast #background {
+ display: none;
+}
+
+.no-focus-outline {
+ outline: none;
+}
diff --git a/browser/installer/windows/nsis/content/stub_common.js b/browser/installer/windows/nsis/content/stub_common.js
new file mode 100644
index 0000000000..5ecceefc1c
--- /dev/null
+++ b/browser/installer/windows/nsis/content/stub_common.js
@@ -0,0 +1,22 @@
+// 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/.
+
+window.attachEvent("onload", function () {
+ if (parseInt(external.getIsHighContrast())) {
+ document.body.className += " high-contrast";
+ } else {
+ document.body.className += " normal-contrast";
+ }
+
+ document.body.style.fontFamily = external.getFontName() + ", sans-serif";
+
+ // All pages have the global footer (or don't, depending on the branding).
+ document.getElementById("footer").innerText =
+ external.getUIString("global_footer");
+
+ // Disallow dragging of the "background" image.
+ document.getElementById("background").attachEvent("ondragstart", function () {
+ return false;
+ });
+});
diff --git a/browser/installer/windows/nsis/defines.nsi.in b/browser/installer/windows/nsis/defines.nsi.in
new file mode 100644
index 0000000000..cbcb2e9be0
--- /dev/null
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -0,0 +1,154 @@
+#filter substitution
+# 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/.
+
+# Defining FunnelcakeVersion will append the value of StubURLVersionAppend to
+# StubURLVersion, append the value of URLManualDownloadAppend to
+# URLManualDownload, and append the value of URLStubDownloadAppend to
+# URLStubDownload. The value of FunnelcakeVersion should not be defined when it
+# is not used and when it is defined its value should never be empty.
+# !define FunnelcakeVersion "999"
+
+!ifdef FunnelcakeVersion
+!define URLManualDownloadAppend "&f=${FunnelcakeVersion}"
+!define URLStubDownloadAppend "-f${FunnelcakeVersion}"
+!define StubURLVersionAppend "-${FunnelcakeVersion}"
+!else
+!define URLManualDownloadAppend ""
+!define URLStubDownloadAppend ""
+!define StubURLVersionAppend ""
+!endif
+
+# These defines should match application.ini settings
+!define AppName "Firefox"
+!define AppVersion "@APP_VERSION@"
+!define GREVersion @MOZILLA_VERSION@
+!define AB_CD "@AB_CD@"
+
+!define FileMainEXE "@MOZ_APP_NAME@.exe"
+!define PrivateBrowsingEXE "private_browsing.exe"
+!define MainWindowClass "MozillaWindowClass"
+!define DialogWindowClass "MozillaDialogClass"
+!define DDEApplication "Firefox"
+!define AppRegName "Firefox"
+!define ToastAumidPrefix "@MOZ_TOAST_APP_NAME@Toast-"
+
+!define BrandProductName "Firefox"
+!ifndef DEV_EDITION
+!define BrandShortName "@MOZ_APP_DISPLAYNAME@"
+!endif
+!ifndef BrandFullName
+!define BrandFullName "${BrandFullNameInternal}"
+!endif
+
+; The C++ defines in `nsNativeAppSupportWin.h` are 1-based. The Windows
+; resource IDs in the registry are 0-based.
+!define IDI_APPICON_ZERO_BASED "0"
+!define IDI_DOCUMENT_ZERO_BASED "1"
+!define IDI_PBICON_ZERO_BASED "4"
+!define IDI_DOCUMENT_PDF_ZERO_BASED "5"
+
+; This one is the location of the Private Browsing icon in
+; private_browsing.exe (as opposed to @MOZ_APP_NAME@.exe)
+!define IDI_PBICON_PB_EXE_ZERO_BASED "0"
+
+!define CERTIFICATE_NAME "Mozilla Corporation"
+!define CERTIFICATE_ISSUER "DigiCert SHA2 Assured ID Code Signing CA"
+; Changing the name or issuer requires us to have both the old and the new
+; in the registry at the same time, temporarily.
+!define CERTIFICATE_NAME_PREVIOUS "Mozilla Corporation"
+!define CERTIFICATE_ISSUER_PREVIOUS "DigiCert Assured ID Code Signing CA-1"
+
+# LSP_CATEGORIES is the permitted LSP categories for the application. Each LSP
+# category value is ANDed together to set multiple permitted categories.
+# See http://msdn.microsoft.com/en-us/library/ms742253%28VS.85%29.aspx
+# The value below removes all LSP categories previously set.
+!define LSP_CATEGORIES "0x00000000"
+
+!if "@MOZ_UPDATE_CHANNEL@" == ""
+!define UpdateChannel "Unknown"
+!else
+!define UpdateChannel "@MOZ_UPDATE_CHANNEL@"
+!endif
+
+#ifdef MOZ_LAUNCHER_PROCESS
+!define MOZ_LAUNCHER_PROCESS
+!define MOZ_LAUNCHER_SUBKEY "Software\Mozilla\${AppName}\Launcher"
+#endif
+
+#ifdef RELEASE_OR_BETA
+!define RELEASE_OR_BETA
+#endif
+
+# Due to official and beta using the same branding this is needed to
+# differentiante between the url used by the stub for downloading.
+!if "@MOZ_UPDATE_CHANNEL@" == "beta"
+!define BETA_UPDATE_CHANNEL
+!endif
+
+!define BaseURLStubPing "http://download-stats.mozilla.org/stub"
+
+# ARCH is used when it is necessary to differentiate the x64 registry keys from
+# the x86 registry keys (e.g. the uninstall registry key).
+#ifdef HAVE_64BIT_BUILD
+!define HAVE_64BIT_BUILD
+#ifdef _ARM64_
+!define ARCH "AArch64"
+!define MinSupportedVer "Microsoft Windows 10 for ARM"
+#else
+!define ARCH "x64"
+!define MinSupportedVer "Microsoft Windows 10 x64"
+#endif
+#else
+!define MinSupportedVer "Microsoft Windows 10"
+!define ARCH "x86"
+#endif
+
+!define MinSupportedCPU "SSE2"
+
+#ifdef MOZ_MAINTENANCE_SERVICE
+!define MOZ_MAINTENANCE_SERVICE
+#endif
+
+#ifdef MOZ_BITS_DOWNLOAD
+!define MOZ_BITS_DOWNLOAD
+#endif
+
+#ifdef MOZ_DEFAULT_BROWSER_AGENT
+!define MOZ_DEFAULT_BROWSER_AGENT
+#endif
+
+!if "@AB_CD@" == "en-US"
+!define MOZ_OPTIONAL_EXTENSIONS
+!else if "@AB_CD@" == "zh-CN"
+!define MOZ_OPTIONAL_EXTENSIONS
+!endif
+
+# To add Private Browsing shortcut argument to setup.exe
+!define MOZ_PRIVATE_BROWSING
+
+# File details shared by both the installer and uninstaller
+VIProductVersion "1.0.0.0"
+VIAddVersionKey "ProductName" "${BrandShortName}"
+VIAddVersionKey "CompanyName" "${CompanyName}"
+VIAddVersionKey "LegalCopyright" "${CompanyName}"
+VIAddVersionKey "FileVersion" "${AppVersion}"
+VIAddVersionKey "ProductVersion" "${AppVersion}"
+# Comments is not used but left below commented out for future reference
+# VIAddVersionKey "Comments" "Comments"
+
+# It isn't possible to get the size of the installation prior to downloading
+# so the stub installer uses an estimate. The size is derived from the size of
+# the complete installer, the size of the extracted complete installer, and at
+# least 15 MB additional for working room.
+!define APPROXIMATE_REQUIRED_SPACE_MB "145"
+
+# Constants for parts of the telemetry submission URL
+!define TELEMETRY_BASE_URL https://incoming.telemetry.mozilla.org/submit
+!define TELEMETRY_NAMESPACE firefox-installer
+!define TELEMETRY_INSTALL_PING_VERSION 1
+!define TELEMETRY_INSTALL_PING_DOCTYPE install
+
+!define TELEMETRY_UNINSTALL_PING_NAMESPACE telemetry
+!define TELEMETRY_UNINSTALL_PING_DOCTYPE uninstall
diff --git a/browser/installer/windows/nsis/extensionsLocale.nsh b/browser/installer/windows/nsis/extensionsLocale.nsh
new file mode 100644
index 0000000000..5a24373bd0
--- /dev/null
+++ b/browser/installer/windows/nsis/extensionsLocale.nsh
@@ -0,0 +1,20 @@
+# 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/.
+
+; Strings for the optional extensions page. This is not in the locale
+; directory so these strings are only translated to zh-CN.
+
+!if "${AB_CD}" == "en-US"
+LangString EXTENSIONS_PAGE_TITLE 0 "Install Optional Extensions"
+LangString EXTENSIONS_PAGE_SUBTITLE 0 "$ExtensionRecommender recommends these extensions"
+LangString OPTIONAL_EXTENSIONS_CHECKBOX_DESC 0 "Install &Extension:"
+LangString OPTIONAL_EXTENSIONS_DESC 0 "You can add or remove these extensions at any time. Click the menu button and choose “Add-ons”."
+!endif
+
+!if "${AB_CD}" == "zh-CN"
+LangString EXTENSIONS_PAGE_TITLE 0 "安装可选扩展"
+LangString EXTENSIONS_PAGE_SUBTITLE 0 "$ExtensionRecommender 推荐安装以下扩展"
+LangString OPTIONAL_EXTENSIONS_CHECKBOX_DESC 0 "安装扩展(&E):"
+LangString OPTIONAL_EXTENSIONS_DESC 0 "您随时可以点击浏览器的菜单按钮并选择“附加组件”来添加或移除扩展。"
+!endif \ No newline at end of file
diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi
new file mode 100755
index 0000000000..7ec5f9fe6f
--- /dev/null
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -0,0 +1,1967 @@
+# 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/.
+
+# Required Plugins:
+# AccessControl
+# https://nsis.sourceforge.io/AccessControl_plug-in
+# AppAssocReg
+# http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# ApplicationID
+# http://nsis.sourceforge.net/ApplicationID_plug-in
+# CityHash
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# nsJSON
+# http://nsis.sourceforge.net/NsJSON_plug-in
+# ShellLink
+# http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC
+# http://nsis.sourceforge.net/UAC_plug-in
+# ServicesHelper
+# Mozilla specific plugin that is located in /other-licenses/nsis
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+Var TmpVal
+Var InstallType
+Var AddStartMenuSC
+Var AddTaskbarSC
+Var AddDesktopSC
+Var AddPrivateBrowsingSC
+Var InstallMaintenanceService
+Var InstallOptionalExtensions
+Var ExtensionRecommender
+Var PageName
+Var PreventRebootRequired
+Var RegisterDefaultAgent
+
+; Telemetry ping fields
+Var SetAsDefault
+Var HadOldInstall
+Var InstallExisted
+Var DefaultInstDir
+Var IntroPhaseStart
+Var OptionsPhaseStart
+Var InstallPhaseStart
+Var FinishPhaseStart
+Var FinishPhaseEnd
+Var InstallResult
+Var LaunchedNewApp
+Var PostSigningData
+
+; By defining NO_STARTMENU_DIR an installer that doesn't provide an option for
+; an application's Start Menu PROGRAMS directory and doesn't define the
+; StartMenuDir variable can use the common InstallOnInitCommon macro.
+!define NO_STARTMENU_DIR
+
+; Attempt to elevate Standard Users in addition to users that
+; are a member of the Administrators group.
+!define NONADMIN_ELEVATE
+
+!define AbortSurveyURL "http://www.kampyle.com/feedback_form/ff-feedback-form.php?site_code=8166124&form_id=12116&url="
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetOptions
+!insertmacro GetParameters
+!insertmacro GetSize
+!insertmacro StrFilter
+!insertmacro WordFind
+!insertmacro WordReplace
+
+; The following includes are custom.
+!include branding.nsi
+!include defines.nsi
+!include common.nsh
+!include locales.nsi
+
+VIAddVersionKey "FileDescription" "${BrandShortName} Installer"
+VIAddVersionKey "OriginalFilename" "setup.exe"
+
+; Must be inserted before other macros that use logging
+!insertmacro _LoggingCommon
+
+!insertmacro AddDisabledDDEHandlerValues
+!insertmacro ChangeMUIHeaderImage
+!insertmacro ChangeMUISidebarImage
+!insertmacro CheckForFilesInUse
+!insertmacro CleanMaintenanceServiceLogs
+!insertmacro CopyFilesFromDir
+!insertmacro CopyPostSigningData
+!insertmacro CreateRegKey
+!insertmacro GetFirstInstallPath
+!insertmacro GetLongPath
+!insertmacro GetPathFromString
+!insertmacro GetParent
+!insertmacro InitHashAppModelId
+!insertmacro IsHandlerForInstallDir
+!insertmacro IsPinnedToTaskBar
+!insertmacro IsUserAdmin
+!insertmacro LogDesktopShortcut
+!insertmacro LogQuickLaunchShortcut
+!insertmacro LogStartMenuShortcut
+!insertmacro ManualCloseAppPrompt
+!insertmacro PinnedToStartMenuLnkCount
+!insertmacro RegCleanAppHandler
+!insertmacro RegCleanMain
+!insertmacro RegCleanUninstall
+!insertmacro RemovePrecompleteEntries
+!insertmacro SetAppLSPCategories
+!insertmacro SetBrandNameVars
+!insertmacro UpdateShortcutAppModelIDs
+!insertmacro UnloadUAC
+!insertmacro WriteRegStr2
+!insertmacro WriteRegDWORD2
+
+; This needs to be inserted after InitHashAppModelId because it uses
+; $AppUserModelID and the compiler can't handle using variables lexically before
+; they've been declared.
+!insertmacro GetInstallerRegistryPref
+
+!include shared.nsh
+
+; Helper macros for ui callbacks. Insert these after shared.nsh
+!insertmacro CheckCustomCommon
+!insertmacro InstallEndCleanupCommon
+!insertmacro InstallOnInitCommon
+!insertmacro InstallStartCleanupCommon
+!insertmacro LeaveDirectoryCommon
+!insertmacro LeaveOptionsCommon
+!insertmacro OnEndCommon
+!insertmacro PreDirectoryCommon
+
+Name "${BrandFullName}"
+OutFile "setup.exe"
+!ifdef HAVE_64BIT_BUILD
+ InstallDir "$PROGRAMFILES64\${BrandFullName}\"
+!else
+ InstallDir "$PROGRAMFILES32\${BrandFullName}\"
+!endif
+ShowInstDetails nevershow
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MOZ_MUI_CUSTOM_ABORT
+!define MUI_CUSTOMFUNCTION_ABORT "CustomAbort"
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_WELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+; By default MUI_BGCOLOR is hardcoded to FFFFFF, which is only correct if the
+; Windows theme or high-contrast mode hasn't changed it, so we need to
+; override that with GetSysColor(COLOR_WINDOW) (this string ends up getting
+; passed to SetCtlColors, which uses this custom syntax to mean that).
+!define MUI_BGCOLOR SYSCLR:WINDOW
+
+; Use a right to left header image when the language is right to left
+!ifdef ${AB_CD}_rtl
+!define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
+!else
+!define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
+!endif
+
+/**
+ * Installation Pages
+ */
+; Welcome Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW showWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveWelcome
+!insertmacro MUI_PAGE_WELCOME
+
+; Custom Options Page
+Page custom preOptions leaveOptions
+
+; Select Install Directory Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preDirectory
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveDirectory
+!define MUI_DIRECTORYPAGE_VERIFYONLEAVE
+!insertmacro MUI_PAGE_DIRECTORY
+
+; Custom Components Page
+!ifdef MOZ_MAINTENANCE_SERVICE
+Page custom preComponents leaveComponents
+!endif
+
+; Custom Shortcuts Page
+Page custom preShortcuts leaveShortcuts
+
+; Custom Extensions Page
+!ifdef MOZ_OPTIONAL_EXTENSIONS
+Page custom preExtensions leaveExtensions
+!endif
+
+; Custom Summary Page
+Page custom preSummary leaveSummary
+
+; Install Files Page
+!insertmacro MUI_PAGE_INSTFILES
+
+; Finish Page
+!define MUI_FINISHPAGE_TITLE_3LINES
+!define MUI_FINISHPAGE_RUN
+!define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp
+!define MUI_FINISHPAGE_RUN_TEXT $(LAUNCH_TEXT)
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preFinish
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW showFinish
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE postFinish
+!insertmacro MUI_PAGE_FINISH
+
+; Use the default dialog for IDD_VERIFY for a simple Banner
+ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
+
+################################################################################
+# Install Sections
+
+; Cleanup operations to perform at the start of the installation.
+Section "-InstallStartCleanup"
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $InstallPhaseStart
+
+ SetDetailsPrint both
+ DetailPrint $(STATUS_CLEANUP)
+ SetDetailsPrint none
+
+ SetOutPath "$INSTDIR"
+ ${StartInstallLog} "${BrandFullName}" "${AB_CD}" "${AppVersion}" "${GREVersion}"
+
+ StrCpy $R9 "true"
+ StrCpy $PreventRebootRequired "false"
+ ${GetParameters} $R8
+ ${GetOptions} "$R8" "/INI=" $R7
+ ${Unless} ${Errors}
+ ; The configuration file must also exist
+ ${If} ${FileExists} "$R7"
+ ReadINIStr $R9 $R7 "Install" "RemoveDistributionDir"
+ ReadINIStr $R8 $R7 "Install" "PreventRebootRequired"
+ ${If} $R8 == "true"
+ StrCpy $PreventRebootRequired "true"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+
+ ${GetParameters} $R8
+ ${InstallGetOption} $R8 "RemoveDistributionDir" $R9
+ ${If} $R9 == "0"
+ StrCpy $R9 "false"
+ ${EndIf}
+ ${InstallGetOption} $R8 "PreventRebootRequired" $PreventRebootRequired
+ ${If} $PreventRebootRequired == "1"
+ StrCpy $PreventRebootRequired "true"
+ ${EndIf}
+
+ ; Remove directories and files we always control before parsing the uninstall
+ ; log so empty directories can be removed.
+ ${If} ${FileExists} "$INSTDIR\updates"
+ RmDir /r "$INSTDIR\updates"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\updated"
+ RmDir /r "$INSTDIR\updated"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults\shortcuts"
+ RmDir /r "$INSTDIR\defaults\shortcuts"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\distribution"
+ ${AndIf} $R9 != "false"
+ RmDir /r "$INSTDIR\distribution"
+ ${EndIf}
+
+ Call CheckIfInstallExisted
+
+ ; Delete the app exe if present to prevent launching the app while we are
+ ; installing.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ; If the user closed the application it can take several seconds for it to
+ ; shut down completely. If the application is being used by another user we
+ ; can rename the file and then delete is when the system is restarted.
+ Sleep 5000
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ ${EndIf}
+
+ ; setup the application model id registration value
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ; Clean up old maintenance service logs
+ ${CleanMaintenanceServiceLogs} "Mozilla\Firefox"
+
+ ${RemoveDeprecatedFiles}
+ ${RemovePrecompleteEntries} "false"
+
+ ${If} ${FileExists} "$INSTDIR\defaults\pref\channel-prefs.js"
+ Delete "$INSTDIR\defaults\pref\channel-prefs.js"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults\pref"
+ RmDir "$INSTDIR\defaults\pref"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults"
+ RmDir "$INSTDIR\defaults"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\uninstall"
+ ; Remove the uninstall directory that we control
+ RmDir /r "$INSTDIR\uninstall"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\update-settings.ini"
+ Delete "$INSTDIR\update-settings.ini"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\installation_telemetry.json"
+ Delete "$INSTDIR\installation_telemetry.json"
+ ${EndIf}
+
+ ; Explictly remove empty webapprt dir in case it exists (bug 757978).
+ RmDir "$INSTDIR\webapprt\components"
+ RmDir "$INSTDIR\webapprt"
+
+ ${InstallStartCleanupCommon}
+SectionEnd
+
+Section "-Application" APP_IDX
+ ${StartUninstallLog}
+
+ SetDetailsPrint both
+ DetailPrint $(STATUS_INSTALL_APP)
+ SetDetailsPrint none
+
+ ${LogHeader} "Installing Main Files"
+ ${CopyFilesFromDir} "$EXEDIR\core" "$INSTDIR" \
+ "$(ERROR_CREATE_DIRECTORY_PREFIX)" \
+ "$(ERROR_CREATE_DIRECTORY_SUFFIX)"
+
+ ; Register DLLs
+ ; XXXrstrong - AccessibleMarshal.dll can be used by multiple applications but
+ ; is only registered for the last application installed. When the last
+ ; application installed is uninstalled AccessibleMarshal.dll will no longer be
+ ; registered. bug 338878
+ ${LogHeader} "DLL Registration"
+ ClearErrors
+ ${RegisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Registering: $INSTDIR\AccessibleMarshal.dll **"
+ ${Else}
+ ${LogUninstall} "DLLReg: \AccessibleMarshal.dll"
+ ${LogMsg} "Registered: $INSTDIR\AccessibleMarshal.dll"
+ ${EndIf}
+
+ ClearErrors
+
+ ; Record the Windows Error Reporting module
+ WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\RuntimeExceptionHelperModules" "$INSTDIR\mozwer.dll" 0
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Recording: $INSTDIR\mozwer.dll **"
+ ${Else}
+ ${LogMsg} "Recorded: $INSTDIR\mozwer.dll"
+ ${EndIf}
+
+ ClearErrors
+
+ ; Apply LPAC permissions to install directory.
+ ${LogHeader} "File access permissions"
+ Push "Marker"
+ AccessControl::GrantOnFile \
+ "$INSTDIR" "(${LpacFirefoxInstallFilesSid})" "GenericRead + GenericExecute"
+ Pop $TmpVal ; get "Marker" or error msg
+ ${If} $TmpVal == "Marker"
+ ${LogMsg} "Granted access for LPAC to $INSTDIR"
+ ${Else}
+ ${LogMsg} "** Error granting access for LPAC to $INSTDIR : $TmpVal **"
+ Pop $TmpVal ; get "Marker"
+ ${EndIf}
+
+ ClearErrors
+
+ ; Default for creating Start Menu shortcut
+ ; (1 = create, 0 = don't create)
+ ${If} $AddStartMenuSC == ""
+ StrCpy $AddStartMenuSC "1"
+ ${EndIf}
+
+ ${If} $AddPrivateBrowsingSC == ""
+ StrCpy $AddPrivateBrowsingSC "1"
+ ${EndIf}
+
+ ; Default for creating Desktop shortcut (1 = create, 0 = don't create)
+ ${If} $AddDesktopSC == ""
+ StrCpy $AddDesktopSC "1"
+ ${EndIf}
+
+ ; Default for adding a Taskbar pin (1 = pin, 0 = don't pin)
+ ${If} $AddTaskbarSC == ""
+ ${GetPinningSupportedByWindowsVersionWithoutSystemPopup} $AddTaskbarSC
+ ${EndIf}
+
+ ${LogHeader} "Adding Registry Entries"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to HKLM
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+
+ ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
+ ${If} "$0" != "${GREVersion}"
+ WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
+ ${EndIf}
+ ${EndIf}
+
+ ${RemoveDeprecatedKeys}
+ ${Set32to64DidMigrateReg}
+
+ ; The previous installer adds several regsitry values to both HKLM and HKCU.
+ ; We now try to add to HKLM and if that fails to HKCU
+
+ ; The order that reg keys and values are added is important if you use the
+ ; uninstall log to remove them on uninstall. When using the uninstall log you
+ ; MUST add children first so they will be removed first on uninstall so they
+ ; will be empty when the key is deleted. This allows the uninstaller to
+ ; specify that only empty keys will be deleted.
+ ${SetAppKeys}
+
+ ${FixClassKeys}
+
+ ; Uninstall keys can only exist under HKLM on some versions of windows. Since
+ ; it doesn't cause problems always add them.
+ ${SetUninstallKeys}
+
+ ; On install always add the FirefoxHTML-, FirefoxPDF-, and FirefoxURL- keys.
+ ; An empty string is used for the 5th param because FirefoxHTML- is not a
+ ; protocol handler.
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ StrCpy $2 "$\"$8$\" -osint -url $\"%1$\""
+
+ ; In Win8, the delegate execute handler picks up the value in FirefoxURL- and
+ ; FirefoxHTML- to launch the desktop browser when it needs to.
+ ${AddDisabledDDEHandlerValues} "FirefoxHTML-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} HTML Document" ""
+ ${AddDisabledDDEHandlerValues} "FirefoxPDF-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_PDF_ZERO_BASED}" \
+ "${AppRegName} PDF Document" ""
+ ${AddDisabledDDEHandlerValues} "FirefoxURL-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} URL" "true"
+
+ ; Create protocol registry keys for dual browser extensions - only if not already set
+ SetShellVarContext current ; Set SHCTX to HKCU
+ !define FIREFOX_PROTOCOL "firefox"
+ ClearErrors
+ ReadRegStr $0 SHCTX "Software\Classes\${FIREFOX_PROTOCOL}" ""
+ ${If} $0 == ""
+ ${AddDisabledDDEHandlerValues} "${FIREFOX_PROTOCOL}" "$2" "$8,${IDI_APPICON_ZERO_BASED}" \
+ "Firefox Browsing Protocol" "true"
+ ${EndIf}
+
+ !define FIREFOX_PRIVATE_PROTOCOL "firefox-private"
+ ClearErrors
+ ReadRegStr $0 SHCTX "Software\Classes\${FIREFOX_PRIVATE_PROTOCOL}" ""
+ ${If} $0 == ""
+ ${AddDisabledDDEHandlerValues} "${FIREFOX_PRIVATE_PROTOCOL}" "$\"$8$\" -osint -private-window $\"%1$\"" \
+ "$8,${IDI_PBICON_PB_EXE_ZERO_BASED}" "Firefox Private Browsing Protocol" "true"
+ ${EndIf}
+ SetShellVarContext all ; Set SHCTX to HKLM
+
+ ; The keys below can be set in HKCU if needed.
+ ${If} $TmpVal == "HKLM"
+ ; Set the Start Menu Internet and Registered App HKLM registry keys.
+ ${SetStartMenuInternet} "HKLM"
+ ${FixShellIconHandler} "HKLM"
+ ${Else}
+ ; Set the Start Menu Internet and Registered App HKCU registry keys.
+ ${SetStartMenuInternet} "HKCU"
+ ${FixShellIconHandler} "HKCU"
+ ${EndIf}
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ; If the maintenance service page was displayed then a value was already
+ ; explicitly selected for installing the maintenance service and
+ ; and so InstallMaintenanceService will already be 0 or 1.
+ ; If the maintenance service page was not displayed then
+ ; InstallMaintenanceService will be equal to "".
+ ${If} $InstallMaintenanceService == ""
+ Call IsUserAdmin
+ Pop $R0
+ ${If} $R0 == "true"
+ ; Only proceed if we have HKLM write access
+ ${AndIf} $TmpVal == "HKLM"
+ ; The user is an admin, so we should default to installing the service.
+ StrCpy $InstallMaintenanceService "1"
+ ${Else}
+ ; The user is not admin, so we can't install the service.
+ StrCpy $InstallMaintenanceService "0"
+ ${EndIf}
+ ${EndIf}
+
+ ${If} $InstallMaintenanceService == "1"
+ ; The user wants to install the maintenance service, so execute
+ ; the pre-packaged maintenance service installer.
+ ; This option can only be turned on if the user is an admin so there
+ ; is no need to use ExecShell w/ verb runas to enforce elevated.
+ nsExec::Exec "$\"$INSTDIR\maintenanceservice_installer.exe$\""
+ ${EndIf}
+!endif
+
+ ; These need special handling on uninstall since they may be overwritten by
+ ; an install into a different location.
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
+ ${WriteRegStr2} $TmpVal "$0" "" "$INSTDIR\${FileMainEXE}" 0
+ ${WriteRegStr2} $TmpVal "$0" "Path" "$INSTDIR" 0
+
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\$R9"
+ ${CreateRegKey} "$TmpVal" "$0" 0
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
+ ${CreateRegKey} "$TmpVal" "$0" 0
+
+ ${If} $TmpVal == "HKLM"
+ ; Set the permitted LSP Categories
+ ${SetAppLSPCategories} ${LSP_CATEGORIES}
+ ${EndIf}
+
+!ifdef MOZ_LAUNCHER_PROCESS
+ ; Launcher telemetry is opt-out, so we always enable it by default in new
+ ; installs. We always use HKCU because this value is a reflection of a pref
+ ; from the user profile. While this is not a perfect abstraction (given the
+ ; possibility of multiple Firefox profiles owned by the same Windows user), it
+ ; is more accurate than a machine-wide setting, and should be accurate in the
+ ; majority of cases.
+ WriteRegDWORD HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Telemetry" 1
+!endif
+
+ ${WriteToastNotificationRegistration} $TmpVal
+
+ ; Create shortcuts
+ ${LogHeader} "Adding Shortcuts"
+
+ ; Remove the start menu shortcuts and directory if the SMPROGRAMS section
+ ; exists in the shortcuts_log.ini and the SMPROGRAMS. The installer's shortcut
+ ; creation code will create the shortcut in the root of the Start Menu
+ ; Programs directory.
+ ${RemoveStartMenuDir}
+
+ ; Always add the application's shortcuts to the shortcuts log ini file. The
+ ; DeleteShortcuts macro will do the right thing on uninstall if the
+ ; shortcuts don't exist.
+ ${LogStartMenuShortcut} "${BrandShortName}.lnk"
+ ${LogQuickLaunchShortcut} "${BrandShortName}.lnk"
+ ${LogDesktopShortcut} "${BrandShortName}.lnk"
+
+ ; Best effort to update the Win7 taskbar and start menu shortcut app model
+ ; id's. The possible contexts are current user / system and the user that
+ ; elevated the installer.
+ Call FixShortcutAppModelIDs
+ ; If the current context is all also perform Win7 taskbar and start menu link
+ ; maintenance for the current user context.
+ ${If} $TmpVal == "HKLM"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ Call FixShortcutAppModelIDs
+ SetShellVarContext all ; Set SHCTX to HKLM
+ ${EndIf}
+
+ ; If running elevated also perform Win7 taskbar and start menu link
+ ; maintenance for the unelevated user context in case that is different than
+ ; the current user.
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $0
+ ${Unless} ${Errors}
+ GetFunctionAddress $0 FixShortcutAppModelIDs
+ UAC::ExecCodeSegment $0
+ ${EndUnless}
+
+ ; UAC only allows elevating to an Admin account so there is no need to add
+ ; the Start Menu or Desktop shortcuts from the original unelevated process
+ ; since this will either add it for the user if unelevated or All Users if
+ ; elevated.
+ ${If} $AddStartMenuSC == 1
+ ; See if there's an existing shortcut for this installation using the old
+ ; name that we should just rename, instead of creating a new shortcut.
+ ; We could do this renaming even when $AddStartMenuSC is false; the idea
+ ; behind not doing that is to interpret "false" as "don't do anything
+ ; involving start menu shortcuts at all." We could also try to do this for
+ ; both shell contexts, but that won't typically accomplish anything.
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
+ ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandFullName}.lnk"
+ Pop $0
+ ${GetLongPath} "$0" $0
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ ${AndIfNot} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ Rename "$SMPROGRAMS\${BrandFullName}.lnk" \
+ "$SMPROGRAMS\${BrandShortName}.lnk"
+ ${LogMsg} "Renamed existing shortcut to $SMPROGRAMS\${BrandShortName}.lnk"
+ ${EndIf}
+ ${Else}
+ CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" \
+ "$AppUserModelID" "true"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $SMPROGRAMS\${BrandShortName}.lnk"
+ ${Else}
+ ${LogMsg} "** ERROR Adding Shortcut: $SMPROGRAMS\${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ ; This is always added if it doesn't already exist to ensure that Windows'
+ ; native "Pin to Taskbar" functionality can find an appropriate shortcut.
+ ; See https://bugzilla.mozilla.org/show_bug.cgi?id=1762994 for additional
+ ; background.
+ ${If} $AddPrivateBrowsingSC == 1
+ ${AddPrivateBrowsingShortcut}
+ ${EndIf}
+
+ ; Update lastwritetime of the Start Menu shortcut to clear the tile cache.
+ ; Do this for both shell contexts in case the user has shortcuts in multiple
+ ; locations, then restore the previous context at the end.
+ SetShellVarContext all
+ ${TouchStartMenuShortcut}
+ SetShellVarContext current
+ ${TouchStartMenuShortcut}
+ ${If} $TmpVal == "HKLM"
+ SetShellVarContext all
+ ${ElseIf} $TmpVal == "HKCU"
+ SetShellVarContext current
+ ${EndIf}
+
+ ${If} $AddDesktopSC == 1
+ ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ ShellLink::GetShortCutTarget "$DESKTOP\${BrandFullName}.lnk"
+ Pop $0
+ ${GetLongPath} "$0" $0
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ ${AndIfNot} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ Rename "$DESKTOP\${BrandFullName}.lnk" "$DESKTOP\${BrandShortName}.lnk"
+ ${LogMsg} "Renamed existing shortcut to $DESKTOP\${BrandShortName}.lnk"
+ ${EndIf}
+ ${Else}
+ CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" \
+ "$AppUserModelID" "true"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $DESKTOP\${BrandShortName}.lnk"
+ ${Else}
+ ${LogMsg} "** ERROR Adding Shortcut: $DESKTOP\${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+!ifdef MOZ_OPTIONAL_EXTENSIONS
+ ${If} ${FileExists} "$INSTDIR\distribution\optional-extensions"
+ ${LogHeader} "Installing optional extensions if requested"
+
+ ${If} $InstallOptionalExtensions != "0"
+ ${AndIf} ${FileExists} "$INSTDIR\distribution\setup.ini"
+ ${Unless} ${FileExists} "$INSTDIR\distribution\extensions"
+ CreateDirectory "$INSTDIR\distribution\extensions"
+ ${EndUnless}
+
+ StrCpy $0 0
+ ${Do}
+ ClearErrors
+ ReadINIStr $1 "$INSTDIR\distribution\setup.ini" "OptionalExtensions" \
+ "extension.$0.id"
+ ${If} ${Errors}
+ ${ExitDo}
+ ${EndIf}
+
+ ReadINIStr $2 "$INSTDIR\distribution\setup.ini" "OptionalExtensions" \
+ "extension.$0.checked"
+ ${If} $2 != ${BST_UNCHECKED}
+ ${LogMsg} "Installing optional extension: $1"
+ CopyFiles /SILENT "$INSTDIR\distribution\optional-extensions\$1.xpi" \
+ "$INSTDIR\distribution\extensions"
+ ${EndIf}
+
+ IntOp $0 $0 + 1
+ ${Loop}
+ ${EndIf}
+
+ ${LogMsg} "Removing the optional-extensions directory"
+ RMDir /r /REBOOTOK "$INSTDIR\distribution\optional-extensions"
+ ${EndIf}
+!endif
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ${If} $TmpVal == "HKLM"
+ ; Add the registry keys for allowed certificates.
+ ${AddMaintCertKeys}
+ ${EndIf}
+!endif
+
+!ifdef MOZ_DEFAULT_BROWSER_AGENT
+ ${If} $RegisterDefaultAgent != "0"
+ ExecWait '"$INSTDIR\default-browser-agent.exe" register-task $AppUserModelID' $0
+
+ ${If} $0 == 0x80070534 ; HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)
+ ; The agent sometimes returns this error from trying to register the task
+ ; when we're running out of the MSI. The error is cryptic, but I believe
+ ; the cause is the fact that the MSI service runs us as SYSTEM, so
+ ; proxying the invocation through the shell gets the task registered as
+ ; the interactive user, which is what we want.
+ ; We use ExecInExplorer only as a fallback instead of always, because it
+ ; doesn't work in all environments; see bug 1602726.
+ ExecInExplorer::Exec "$INSTDIR\default-browser-agent.exe" \
+ /cmdargs "register-task $AppUserModelID"
+ ; We don't need Exec's return value, but don't leave it on the stack.
+ Pop $0
+ ${EndIf}
+
+ ${If} $RegisterDefaultAgent == ""
+ ; If the variable was unset, force it to a good value.
+ StrCpy $RegisterDefaultAgent 1
+ ${EndIf}
+ ${EndIf}
+ ; Remember whether we were told to skip registering the agent, so that updates
+ ; won't try to create a registration when they don't find an existing one.
+ WriteRegDWORD HKCU "Software\Mozilla\${AppName}\Installer\$AppUserModelID" \
+ "DidRegisterDefaultBrowserAgent" $RegisterDefaultAgent
+!endif
+
+; Return value is saved to an unused variable to prevent the the error flag
+; from being set.
+Var /GLOBAL UnusedExecCatchReturn
+ExecWait '"$INSTDIR\${FileMainEXE}" --backgroundtask install' $UnusedExecCatchReturn
+SectionEnd
+
+; Cleanup operations to perform at the end of the installation.
+Section "-InstallEndCleanup"
+ SetDetailsPrint both
+ DetailPrint "$(STATUS_CLEANUP)"
+ SetDetailsPrint none
+
+ ; Maybe copy the post-signing data?
+ StrCpy $PostSigningData ""
+ ${GetParameters} $0
+ ClearErrors
+ ; We don't get post-signing data from the MSI.
+ ${GetOptions} $0 "/LaunchedFromMSI" $1
+ ${If} ${Errors}
+ ; The stub will handle copying the data if it ran us.
+ ClearErrors
+ ${GetOptions} $0 "/LaunchedFromStub" $1
+ ${If} ${Errors}
+ ; We're being run standalone, copy the data.
+ ${CopyPostSigningData}
+ Pop $PostSigningData
+ ${EndIf}
+ ${EndIf}
+
+ ; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
+ ${MigrateTaskBarShortcut} "$AddTaskbarSC"
+
+ ; Add the Firewall entries during install
+ Call AddFirewallEntries
+
+ ; Refresh desktop icons
+ ${RefreshShellIcons}
+
+ ${InstallEndCleanupCommon}
+
+ ${If} $PreventRebootRequired == "true"
+ SetRebootFlag false
+ ${EndIf}
+
+ ${If} ${RebootFlag}
+ ; Admin is required to delete files on reboot so only add the moz-delete if
+ ; the user is an admin. After calling UAC::IsAdmin $0 will equal 1 if the
+ ; user is an admin.
+ UAC::IsAdmin
+ ${If} "$0" == "1"
+ ; When a reboot is required give RefreshShellIcons time to finish the
+ ; refreshing the icons so the OS doesn't display the icons from helper.exe
+ Sleep 10000
+ ${LogHeader} "Reboot Required To Finish Installation"
+ ; ${FileMainEXE}.moz-upgrade should never exist but just in case...
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ ${EndUnless}
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-delete"
+ ${Unless} ${Errors}
+ Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete"
+ ${EndUnless}
+ ${EndIf}
+
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ CopyFiles /SILENT "$INSTDIR\uninstall\helper.exe" "$INSTDIR"
+ FileOpen $0 "$INSTDIR\${FileMainEXE}" w
+ FileWrite $0 "Will be deleted on restart"
+ Rename /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
+ FileClose $0
+ Delete "$INSTDIR\${FileMainEXE}"
+ Rename "$INSTDIR\helper.exe" "$INSTDIR\${FileMainEXE}"
+ ${EndUnless}
+ ${EndIf}
+ ${EndIf}
+
+ Call WriteInstallationTelemetryData
+
+ StrCpy $InstallResult "success"
+
+ ; When we're using the GUI, .onGUIEnd sends the ping, but of course that isn't
+ ; invoked when we're running silently.
+ ${If} ${Silent}
+ Call SendPing
+ ${EndIf}
+SectionEnd
+
+################################################################################
+# Install Abort Survey Functions
+
+Function CustomAbort
+ ${If} "${AB_CD}" == "en-US"
+ ${AndIf} "$PageName" != ""
+ ${AndIf} ${FileExists} "$EXEDIR\core\distribution\distribution.ini"
+ ReadINIStr $0 "$EXEDIR\core\distribution\distribution.ini" "Global" "about"
+ ClearErrors
+ ${WordFind} "$0" "Funnelcake" "E#" $1
+ ${Unless} ${Errors}
+ ; Yes = fill out the survey and exit, No = don't fill out survey and exit,
+ ; Cancel = don't exit.
+ MessageBox MB_YESNO|MB_ICONEXCLAMATION \
+ "Would you like to tell us why you are canceling this installation?" \
+ IDYes +1 IDNO CustomAbort_finish
+ ${If} "$PageName" == "Welcome"
+ GetFunctionAddress $0 AbortSurveyWelcome
+ ${ElseIf} "$PageName" == "Options"
+ GetFunctionAddress $0 AbortSurveyOptions
+ ${ElseIf} "$PageName" == "Directory"
+ GetFunctionAddress $0 AbortSurveyDirectory
+ ${ElseIf} "$PageName" == "Shortcuts"
+ GetFunctionAddress $0 AbortSurveyShortcuts
+ ${ElseIf} "$PageName" == "Summary"
+ GetFunctionAddress $0 AbortSurveySummary
+ ${EndIf}
+ ClearErrors
+ ${GetParameters} $1
+ ${GetOptions} "$1" "/UAC:" $2
+ ${If} ${Errors}
+ Call $0
+ ${Else}
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+
+ CustomAbort_finish:
+ Return
+ ${EndUnless}
+ ${EndIf}
+
+ MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(MOZ_MUI_TEXT_ABORTWARNING)" \
+ IDYES +1 IDNO +2
+ Return
+ Abort
+FunctionEnd
+
+Function AbortSurveyWelcome
+ ExecShell "open" "${AbortSurveyURL}step1"
+FunctionEnd
+
+Function AbortSurveyOptions
+ ExecShell "open" "${AbortSurveyURL}step2"
+FunctionEnd
+
+Function AbortSurveyDirectory
+ ExecShell "open" "${AbortSurveyURL}step3"
+FunctionEnd
+
+Function AbortSurveyShortcuts
+ ExecShell "open" "${AbortSurveyURL}step4"
+FunctionEnd
+
+Function AbortSurveySummary
+ ExecShell "open" "${AbortSurveyURL}step5"
+FunctionEnd
+
+################################################################################
+# Helper Functions
+
+Function CheckExistingInstall
+ ; If there is a pending file copy from a previous upgrade don't allow
+ ; installing until after the system has rebooted.
+ IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4
+ MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
+ Reboot
+ Quit
+
+ ; If there is a pending file deletion from a previous uninstall don't allow
+ ; installing until after the system has rebooted.
+ IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +4
+ MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +2
+ Reboot
+ Quit
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ ; Disable the next, cancel, and back buttons
+ GetDlgItem $0 $HWNDPARENT 1 ; Next button
+ EnableWindow $0 0
+ GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
+ EnableWindow $0 0
+ GetDlgItem $0 $HWNDPARENT 3 ; Back button
+ EnableWindow $0 0
+
+ Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)"
+
+ ${If} "$TmpVal" == "FoundAppWindow"
+ Sleep 5000
+ ${EndIf}
+
+ ${PushFilesToCheck}
+
+ ; Store the return value in $TmpVal so it is less likely to be accidentally
+ ; overwritten elsewhere.
+ ${CheckForFilesInUse} $TmpVal
+
+ Banner::destroy
+
+ ; Enable the next, cancel, and back buttons
+ GetDlgItem $0 $HWNDPARENT 1 ; Next button
+ EnableWindow $0 1
+ GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
+ EnableWindow $0 1
+ GetDlgItem $0 $HWNDPARENT 3 ; Back button
+ EnableWindow $0 1
+
+ ; If there are files in use $TmpVal will be "true"
+ ${If} "$TmpVal" == "true"
+ ; If it finds a window of the right class, then ManualCloseAppPrompt will
+ ; abort leaving the value of $TmpVal set to "FoundAppWindow".
+ StrCpy $TmpVal "FoundAppWindow"
+ ${ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ ${ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ StrCpy $TmpVal "true"
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+Function LaunchApp
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
+ ${Else}
+ GetFunctionAddress $0 LaunchAppFromElevatedProcess
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+
+ StrCpy $LaunchedNewApp true
+FunctionEnd
+
+Function LaunchAppFromElevatedProcess
+ ; Set our current working directory to the application's install directory
+ ; otherwise the 7-Zip temp directory will be in use and won't be deleted.
+ SetOutPath "$INSTDIR"
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
+FunctionEnd
+
+Function SendPing
+ ${GetParameters} $0
+ ${GetOptions} $0 "/LaunchedFromStub" $0
+ ${IfNot} ${Errors}
+ Return
+ ${EndIf}
+
+ ; Create a GUID to use as the unique document ID.
+ System::Call "rpcrt4::UuidCreate(g . r0)i"
+ ; StringFromGUID2 (which is what System::Call uses internally to stringify
+ ; GUIDs) includes braces in its output, and we don't want those.
+ StrCpy $0 $0 -1 1
+
+ ; Configure the HTTP request for the ping
+ nsJSON::Set /tree ping /value "{}"
+ nsJSON::Set /tree ping "Url" /value \
+ '"${TELEMETRY_BASE_URL}/${TELEMETRY_NAMESPACE}/${TELEMETRY_INSTALL_PING_DOCTYPE}/${TELEMETRY_INSTALL_PING_VERSION}/$0"'
+ nsJSON::Set /tree ping "Verb" /value '"POST"'
+ nsJSON::Set /tree ping "DataType" /value '"JSON"'
+ nsJSON::Set /tree ping "AccessType" /value '"PreConfig"'
+
+ ; Fill in the ping payload
+ nsJSON::Set /tree ping "Data" /value "{}"
+ nsJSON::Set /tree ping "Data" "installer_type" /value '"full"'
+ nsJSON::Set /tree ping "Data" "installer_version" /value '"${AppVersion}"'
+ nsJSON::Set /tree ping "Data" "build_channel" /value '"${Channel}"'
+ nsJSON::Set /tree ping "Data" "update_channel" /value '"${UpdateChannel}"'
+ nsJSON::Set /tree ping "Data" "locale" /value '"${AB_CD}"'
+
+ ReadINIStr $0 "$INSTDIR\application.ini" "App" "Version"
+ nsJSON::Set /tree ping "Data" "version" /value '"$0"'
+ ReadINIStr $0 "$INSTDIR\application.ini" "App" "BuildID"
+ nsJSON::Set /tree ping "Data" "build_id" /value '"$0"'
+
+ ; Capture the distribution ID and version if they exist.
+ StrCpy $1 "$INSTDIR\distribution\distribution.ini"
+ ${If} ${FileExists} "$1"
+ ReadINIStr $0 "$1" "Global" "id"
+ nsJSON::Set /tree ping "Data" "distribution_id" /value '"$0"'
+ ReadINIStr $0 "$1" "Global" "version"
+ nsJSON::Set /tree ping "Data" "distribution_version" /value '"$0"'
+ ${EndIf}
+
+ ${GetParameters} $0
+ ${GetOptions} $0 "/LaunchedFromMSI" $0
+ ${IfNot} ${Errors}
+ nsJSON::Set /tree ping "Data" "from_msi" /value true
+ ${EndIf}
+
+ !ifdef HAVE_64BIT_BUILD
+ nsJSON::Set /tree ping "Data" "64bit_build" /value true
+ !else
+ nsJSON::Set /tree ping "Data" "64bit_build" /value false
+ !endif
+
+ ${If} ${RunningX64}
+ nsJSON::Set /tree ping "Data" "64bit_os" /value true
+ ${Else}
+ nsJSON::Set /tree ping "Data" "64bit_os" /value false
+ ${EndIf}
+
+ ; Though these values are sometimes incorrect due to bug 444664 it happens
+ ; so rarely it isn't worth working around it by reading the registry values.
+ ${WinVerGetMajor} $0
+ ${WinVerGetMinor} $1
+ ${WinVerGetBuild} $2
+ nsJSON::Set /tree ping "Data" "os_version" /value '"$0.$1.$2"'
+ ${If} ${IsServerOS}
+ nsJSON::Set /tree ping "Data" "server_os" /value true
+ ${Else}
+ nsJSON::Set /tree ping "Data" "server_os" /value false
+ ${EndIf}
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
+ "Write Test"
+ ${If} ${Errors}
+ nsJSON::Set /tree ping "Data" "admin_user" /value false
+ ${Else}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ nsJSON::Set /tree ping "Data" "admin_user" /value true
+ ${EndIf}
+
+ ${If} $DefaultInstDir == $INSTDIR
+ nsJSON::Set /tree ping "Data" "default_path" /value true
+ ${Else}
+ nsJSON::Set /tree ping "Data" "default_path" /value false
+ ${EndIf}
+
+ nsJSON::Set /tree ping "Data" "set_default" /value "$SetAsDefault"
+
+ nsJSON::Set /tree ping "Data" "new_default" /value false
+ nsJSON::Set /tree ping "Data" "old_default" /value false
+
+ AppAssocReg::QueryCurrentDefault "http" "protocol" "effective"
+ Pop $0
+ ReadRegStr $0 HKCR "$0\shell\open\command" ""
+ ${If} $0 != ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $1
+ ${GetLongPath} "$1" $1
+ ${If} $1 == $INSTDIR
+ nsJSON::Set /tree ping "Data" "new_default" /value true
+ ${Else}
+ StrCpy $0 "$0" "" -11 # 11 == length of "firefox.exe"
+ ${If} "$0" == "${FileMainEXE}"
+ nsJSON::Set /tree ping "Data" "old_default" /value true
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ nsJSON::Set /tree ping "Data" "had_old_install" /value "$HadOldInstall"
+
+ ${If} ${Silent}
+ ; In silent mode, only the install phase is executed, and the GUI events
+ ; that initialize most of the phase times are never called; only
+ ; $InstallPhaseStart and $FinishPhaseStart have usable values.
+ ${GetSecondsElapsed} $InstallPhaseStart $FinishPhaseStart $0
+
+ nsJSON::Set /tree ping "Data" "intro_time" /value 0
+ nsJSON::Set /tree ping "Data" "options_time" /value 0
+ nsJSON::Set /tree ping "Data" "install_time" /value "$0"
+ nsJSON::Set /tree ping "Data" "finish_time" /value 0
+ ${Else}
+ ; In GUI mode, all we can be certain of is that the intro phase has started;
+ ; the user could have canceled at any time and phases after that won't
+ ; have run at all. So we have to be prepared for anything after
+ ; $IntroPhaseStart to be uninitialized. For anything that isn't filled in
+ ; yet we'll use the current tick count. That means that any phases that
+ ; weren't entered at all will get 0 for their times because the start and
+ ; end tick counts will be the same.
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $0
+
+ ${If} $OptionsPhaseStart == 0
+ StrCpy $OptionsPhaseStart $0
+ ${EndIf}
+ ${GetSecondsElapsed} $IntroPhaseStart $OptionsPhaseStart $1
+ nsJSON::Set /tree ping "Data" "intro_time" /value "$1"
+
+ ${If} $InstallPhaseStart == 0
+ StrCpy $InstallPhaseStart $0
+ ${EndIf}
+ ${GetSecondsElapsed} $OptionsPhaseStart $InstallPhaseStart $1
+ nsJSON::Set /tree ping "Data" "options_time" /value "$1"
+
+ ${If} $FinishPhaseStart == 0
+ StrCpy $FinishPhaseStart $0
+ ${EndIf}
+ ${GetSecondsElapsed} $InstallPhaseStart $FinishPhaseStart $1
+ nsJSON::Set /tree ping "Data" "install_time" /value "$1"
+
+ ${If} $FinishPhaseEnd == 0
+ StrCpy $FinishPhaseEnd $0
+ ${EndIf}
+ ${GetSecondsElapsed} $FinishPhaseStart $FinishPhaseEnd $1
+ nsJSON::Set /tree ping "Data" "finish_time" /value "$1"
+ ${EndIf}
+
+ ; $PostSigningData should only be empty if we didn't try to copy the
+ ; postSigningData file at all. If we did try and the file was missing
+ ; or empty, this will be "0", and for consistency with the stub we will
+ ; still submit it.
+ ${If} $PostSigningData != ""
+ nsJSON::Quote /always $PostSigningData
+ Pop $0
+ nsJSON::Set /tree ping "Data" "attribution" /value $0
+ ${EndIf}
+
+ nsJSON::Set /tree ping "Data" "new_launched" /value "$LaunchedNewApp"
+
+ nsJSON::Set /tree ping "Data" "succeeded" /value false
+ ${If} $InstallResult == "cancel"
+ nsJSON::Set /tree ping "Data" "user_cancelled" /value true
+ ${ElseIf} $InstallResult == "success"
+ nsJSON::Set /tree ping "Data" "succeeded" /value true
+ ${EndIf}
+
+ ${If} ${Silent}
+ nsJSON::Set /tree ping "Data" "silent" /value true
+ ${Else}
+ nsJSON::Set /tree ping "Data" "silent" /value false
+ ${EndIf}
+
+ ; Send the ping request. This call will block until a response is received,
+ ; but we shouldn't have any windows still open, so we won't jank anything.
+ nsJSON::Set /http ping
+FunctionEnd
+
+; Record data about this installation for use in in-app Telemetry pings.
+;
+; This should be run only after a successful installation, as it will
+; pull data from $INSTDIR\application.ini.
+;
+; Unlike the install ping or post-signing data, which is only sent/written by
+; the full installer when it is not run by the stub (since the stub has more
+; information), this will always be recorded by the full installer, to reduce
+; duplication and ensure consistency.
+;
+; Note: Should be assumed to clobber all $0, $1, etc registers.
+!define JSONSet `nsJSON::Set /tree installation_data`
+Function WriteInstallationTelemetryData
+ ${JSONSet} /value "{}"
+
+ ReadINIStr $0 "$INSTDIR\application.ini" "App" "Version"
+ ${JSONSet} "version" /value '"$0"'
+ ReadINIStr $0 "$INSTDIR\application.ini" "App" "BuildID"
+ ${JSONSet} "build_id" /value '"$0"'
+
+ ; Check for write access to HKLM, if successful then report this user
+ ; as an (elevated) admin.
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
+ "Write Test"
+ ${If} ${Errors}
+ StrCpy $1 "false"
+ ${Else}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $1 "true"
+ ${EndIf}
+ ${JSONSet} "admin_user" /value $1
+
+ ; Note: This is not the same as $HadOldInstall, which looks for any install
+ ; in the registry. $InstallExisted is true only if this run of the installer
+ ; is replacing an existing main EXE.
+ ${If} $InstallExisted != ""
+ ${JSONSet} "install_existed" /value $InstallExisted
+ ${EndIf}
+
+ ; Check for top-level profile directory
+ ; Note: This is the same check used to set $ExistingProfile in stub.nsi
+ ${GetLocalAppDataFolder} $0
+ ${If} ${FileExists} "$0\Mozilla\Firefox"
+ StrCpy $1 "true"
+ ${Else}
+ StrCpy $1 "false"
+ ${EndIf}
+ ${JSONSet} "profdir_existed" /value $1
+
+ ${GetParameters} $0
+ ${GetOptions} $0 "/LaunchedFromStub" $1
+ ${IfNot} ${Errors}
+ ${JSONSet} "installer_type" /value '"stub"'
+ ${Else}
+ ; Not launched from stub
+ ${JSONSet} "installer_type" /value '"full"'
+
+ ; Include additional info relevant when the full installer is run directly
+
+ ${If} ${Silent}
+ StrCpy $1 "true"
+ ${Else}
+ StrCpy $1 "false"
+ ${EndIf}
+ ${JSONSet} "silent" /value $1
+
+ ${GetOptions} $0 "/LaunchedFromMSI" $1
+ ${IfNot} ${Errors}
+ StrCpy $1 "true"
+ ${Else}
+ StrCpy $1 "false"
+ ${EndIf}
+ ${JSONSet} "from_msi" /value $1
+
+ ; NOTE: for non-admin basic installs, or reinstalls, $DefaultInstDir may not
+ ; reflect the actual default path.
+ ${If} $DefaultInstDir == $INSTDIR
+ StrCpy $1 "true"
+ ${Else}
+ StrCpy $1 "false"
+ ${EndIf}
+ ${JSONSet} "default_path" /value $1
+ ${EndIf}
+
+ ; Timestamp, to allow app to detect a new install.
+ ; As a 64-bit integer isn't valid JSON, quote as a string.
+ System::Call "kernel32::GetSystemTimeAsFileTime(*l.r0)"
+ ${JSONSet} "install_timestamp" /value '"$0"'
+
+ nsJSON::Serialize /tree installation_data /file /unicode "$INSTDIR\installation_telemetry.json"
+FunctionEnd
+!undef JSONSet
+
+; Set $InstallExisted (if not yet set) by checking for the EXE.
+; Should be called before trying to delete the EXE when install begins.
+Function CheckIfInstallExisted
+ ${If} $InstallExisted == ""
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ StrCpy $InstallExisted true
+ ${Else}
+ StrCpy $InstallExisted false
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!ifdef MOZ_OPTIONAL_EXTENSIONS
+!include "extensionsLocale.nsh"
+!endif
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale
+; using " " for BrandingText will hide the "Nullsoft Install System..." branding
+BrandingText " "
+
+################################################################################
+# Page pre, show, and leave functions
+
+Function preWelcome
+ StrCpy $PageName "Welcome"
+ ${If} ${FileExists} "$EXEDIR\core\distribution\modern-wizard.bmp"
+ Delete "$PLUGINSDIR\modern-wizard.bmp"
+ CopyFiles /SILENT "$EXEDIR\core\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
+ ${EndIf}
+
+ ; We don't want the header bitmap showing on the welcome page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $IntroPhaseStart
+FunctionEnd
+
+Function showWelcome
+ ; The welcome and finish pages don't get the correct colors for their labels
+ ; like the other pages do, presumably because they're built by filling in an
+ ; InstallOptions .ini file instead of from a dialog resource like the others.
+ ; Field 2 is the header and Field 3 is the body text.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+FunctionEnd
+
+Function leaveWelcome
+ ; Bring back the header bitmap for the next pages.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_SHOW}
+FunctionEnd
+
+Function preOptions
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $OptionsPhaseStart
+
+ ; The header and subheader on the wizard pages don't get the correct text
+ ; color by default for some reason, even though the other controls do.
+ GetDlgItem $0 $HWNDPARENT 1037
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ GetDlgItem $0 $HWNDPARENT 1038
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ StrCpy $PageName "Options"
+ ${If} ${FileExists} "$EXEDIR\core\distribution\modern-header.bmp"
+ Delete "$PLUGINSDIR\modern-header.bmp"
+ CopyFiles /SILENT "$EXEDIR\core\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
+ ${EndIf}
+ ${ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
+ !insertmacro MUI_HEADER_TEXT "$(OPTIONS_PAGE_TITLE)" "$(OPTIONS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "options.ini"
+FunctionEnd
+
+Function leaveOptions
+ ${MUI_INSTALLOPTIONS_READ} $0 "options.ini" "Settings" "State"
+ ${If} $0 != 0
+ Abort
+ ${EndIf}
+ ${MUI_INSTALLOPTIONS_READ} $R0 "options.ini" "Field 2" "State"
+ StrCmp $R0 "1" +1 +2
+ StrCpy $InstallType ${INSTALLTYPE_BASIC}
+ ${MUI_INSTALLOPTIONS_READ} $R0 "options.ini" "Field 3" "State"
+ StrCmp $R0 "1" +1 +2
+ StrCpy $InstallType ${INSTALLTYPE_CUSTOM}
+
+ ${LeaveOptionsCommon}
+
+ ${If} $InstallType == ${INSTALLTYPE_BASIC}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+
+Function preDirectory
+ StrCpy $PageName "Directory"
+ ${PreDirectoryCommon}
+
+ StrCpy $DefaultInstDir $INSTDIR
+FunctionEnd
+
+Function leaveDirectory
+ ${If} $InstallType == ${INSTALLTYPE_BASIC}
+ Call CheckExistingInstall
+ ${EndIf}
+ ${LeaveDirectoryCommon} "$(WARN_DISK_SPACE)" "$(WARN_WRITE_ACCESS)"
+FunctionEnd
+
+Function preShortcuts
+ StrCpy $PageName "Shortcuts"
+ ${CheckCustomCommon}
+ !insertmacro MUI_HEADER_TEXT "$(SHORTCUTS_PAGE_TITLE)" "$(SHORTCUTS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "shortcuts.ini"
+FunctionEnd
+
+Function leaveShortcuts
+ ${MUI_INSTALLOPTIONS_READ} $0 "shortcuts.ini" "Settings" "State"
+ ${If} $0 != 0
+ Abort
+ ${EndIf}
+ ${MUI_INSTALLOPTIONS_READ} $AddDesktopSC "shortcuts.ini" "Field 2" "State"
+ ${MUI_INSTALLOPTIONS_READ} $AddStartMenuSC "shortcuts.ini" "Field 3" "State"
+
+
+ ${If} ${IsPinningSupportedByWindowsVersionWithoutSystemPopup}
+ ${MUI_INSTALLOPTIONS_READ} $AddTaskbarSC "shortcuts.ini" "Field 4" "State"
+ ${EndIf}
+
+ ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+Function preComponents
+ ; If the service already exists, don't show this page
+ ServicesHelper::IsInstalled "MozillaMaintenance"
+ Pop $R9
+ ${If} $R9 == 1
+ ; The service already exists so don't show this page.
+ Abort
+ ${EndIf}
+
+ ; Don't show the custom components page if the
+ ; user is not an admin
+ Call IsUserAdmin
+ Pop $R9
+ ${If} $R9 != "true"
+ Abort
+ ${EndIf}
+
+ ; Only show the maintenance service page if we have write access to HKLM
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" \
+ "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ ClearErrors
+ Abort
+ ${Else}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ ${EndIf}
+
+ StrCpy $PageName "Components"
+ ${CheckCustomCommon}
+ !insertmacro MUI_HEADER_TEXT "$(COMPONENTS_PAGE_TITLE)" "$(COMPONENTS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "components.ini"
+FunctionEnd
+
+Function leaveComponents
+ ${MUI_INSTALLOPTIONS_READ} $0 "components.ini" "Settings" "State"
+ ${If} $0 != 0
+ Abort
+ ${EndIf}
+ ${MUI_INSTALLOPTIONS_READ} $InstallMaintenanceService "components.ini" "Field 2" "State"
+ ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+!endif
+
+!ifdef MOZ_OPTIONAL_EXTENSIONS
+Function preExtensions
+ StrCpy $PageName "Extensions"
+ ${CheckCustomCommon}
+
+ ; Abort if no optional extensions configured in distribution/setup.ini
+ ${If} ${FileExists} "$EXEDIR\core\distribution\setup.ini"
+ ClearErrors
+ ReadINIStr $ExtensionRecommender "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "Recommender.${AB_CD}"
+ ${If} ${Errors}
+ ClearErrors
+ ReadINIStr $ExtensionRecommender "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "Recommender"
+ ${EndIf}
+
+ ${If} ${Errors}
+ ClearErrors
+ Abort
+ ${EndIf}
+ ${Else}
+ Abort
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(EXTENSIONS_PAGE_TITLE)" "$(EXTENSIONS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "extensions.ini"
+FunctionEnd
+
+Function leaveExtensions
+ ${MUI_INSTALLOPTIONS_READ} $0 "extensions.ini" "Settings" "NumFields"
+ ${MUI_INSTALLOPTIONS_READ} $1 "extensions.ini" "Settings" "State"
+
+ ; $0 is count of checkboxes
+ IntOp $0 $0 - 1
+
+ ${If} $1 > $0
+ Abort
+ ${ElseIf} $1 == 0
+ ; $1 is count of selected optional extension(s)
+ StrCpy $1 0
+
+ StrCpy $2 2
+ ${Do}
+ ${MUI_INSTALLOPTIONS_READ} $3 "extensions.ini" "Field $2" "State"
+ ${If} $3 == ${BST_CHECKED}
+ IntOp $1 $1 + 1
+ ${EndIf}
+
+ IntOp $4 $2 - 2
+ WriteINIStr "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "extension.$4.checked" "$3"
+
+ ${If} $0 == $2
+ ${ExitDo}
+ ${Else}
+ IntOp $2 $2 + 1
+ ${EndIf}
+ ${Loop}
+
+ ; Different from state of field 1, "0" means no optional extensions selected
+ ${If} $1 > 0
+ StrCpy $InstallOptionalExtensions "1"
+ ${Else}
+ StrCpy $InstallOptionalExtensions "0"
+ ${EndIf}
+
+ ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+ Call CheckExistingInstall
+ ${EndIf}
+ ${ElseIf} $1 == 1
+ ; Check/uncheck all optional extensions with field 1
+ ${MUI_INSTALLOPTIONS_READ} $1 "extensions.ini" "Field 1" "State"
+
+ StrCpy $2 2
+ ${Do}
+ ${MUI_INSTALLOPTIONS_READ} $3 "extensions.ini" "Field $2" "HWND"
+ SendMessage $3 ${BM_SETCHECK} $1 0
+
+ ${If} $0 == $2
+ ${ExitDo}
+ ${Else}
+ IntOp $2 $2 + 1
+ ${EndIf}
+ ${Loop}
+
+ Abort
+ ${ElseIf} $1 > 1
+ StrCpy $1 ${BST_CHECKED}
+
+ StrCpy $2 2
+ ${Do}
+ ${MUI_INSTALLOPTIONS_READ} $3 "extensions.ini" "Field $2" "State"
+ ${If} $3 == ${BST_UNCHECKED}
+ StrCpy $1 ${BST_UNCHECKED}
+ ${ExitDo}
+ ${EndIf}
+
+ ${If} $0 == $2
+ ${ExitDo}
+ ${Else}
+ IntOp $2 $2 + 1
+ ${EndIf}
+ ${Loop}
+
+ ; Check field 1 only if all optional extensions are selected
+ ${MUI_INSTALLOPTIONS_READ} $3 "extensions.ini" "Field 1" "HWND"
+ SendMessage $3 ${BM_SETCHECK} $1 0
+
+ Abort
+ ${EndIf}
+FunctionEnd
+!endif
+
+Function preSummary
+ StrCpy $PageName "Summary"
+ ; Setup the summary.ini file for the Custom Summary Page
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "3"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Text "$(SUMMARY_INSTALLED_TO)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Type "text"
+ ; The contents of this control must be set as follows in the pre function
+ ; ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND"
+ ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" state ""
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Top "17"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" flags "READONLY"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Top "130"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Bottom "150"
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Text "$(SUMMARY_UPGRADE_CLICK)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NextButtonText "$(UPGRADE_BUTTON)"
+ ${Else}
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Text "$(SUMMARY_INSTALL_CLICK)"
+ DeleteINIStr "$PLUGINSDIR\summary.ini" "Settings" NextButtonText
+ ${EndIf}
+
+ ${If} "$TmpVal" == "true"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "4"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Text "$(SUMMARY_REBOOT_REQUIRED_INSTALL)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "35"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "50"
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(SUMMARY_PAGE_TITLE)" "$(SUMMARY_PAGE_SUBTITLE)"
+
+ ; The Summary custom page has a textbox that will automatically receive
+ ; focus. This sets the focus to the Install button instead.
+ !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "summary.ini"
+ GetDlgItem $0 $HWNDPARENT 1
+ System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i"
+ ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND"
+ SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ !insertmacro MUI_INSTALLOPTIONS_SHOW
+FunctionEnd
+
+Function leaveSummary
+ Call CheckIfInstallExisted
+
+ ; Try to delete the app executable and if we can't delete it try to find the
+ ; app's message window and prompt the user to close the app. This allows
+ ; running an instance that is located in another directory. If for whatever
+ ; reason there is no message window we will just rename the app's files and
+ ; then remove them on restart.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ${ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ ${ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ ${EndIf}
+FunctionEnd
+
+; When we add an optional action to the finish page the cancel button is
+; enabled. This disables it and leaves the finish button as the only choice.
+Function preFinish
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $FinishPhaseStart
+
+ StrCpy $PageName ""
+ ${EndInstallLog} "${BrandFullName}"
+ !insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "cancelenabled" "0"
+
+ ; We don't want the header bitmap showing on the finish page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+FunctionEnd
+
+Function showFinish
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+
+ ; Field 4 is the launch checkbox. Since it's a checkbox, we need to
+ ; clear the theme from it before we can set its background color.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 4" "HWND"
+ System::Call 'uxtheme::SetWindowTheme(i $0, w " ", w " ")'
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+FunctionEnd
+
+Function postFinish
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $FinishPhaseEnd
+FunctionEnd
+
+################################################################################
+# Initialization Functions
+
+Function .onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ ; Initialize the variables used for telemetry
+ StrCpy $SetAsDefault true
+ StrCpy $HadOldInstall false
+ StrCpy $InstallExisted ""
+ StrCpy $DefaultInstDir $INSTDIR
+ StrCpy $IntroPhaseStart 0
+ StrCpy $OptionsPhaseStart 0
+ StrCpy $InstallPhaseStart 0
+ StrCpy $FinishPhaseStart 0
+ StrCpy $FinishPhaseEnd 0
+ StrCpy $InstallResult "cancel"
+ StrCpy $LaunchedNewApp false
+
+ StrCpy $PageName ""
+ StrCpy $LANGUAGE 0
+ ${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
+
+ ; Don't install on systems that don't support SSE2. The parameter value of
+ ; 10 is for PF_XMMI64_INSTRUCTIONS_AVAILABLE which will check whether the
+ ; SSE2 instruction set is available. Result returned in $R7.
+ System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
+
+ ; Windows 8.1/Server 2012 R2 and lower are not supported.
+ ${Unless} ${AtLeastWin10}
+ ${If} "$R7" == "0"
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
+ ${Else}
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_MSG)"
+ ${EndIf}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$R7" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndUnless}
+
+ ; SSE2 CPU support
+ ${If} "$R7" == "0"
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndIf}
+
+!ifdef HAVE_64BIT_BUILD
+ ${If} "${ARCH}" == "AArch64"
+ ${IfNot} ${IsNativeARM64}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_OSVER_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndIf}
+ ${ElseIfNot} ${RunningX64}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_OSVER_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndIf}
+ SetRegView 64
+!endif
+
+ SetShellVarContext all
+ ${GetFirstInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+ ${If} "$0" == "false"
+ SetShellVarContext current
+ ${GetFirstInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+ ${If} "$0" == "false"
+ StrCpy $HadOldInstall false
+ ${Else}
+ StrCpy $HadOldInstall true
+ ${EndIf}
+ ${Else}
+ StrCpy $HadOldInstall true
+ ${EndIf}
+
+ ${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
+
+ !insertmacro InitInstallOptionsFile "options.ini"
+ !insertmacro InitInstallOptionsFile "shortcuts.ini"
+ !insertmacro InitInstallOptionsFile "components.ini"
+ !insertmacro InitInstallOptionsFile "extensions.ini"
+ !insertmacro InitInstallOptionsFile "summary.ini"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "5"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Text "$(OPTIONS_SUMMARY)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Top "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Bottom "10"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Type "RadioButton"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Text "$(OPTION_STANDARD_RADIO)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Top "25"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Bottom "35"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" State "1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Flags "GROUP"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Type "RadioButton"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Text "$(OPTION_CUSTOM_RADIO)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Top "55"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Bottom "65"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" State "0"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Text "$(OPTION_STANDARD_DESC)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Left "15"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Top "37"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Bottom "57"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Text "$(OPTION_CUSTOM_DESC)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Left "15"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Top "67"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Bottom "87"
+
+ ${If} ${IsPinningSupportedByWindowsVersionWithoutSystemPopup}
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "4"
+ ${Else}
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "3"
+ ${EndIf}
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Text "$(CREATE_ICONS_DESC)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Text "$(ICONS_DESKTOP)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Top "20"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" State "1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Flags "GROUP"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Text "$(ICONS_STARTMENU)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Top "40"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Bottom "50"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" State "1"
+
+ ${If} ${IsPinningSupportedByWindowsVersionWithoutSystemPopup}
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Text "$(ICONS_TASKBAR)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Left "0"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Top "60"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Bottom "70"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" State "1"
+ ${EndIf}
+
+ ; Setup the components.ini file for the Components Page
+ WriteINIStr "$PLUGINSDIR\components.ini" "Settings" NumFields "2"
+
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Text "$(OPTIONAL_COMPONENTS_DESC)"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "25"
+
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Text "$(MAINTENANCE_SERVICE_CHECKBOX_DESC)"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top "27"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "37"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" State "1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Flags "GROUP"
+
+ ; Setup the extensions.ini file for the Custom Extensions Page
+ StrCpy $R9 0
+ StrCpy $R8 ${BST_CHECKED}
+
+ ${If} ${FileExists} "$EXEDIR\core\distribution\setup.ini"
+ ${Do}
+ IntOp $R7 $R9 + 2
+
+ ClearErrors
+ ReadINIStr $R6 "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "extension.$R9.name.${AB_CD}"
+ ${If} ${Errors}
+ ClearErrors
+ ReadINIStr $R6 "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "extension.$R9.name"
+ ${EndIf}
+
+ ${If} ${Errors}
+ ${ExitDo}
+ ${EndIf}
+
+ ; Each row moves down by 13 DLUs
+ IntOp $R2 $R9 * 13
+ IntOp $R2 $R2 + 21
+ IntOp $R1 $R2 + 10
+
+ ClearErrors
+ ReadINIStr $R0 "$EXEDIR\core\distribution\setup.ini" \
+ "OptionalExtensions" "extension.$R9.checked"
+ ${If} ${Errors}
+ StrCpy $R0 ${BST_CHECKED}
+ ${ElseIf} $R0 == "0"
+ StrCpy $R8 ${BST_UNCHECKED}
+ ${EndIf}
+
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Text "$R6"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Left "11"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Right "-1"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Top "$R2"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Bottom "$R1"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" State "$R0"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R7" Flags "NOTIFY"
+
+ IntOp $R9 $R9 + 1
+ ${Loop}
+ ${EndIf}
+
+ IntOp $R9 $R9 + 2
+
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Settings" NumFields "$R9"
+
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Text "$(OPTIONAL_EXTENSIONS_CHECKBOX_DESC)"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Bottom "15"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" State "$R8"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field 1" Flags "GROUP|NOTIFY"
+
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Type "label"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Text "$(OPTIONAL_EXTENSIONS_DESC)"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Left "0"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Right "-1"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Top "-23"
+ WriteINIStr "$PLUGINSDIR\extensions.ini" "Field $R9" Bottom "-5"
+
+ ; There must always be a core directory.
+ ${GetSize} "$EXEDIR\core\" "/S=0K" $R5 $R7 $R8
+ SectionSetSize ${APP_IDX} $R5
+
+ ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if
+ ; the user clicks the back button
+ StrCpy $hHeaderBitmap ""
+FunctionEnd
+
+Function .onGUIEnd
+ ${OnEndCommon}
+ Call SendPing
+FunctionEnd
diff --git a/browser/installer/windows/nsis/maintenanceservice_installer.nsi b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
new file mode 100644
index 0000000000..c285e45bbd
--- /dev/null
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -0,0 +1,343 @@
+# 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/.
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel admin
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+; Variables
+Var TempMaintServiceName
+Var BrandFullNameDA
+Var BrandFullName
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetOptions
+!insertmacro GetParameters
+!insertmacro GetSize
+
+; The test machines use this fallback key to run tests.
+; And anyone that wants to run tests themselves should already have
+; this installed.
+!define FallbackKey \
+ "SOFTWARE\Mozilla\MaintenanceService\3932ecacee736d366d6436db0f55bce4"
+
+!define CompanyName "Mozilla Corporation"
+!define BrandFullNameInternal ""
+
+; The following includes are custom.
+!include defines.nsi
+; We keep defines.nsi defined so that we get other things like
+; the version number, but we redefine BrandFullName
+!define MaintFullName "Mozilla Maintenance Service"
+!ifdef BrandFullName
+!undef BrandFullName
+!endif
+!define BrandFullName "${MaintFullName}"
+
+!include common.nsh
+!include locales.nsi
+
+VIAddVersionKey "FileDescription" "${MaintFullName} Installer"
+VIAddVersionKey "OriginalFilename" "maintenanceservice_installer.exe"
+
+Name "${MaintFullName}"
+OutFile "maintenanceservice_installer.exe"
+
+; Get installation folder from registry if available
+InstallDirRegKey HKLM "Software\Mozilla\MaintenanceService" ""
+
+SetOverwrite on
+
+; serviceinstall.cpp also uses this key, in case the path is changed, update
+; there too.
+!define MaintUninstallKey \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
+
+; Always install into the 32-bit location even if we have a 64-bit build.
+; This is because we use only 1 service for all Firefox channels.
+; Allow either x86 and x64 builds to exist at this location, depending on
+; what is the latest build.
+InstallDir "$PROGRAMFILES32\${MaintFullName}\"
+ShowUnInstDetails nevershow
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+
+;Interface Settings
+!define MUI_ABORTWARNING
+
+; Uninstaller Pages
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale
+; using " " for BrandingText will hide the "Nullsoft Install System..." branding
+BrandingText " "
+
+Function .onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ SetSilent silent
+
+ ${Unless} ${AtLeastWin10}
+ Abort
+ ${EndUnless}
+FunctionEnd
+
+Function un.onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ StrCpy $BrandFullNameDA "${MaintFullName}"
+ StrCpy $BrandFullName "${MaintFullName}"
+FunctionEnd
+
+Section "MaintenanceService"
+ AllowSkipFiles off
+
+ CreateDirectory $INSTDIR
+ SetOutPath $INSTDIR
+
+ ; If the service already exists, then it will be stopped when upgrading it
+ ; via the maintenanceservice_tmp.exe command executed below.
+ ; The maintenanceservice_tmp.exe command will rename the file to
+ ; maintenanceservice.exe if maintenanceservice_tmp.exe is newer.
+ ; If the service does not exist yet, we install it and drop the file on
+ ; disk as maintenanceservice.exe directly.
+ StrCpy $TempMaintServiceName "maintenanceservice.exe"
+ IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
+ StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
+ skipAlreadyExists:
+
+ ; We always write out a copy and then decide whether to install it or
+ ; not via calling its 'install' cmdline which works by version comparison.
+ CopyFiles /SILENT "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
+
+ ; The updater.ini file is only used when performing an install or upgrade,
+ ; and only if that install or upgrade is successful. If an old updater.ini
+ ; happened to be copied into the maintenance service installation directory
+ ; but the service was not newer, the updater.ini file would be unused.
+ ; It is used to fill the description of the service on success.
+ CopyFiles /SILENT "$EXEDIR\updater.ini" "$INSTDIR\updater.ini"
+
+ ; Install the application maintenance service.
+ ; If a service already exists, the command line parameter will stop the
+ ; service and only install itself if it is newer than the already installed
+ ; service. If successful it will remove the old maintenanceservice.exe
+ ; and replace it with maintenanceservice_tmp.exe.
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/Upgrade" $0
+ ${If} ${Errors}
+ ExecWait '"$INSTDIR\$TempMaintServiceName" install'
+ ${Else}
+ ; The upgrade cmdline is the same as install except
+ ; It will fail if the service isn't already installed.
+ ExecWait '"$INSTDIR\$TempMaintServiceName" upgrade'
+ ${EndIf}
+
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ ; Since the Maintenance service can be installed either x86 or x64,
+ ; always use the 64-bit registry.
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ ; Previous versions always created the uninstall key in the 32-bit registry.
+ ; Clean those old entries out if they still exist.
+ SetRegView 32
+ DeleteRegKey HKLM "${MaintUninstallKey}"
+ ; Preserve the lastused value before we switch to 64.
+ SetRegView lastused
+
+ SetRegView 64
+ ${EndIf}
+
+ WriteRegStr HKLM "${MaintUninstallKey}" "DisplayName" "${MaintFullName}"
+ WriteRegStr HKLM "${MaintUninstallKey}" "UninstallString" \
+ '"$INSTDIR\uninstall.exe"'
+ WriteRegStr HKLM "${MaintUninstallKey}" "DisplayIcon" \
+ "$INSTDIR\Uninstall.exe,0"
+ WriteRegStr HKLM "${MaintUninstallKey}" "DisplayVersion" "${AppVersion}"
+ WriteRegStr HKLM "${MaintUninstallKey}" "Publisher" "Mozilla"
+ WriteRegStr HKLM "${MaintUninstallKey}" "Comments" "${BrandFullName}"
+ WriteRegDWORD HKLM "${MaintUninstallKey}" "NoModify" 1
+ ${GetSize} "$INSTDIR" "/S=0K" $R2 $R3 $R4
+ WriteRegDWORD HKLM "${MaintUninstallKey}" "EstimatedSize" $R2
+
+ ; Write out that a maintenance service was attempted.
+ ; We do this because on upgrades we will check this value and we only
+ ; want to install once on the first upgrade to maintenance service.
+ ; Also write out that we are currently installed, preferences will check
+ ; this value to determine if we should show the service update pref.
+ WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Attempted" 1
+ WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Installed" 1
+ DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "FFPrefetchDisabled"
+
+ ; Included here for debug purposes only.
+ ; These keys are used to bypass the installation dir is a valid installation
+ ; check from the service so that tests can be run.
+ ; WriteRegStr HKLM "${FallbackKey}\0" "name" "Mozilla Corporation"
+ ; WriteRegStr HKLM "${FallbackKey}\0" "issuer" "DigiCert SHA2 Assured ID Code Signing CA"
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+SectionEnd
+
+; By renaming before deleting we improve things slightly in case
+; there is a file in use error. In this case a new install can happen.
+Function un.RenameDelete
+ Pop $9
+ ; If the .moz-delete file already exists previously, delete it
+ ; If it doesn't exist, the call is ignored.
+ ; We don't need to pass /REBOOTOK here since it was already marked that way
+ ; if it exists.
+ Delete "$9.moz-delete"
+ Rename "$9" "$9.moz-delete"
+ ${If} ${Errors}
+ Delete /REBOOTOK "$9"
+ ${Else}
+ Delete /REBOOTOK "$9.moz-delete"
+ ${EndIf}
+ ClearErrors
+FunctionEnd
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; NOTE: The maintenance service uninstaller does not currently get updated when
+; the service itself does during application updates. Under normal use, only
+; running the Firefox installer will generate a new maintenance service
+; uninstaller. That means anything added here will not be seen by users until
+; they run a new Firefox installer. Fixing this is tracked in
+; https://bugzilla.mozilla.org/show_bug.cgi?id=1665193
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Section "Uninstall"
+ ; Delete the service so that no updates will be attempted
+ ExecWait '"$INSTDIR\maintenanceservice.exe" uninstall'
+
+ Push "$INSTDIR\updater.ini"
+ Call un.RenameDelete
+ Push "$INSTDIR\maintenanceservice.exe"
+ Call un.RenameDelete
+ Push "$INSTDIR\maintenanceservice_tmp.exe"
+ Call un.RenameDelete
+ Push "$INSTDIR\maintenanceservice.old"
+ Call un.RenameDelete
+ Push "$INSTDIR\Uninstall.exe"
+ Call un.RenameDelete
+ Push "$INSTDIR\update\updater.ini"
+ Call un.RenameDelete
+ Push "$INSTDIR\update\updater.exe"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-1.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-2.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-3.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-4.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-5.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-6.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-7.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-8.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-9.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-10.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-install.log"
+ Call un.RenameDelete
+ Push "$INSTDIR\logs\maintenanceservice-uninstall.log"
+ Call un.RenameDelete
+ SetShellVarContext all
+ Push "$APPDATA\Mozilla\logs\maintenanceservice.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-1.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-2.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-3.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-4.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-5.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-6.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-7.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-8.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-9.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-10.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-install.log"
+ Call un.RenameDelete
+ Push "$APPDATA\Mozilla\logs\maintenanceservice-uninstall.log"
+ Call un.RenameDelete
+ RMDir /REBOOTOK "$APPDATA\Mozilla\logs"
+ RMDir /REBOOTOK "$APPDATA\Mozilla"
+ RMDir /REBOOTOK "$INSTDIR\logs"
+ RMDir /REBOOTOK "$INSTDIR\update"
+ RMDir /REBOOTOK "$INSTDIR\UpdateLogs"
+ RMDir /REBOOTOK "$INSTDIR"
+
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+ DeleteRegKey HKLM "${MaintUninstallKey}"
+ DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "Installed"
+ DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "FFPrefetchDisabled"
+ DeleteRegKey HKLM "${FallbackKey}\"
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+SectionEnd
diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh
new file mode 100755
index 0000000000..f0caece663
--- /dev/null
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -0,0 +1,1826 @@
+# 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/.
+
+; Generated by DeriveCapabilitySidsFromName with the name "lpacFirefoxInstallFiles"
+!define LpacFirefoxInstallFilesSid "S-1-15-3-1024-1238444810-1356253261-2257478630-1143196962-1563090664-2414759320-1282101916-4218287853"
+
+!macro PostUpdate
+ ${CreateShortcutsLog}
+
+ ; Remove registry entries for non-existent apps and for apps that point to our
+ ; install location in the Software\Mozilla key and uninstall registry entries
+ ; that point to our install location for both HKCU and HKLM.
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+
+ ; setup the application model id registration value
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU"
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM"
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+ ${FixShellIconHandler} "HKLM"
+ ${SetAppLSPCategories} ${LSP_CATEGORIES}
+
+ ; Add the Firewall entries after an update
+ Call AddFirewallEntries
+
+ ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
+ ${If} "$0" != "${GREVersion}"
+ WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
+ ${EndIf}
+ ${EndIf}
+
+ ; Update the name/icon/AppModelID of our shortcuts as needed, then update the
+ ; lastwritetime of the Start Menu shortcut to clear the tile icon cache.
+ ; Do this for both shell contexts in case the user has shortcuts in multiple
+ ; locations, then restore the previous context at the end.
+ SetShellVarContext all
+ ${UpdateShortcutsBranding}
+ ${TouchStartMenuShortcut}
+ Call FixShortcutAppModelIDs
+ SetShellVarContext current
+ ${UpdateShortcutsBranding}
+ ${TouchStartMenuShortcut}
+ Call FixShortcutAppModelIDs
+ ${If} $TmpVal == "HKLM"
+ SetShellVarContext all
+ ${ElseIf} $TmpVal == "HKCU"
+ SetShellVarContext current
+ ${EndIf}
+
+ ${RemoveDeprecatedKeys}
+ ${Set32to64DidMigrateReg}
+
+ ${SetAppKeys}
+ ${FixClassKeys}
+ ${SetUninstallKeys}
+ ${If} $TmpVal == "HKLM"
+ ${SetStartMenuInternet} HKLM
+ ${ElseIf} $TmpVal == "HKCU"
+ ${SetStartMenuInternet} HKCU
+ ${EndIf}
+
+ ; Remove files that may be left behind by the application in the
+ ; VirtualStore directory.
+ ${CleanVirtualStore}
+
+ ${RemoveDeprecatedFiles}
+
+ ; Fix the distribution.ini file if applicable
+ ${FixDistributionsINI}
+
+ ; https://bugzilla.mozilla.org/show_bug.cgi?id=1616355
+ ; Migrate postSigningData file if present, and if it doesn't already exist.
+ ${GetLocalAppDataFolder} $0
+ ${If} ${FileExists} "$INSTDIR\postSigningData"
+ ; If it already exists, just delete the appdata one.
+ ; It's possible this was for a different install, but it's impossible to
+ ; know for sure, so we may as well just get rid of it.
+ Delete /REBOOTOK "$0\Mozilla\Firefox\postSigningData"
+ ${Else}
+ ${If} ${FileExists} "$0\Mozilla\Firefox\postSigningData"
+ Rename "$0\Mozilla\Firefox\postSigningData" "$INSTDIR\postSigningData"
+ ${EndIf}
+ ${EndIf}
+
+ RmDir /r /REBOOTOK "$INSTDIR\${TO_BE_DELETED}"
+
+ ; Register AccessibleMarshal.dll with COM (this requires write access to HKLM)
+ ${RegisterAccessibleMarshal}
+
+ ; Record the Windows Error Reporting module
+ WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\RuntimeExceptionHelperModules" "$INSTDIR\mozwer.dll" 0
+
+ ; Apply LPAC permissions to install directory.
+ Push "Marker"
+ AccessControl::GrantOnFile \
+ "$INSTDIR" "(${LpacFirefoxInstallFilesSid})" "GenericRead + GenericExecute"
+ Pop $TmpVal ; get "Marker" or error msg
+ ${If} $TmpVal != "Marker"
+ Pop $TmpVal ; get "Marker"
+ ${EndIf}
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ Call IsUserAdmin
+ Pop $R0
+ ${If} $R0 == "true"
+ ; Only proceed if we have HKLM write access
+ ${AndIf} $TmpVal == "HKLM"
+ ; We check to see if the maintenance service install was already attempted.
+ ; Since the Maintenance service can be installed either x86 or x64,
+ ; always use the 64-bit registry for checking if an attempt was made.
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+ ReadRegDWORD $5 HKLM "Software\Mozilla\MaintenanceService" "Attempted"
+ ClearErrors
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+
+ ; Add the registry keys for allowed certificates.
+ ${AddMaintCertKeys}
+
+ ; If the maintenance service is already installed, do nothing.
+ ; The maintenance service will launch:
+ ; maintenanceservice_installer.exe /Upgrade to upgrade the maintenance
+ ; service if necessary. If the update was done from updater.exe without
+ ; the service (i.e. service is failing), updater.exe will do the update of
+ ; the service. The reasons we do not do it here is because we don't want
+ ; to have to prompt for limited user accounts when the service isn't used
+ ; and we currently call the PostUpdate twice, once for the user and once
+ ; for the SYSTEM account. Also, this would stop the maintenance service
+ ; and we need a return result back to the service when run that way.
+ ${If} $5 == ""
+ ; An install of maintenance service was never attempted.
+ ; We know we are an Admin and that we have write access into HKLM
+ ; based on the above checks, so attempt to just run the EXE.
+ ; In the worst case, in case there is some edge case with the
+ ; IsAdmin check and the permissions check, the maintenance service
+ ; will just fail to be attempted to be installed.
+ nsExec::Exec "$\"$INSTDIR\maintenanceservice_installer.exe$\""
+ ${EndIf}
+ ${EndIf}
+!endif
+
+!ifdef MOZ_LAUNCHER_PROCESS
+ ${ResetLauncherProcessDefaults}
+!endif
+
+ ${WriteToastNotificationRegistration} $TmpVal
+
+; Make sure the scheduled task registration for the default browser agent gets
+; updated, but only if we're not the instance of PostUpdate that was started
+; by the service, because this needs to run as the actual user. Also, don't do
+; that if the installer was told not to register the agent task at all.
+!ifdef MOZ_DEFAULT_BROWSER_AGENT
+${If} $TmpVal == "HKCU"
+ ClearErrors
+ ReadRegDWORD $0 HKCU "Software\Mozilla\${AppName}\Installer\$AppUserModelID" \
+ "DidRegisterDefaultBrowserAgent"
+ ${If} $0 != 0
+ ${OrIf} ${Errors}
+ ExecWait '"$INSTDIR\default-browser-agent.exe" register-task $AppUserModelID'
+ ${EndIf}
+${ElseIf} $TmpVal == "HKLM"
+ ; If we're the privileged PostUpdate, make sure that the unprivileged one
+ ; will have permission to create a task by clearing out the old one first.
+ ExecWait '"$INSTDIR\default-browser-agent.exe" unregister-task $AppUserModelID'
+${EndIf}
+!endif
+
+${RemoveDefaultBrowserAgentShortcut}
+!macroend
+!define PostUpdate "!insertmacro PostUpdate"
+
+; Update the last modified time on the Start Menu shortcut, so that its icon
+; gets refreshed. Should be called on Win8+ after UpdateShortcutBranding.
+!macro TouchStartMenuShortcut
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ FileOpen $0 "$SMPROGRAMS\${BrandShortName}.lnk" a
+ ${IfNot} ${Errors}
+ System::Call '*(i, i) p .r1'
+ System::Call 'kernel32::GetSystemTimeAsFileTime(p r1)'
+ System::Call 'kernel32::SetFileTime(p r0, i 0, i 0, p r1) i .r2'
+ System::Free $1
+ FileClose $0
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define TouchStartMenuShortcut "!insertmacro TouchStartMenuShortcut"
+
+!macro AddPrivateBrowsingShortcut
+ ${IfNot} ${FileExists} "$SMPROGRAMS\$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk"
+ CreateShortcut "$SMPROGRAMS\$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk" "$INSTDIR\${PrivateBrowsingEXE}" "" "$INSTDIR\${PrivateBrowsingEXE}" ${IDI_PBICON_PB_EXE_ZERO_BASED}
+ ShellLink::SetShortcutWorkingDirectory "$SMPROGRAMS\$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk" "$INSTDIR"
+ ShellLink::SetShortcutDescription "$SMPROGRAMS\$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk" "$(PRIVATE_BROWSING_SHORTCUT_TITLE)"
+ ApplicationID::Set "$SMPROGRAMS\$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk" "$AppUserModelID;PrivateBrowsingAUMID" "true"
+ ${LogStartMenuShortcut} "$(PRIVATE_BROWSING_SHORTCUT_TITLE).lnk"
+ ${EndIf}
+!macroend
+!define AddPrivateBrowsingShortcut "!insertmacro AddPrivateBrowsingShortcut"
+
+!macro SetAsDefaultAppGlobal
+ ${RemoveDeprecatedKeys} ; Does not use SHCTX
+
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${SetHandlers} ; Uses SHCTX
+ ${SetStartMenuInternet} "HKLM"
+ ${FixShellIconHandler} "HKLM"
+ ${ShowShortcuts}
+!macroend
+!define SetAsDefaultAppGlobal "!insertmacro SetAsDefaultAppGlobal"
+
+; Removes shortcuts for this installation. This should also remove the
+; application from Open With for the file types the application handles
+; (bug 370480).
+!macro HideShortcuts
+ ; Find the correct registry path to clear IconsVisible.
+ StrCpy $R1 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
+ ReadRegDWORD $0 HKLM "$R1" "ShowIconsCommand"
+ ${If} ${Errors}
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $0
+ StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
+ ${EndIf}
+ WriteRegDWORD HKLM "$R1" "IconsVisible" 0
+ WriteRegDWORD HKCU "$R1" "IconsVisible" 0
+
+ SetShellVarContext all ; Set $DESKTOP to All Users
+ ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ SetShellVarContext current ; Set $DESKTOP to the current user's desktop
+ ${EndUnless}
+
+ ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ ShellLink::GetShortCutArgs "$DESKTOP\${BrandShortName}.lnk"
+ Pop $0
+ ${If} "$0" == ""
+ ShellLink::GetShortCutTarget "$DESKTOP\${BrandShortName}.lnk"
+ Pop $0
+ ${GetLongPath} "$0" $0
+ ${If} "$0" == "$INSTDIR\${FileMainEXE}"
+ Delete "$DESKTOP\${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ SetShellVarContext all ; Set $SMPROGRAMS to All Users
+ ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ SetShellVarContext current ; Set $SMPROGRAMS to the current user's Start
+ ; Menu Programs directory
+ ${EndUnless}
+
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ ShellLink::GetShortCutArgs "$SMPROGRAMS\${BrandShortName}.lnk"
+ Pop $0
+ ${If} "$0" == ""
+ ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandShortName}.lnk"
+ Pop $0
+ ${GetLongPath} "$0" $0
+ ${If} "$0" == "$INSTDIR\${FileMainEXE}"
+ Delete "$SMPROGRAMS\${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk"
+ ShellLink::GetShortCutArgs "$QUICKLAUNCH\${BrandShortName}.lnk"
+ Pop $0
+ ${If} "$0" == ""
+ ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandShortName}.lnk"
+ Pop $0
+ ${GetLongPath} "$0" $0
+ ${If} "$0" == "$INSTDIR\${FileMainEXE}"
+ Delete "$QUICKLAUNCH\${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define HideShortcuts "!insertmacro HideShortcuts"
+
+; Adds shortcuts for this installation. This should also add the application
+; to Open With for the file types the application handles (bug 370480).
+!macro ShowShortcuts
+ ; Find the correct registry path to set IconsVisible.
+ StrCpy $R1 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
+ ReadRegDWORD $0 HKLM "$R1" "ShowIconsCommand"
+ ${If} ${Errors}
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $0
+ StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
+ ${EndIf}
+ WriteRegDWORD HKLM "$R1" "IconsVisible" 1
+ WriteRegDWORD HKCU "$R1" "IconsVisible" 1
+
+ SetShellVarContext all ; Set $DESKTOP to All Users
+ ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" "$AppUserModelID" "true"
+ ${EndIf}
+ ${Else}
+ SetShellVarContext current ; Set $DESKTOP to the current user's desktop
+ ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" "$AppUserModelID" "true"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+ ${EndIf}
+ ${EndUnless}
+
+ SetShellVarContext all ; Set $SMPROGRAMS to All Users
+ ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true"
+ ${EndIf}
+ ${Else}
+ SetShellVarContext current ; Set $SMPROGRAMS to the current user's Start
+ ; Menu Programs directory
+ ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+ ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+ ${EndIf}
+ ${EndUnless}
+!macroend
+!define ShowShortcuts "!insertmacro ShowShortcuts"
+
+; Update the branding name on all shortcuts our installer created
+; to convert from BrandFullName (which is what we used to name shortcuts)
+; to BrandShortName (which is what we now name shortcuts). We only rename
+; desktop and start menu shortcuts, because touching taskbar pins often
+; (but inconsistently) triggers various broken behaviors in the shell.
+; This assumes SHCTX is set correctly.
+!macro UpdateShortcutsBranding
+ ${UpdateOneShortcutBranding} "STARTMENU" "$SMPROGRAMS"
+ ${UpdateOneShortcutBranding} "DESKTOP" "$DESKTOP"
+!macroend
+!define UpdateShortcutsBranding "!insertmacro UpdateShortcutsBranding"
+
+!macro UpdateOneShortcutBranding LOG_SECTION SHORTCUT_DIR
+ ; Only try to rename the shortcuts found in the shortcuts log, to avoid
+ ; blowing away a name that the user created.
+ ${GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R9
+ ${If} ${FileExists} "$R9"
+ ClearErrors
+ ; The shortcuts log contains a numbered list of entries for each section,
+ ; but we never actually create more than one.
+ ReadINIStr $R8 "$R9" "${LOG_SECTION}" "Shortcut0"
+ ${IfNot} ${Errors}
+ ${If} ${FileExists} "${SHORTCUT_DIR}\$R8"
+ ShellLink::GetShortCutTarget "${SHORTCUT_DIR}\$R8"
+ Pop $R7
+ ${GetLongPath} "$R7" $R7
+ ${If} $R7 == "$INSTDIR\${FileMainEXE}"
+ ${AndIf} $R8 != "${BrandShortName}.lnk"
+ ${AndIfNot} ${FileExists} "${SHORTCUT_DIR}\${BrandShortName}.lnk"
+ ClearErrors
+ Rename "${SHORTCUT_DIR}\$R8" "${SHORTCUT_DIR}\${BrandShortName}.lnk"
+ ${IfNot} ${Errors}
+ ; Update the shortcut log manually instead of calling LogShortcut
+ ; because it would add a Shortcut1 entry, and we really do want to
+ ; overwrite the existing entry 0, since we just renamed the file.
+ WriteINIStr "$R9" "${LOG_SECTION}" "Shortcut0" \
+ "${BrandShortName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define UpdateOneShortcutBranding "!insertmacro UpdateOneShortcutBranding"
+
+; Remove a shortcut unintentionally added by the default browser agent (bug 1672957, 1681207)
+!macro RemoveDefaultBrowserAgentShortcut
+ Push $0
+ Push $1
+ Push $2
+ Push $3
+
+ ; Get the current user's Start Menu Programs.
+ ${GetProgramsFolder} $1
+
+ ; The shortcut would have been named MOZ_BASE_NAME regardless of branding.
+ ; According to defines.nsi.in AppName should match application.ini, and application.ini.in sets
+ ; [App] Name from MOZ_BASE_NAME.
+ StrCpy $1 "$1\${AppName}.lnk"
+ ShellLink::GetShortCutTarget $1
+ Pop $0
+
+ ; ShellLink::GetShortCutTarget, and the underlying IShellLink::GetPath(), have an issue
+ ; where "C:\Program Files" becomes "C:\Program Files (x86)" in some cases.
+ ; It should be OK to remove the shortcut (which matches our app name) even if it isn't from this
+ ; install, as long as the file name portion of the target path matches.
+ StrCpy $2 "\default-browser-agent.exe"
+ StrLen $3 $2
+ ; Select the substring to match from the end of the target path.
+ StrCpy $0 $0 $3 -$3
+ ${If} $0 == $2
+ Delete $1
+ ${EndIf}
+
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+!macroend
+!define RemoveDefaultBrowserAgentShortcut "!insertmacro RemoveDefaultBrowserAgentShortcut"
+
+!macro AddAssociationIfNoneExist FILE_TYPE KEY
+ ClearErrors
+ EnumRegKey $7 HKCR "${FILE_TYPE}" 0
+ ${If} ${Errors}
+ WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}" "" ${KEY}
+ ${EndIf}
+ WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}\OpenWithProgids" ${KEY} ""
+!macroend
+!define AddAssociationIfNoneExist "!insertmacro AddAssociationIfNoneExist"
+
+; Adds the protocol and file handler registry entries for making Firefox the
+; default handler (uses SHCTX).
+!macro SetHandlers
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+
+ ; See if we're using path hash suffixed registry keys for this install.
+ StrCpy $5 ""
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $2
+ ReadRegStr $0 SHCTX "Software\Clients\StartMenuInternet\$2\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 != $8
+ StrCpy $5 "-$AppUserModelID"
+ ${EndIf}
+
+ StrCpy $0 "SOFTWARE\Classes"
+ StrCpy $2 "$\"$8$\" -osint -url $\"%1$\""
+
+ ; Associate the file handlers with FirefoxHTML, if they aren't already.
+ ReadRegStr $6 SHCTX "$0\.htm" ""
+ ${WordFind} "$6" "-" "+1{" $6
+ ${If} "$6" != "FirefoxHTML"
+ WriteRegStr SHCTX "$0\.htm" "" "FirefoxHTML$5"
+ ${EndIf}
+
+ ReadRegStr $6 SHCTX "$0\.html" ""
+ ${WordFind} "$6" "-" "+1{" $6
+ ${If} "$6" != "FirefoxHTML"
+ WriteRegStr SHCTX "$0\.html" "" "FirefoxHTML$5"
+ ${EndIf}
+
+ ReadRegStr $6 SHCTX "$0\.shtml" ""
+ ${WordFind} "$6" "-" "+1{" $6
+ ${If} "$6" != "FirefoxHTML"
+ WriteRegStr SHCTX "$0\.shtml" "" "FirefoxHTML$5"
+ ${EndIf}
+
+ ReadRegStr $6 SHCTX "$0\.xht" ""
+ ${WordFind} "$6" "-" "+1{" $6
+ ${If} "$6" != "FirefoxHTML"
+ WriteRegStr SHCTX "$0\.xht" "" "FirefoxHTML$5"
+ ${EndIf}
+
+ ReadRegStr $6 SHCTX "$0\.xhtml" ""
+ ${WordFind} "$6" "-" "+1{" $6
+ ${If} "$6" != "FirefoxHTML"
+ WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML$5"
+ ${EndIf}
+
+
+ ; Keep this list synchronized with
+ ; https://searchfox.org/mozilla-central/source/browser/installer/windows/msix/AppxManifest.xml.in.
+ ; and `os.environment.launched_to_handle` and `os.environment.invoked_to_handle` telemetry in
+ ; https://searchfox.org/mozilla-central/source/browser/components/BrowserContentHandler.sys.mjs.
+ ${AddAssociationIfNoneExist} ".oga" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".ogg" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".ogv" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".webm" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".svg" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".webp" "FirefoxHTML$5"
+ ${AddAssociationIfNoneExist} ".avif" "FirefoxHTML$5"
+
+ ${AddAssociationIfNoneExist} ".pdf" "FirefoxPDF$5"
+
+ ; An empty string is used for the 5th param because FirefoxHTML- is not a
+ ; protocol handler. Ditto for FirefoxPDF-.
+ ${AddDisabledDDEHandlerValues} "FirefoxHTML$5" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} HTML Document" ""
+
+ ${AddDisabledDDEHandlerValues} "FirefoxPDF$5" "$2" "$8,${IDI_DOCUMENT_PDF_ZERO_BASED}" \
+ "${AppRegName} PDF Document" ""
+
+ ${AddDisabledDDEHandlerValues} "FirefoxURL$5" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "${AppRegName} URL" \
+ "true"
+ ; An empty string is used for the 4th & 5th params because the following
+ ; protocol handlers already have a display name and the additional keys
+ ; required for a protocol handler.
+ ${AddDisabledDDEHandlerValues} "http" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+ ${AddDisabledDDEHandlerValues} "https" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+ ${AddDisabledDDEHandlerValues} "mailto" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+!macroend
+!define SetHandlers "!insertmacro SetHandlers"
+
+!macro WriteApplicationsSupportedType RegKey Type
+ WriteRegStr ${RegKey} "Software\Classes\Applications\${FileMainEXE}\SupportedTypes" "${Type}" ""
+!macroend
+!define WriteApplicationsSupportedType "!insertmacro WriteApplicationsSupportedType"
+
+; Adds the HKLM\Software\Clients\StartMenuInternet\Firefox-[pathhash] registry
+; entries (does not use SHCTX).
+;
+; The values for StartMenuInternet are only valid under HKLM and there can only
+; be one installation registerred under StartMenuInternet per application since
+; the key name is derived from the main application executable.
+;
+; In Windows 8 this changes slightly, you can store StartMenuInternet entries in
+; HKCU. The icon in start menu for StartMenuInternet is deprecated as of Win7,
+; but the subkeys are what's important. Control panel default programs looks
+; for them only in HKLM pre win8.
+;
+; The StartMenuInternet key and friends are documented at
+; https://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=vs.85).aspx
+;
+; This function also writes our RegisteredApplications entry, which gets us
+; listed in the Settings app's default browser options on Windows 8+, and in
+; Set Program Access and Defaults on earlier versions.
+!macro SetStartMenuInternet RegKey
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ ${GetLongPath} "$INSTDIR\uninstall\helper.exe" $7
+
+ ; If we already have keys at the old FIREFOX.EXE path, then just update those.
+ ; We have to be careful to update the existing keys in place so that we don't
+ ; create duplicate keys for the same installation, or cause Windows to think
+ ; something "suspicious" has happened and it should reset the default browser.
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $1
+ ReadRegStr $0 ${RegKey} "Software\Clients\StartMenuInternet\$1\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 != $8
+ StrCpy $1 "${AppRegName}-$AppUserModelID"
+ StrCpy $2 "-$AppUserModelID"
+ ${Else}
+ StrCpy $2 ""
+ ${EndIf}
+ StrCpy $0 "Software\Clients\StartMenuInternet\$1"
+
+ WriteRegStr ${RegKey} "$0" "" "${BrandFullName}"
+
+ WriteRegStr ${RegKey} "$0\DefaultIcon" "" "$8,${IDI_APPICON_ZERO_BASED}"
+
+ ; The Reinstall Command is defined at
+ ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/registeringapps.asp
+ WriteRegStr ${RegKey} "$0\InstallInfo" "HideIconsCommand" "$\"$7$\" /HideShortcuts"
+ WriteRegStr ${RegKey} "$0\InstallInfo" "ShowIconsCommand" "$\"$7$\" /ShowShortcuts"
+ WriteRegStr ${RegKey} "$0\InstallInfo" "ReinstallCommand" "$\"$7$\" /SetAsDefaultAppGlobal"
+ WriteRegDWORD ${RegKey} "$0\InstallInfo" "IconsVisible" 1
+
+ WriteRegStr ${RegKey} "$0\shell\open\command" "" "$\"$8$\""
+
+ WriteRegStr ${RegKey} "$0\shell\properties" "" "$(CONTEXT_OPTIONS)"
+ WriteRegStr ${RegKey} "$0\shell\properties\command" "" "$\"$8$\" -preferences"
+
+ WriteRegStr ${RegKey} "$0\shell\safemode" "" "$(CONTEXT_SAFE_MODE)"
+ WriteRegStr ${RegKey} "$0\shell\safemode\command" "" "$\"$8$\" -safe-mode"
+
+ ; Capabilities registry keys
+ WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
+ WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationIcon" "$8,${IDI_APPICON_ZERO_BASED}"
+ WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationName" "${BrandShortName}"
+
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".htm" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".html" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".shtml" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".svg" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".webp" "FirefoxHTML$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".avif" "FirefoxHTML$2"
+
+ WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".pdf" "FirefoxPDF$2"
+
+ WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "$1"
+
+ ; In the past, we supported ftp. Since we don't delete and re-create the
+ ; entire key, we need to remove any existing registration.
+ DeleteRegValue ${RegKey} "$0\Capabilities\URLAssociations" "ftp"
+
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http" "FirefoxURL$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https" "FirefoxURL$2"
+ WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "mailto" "FirefoxURL$2"
+
+ WriteRegStr ${RegKey} "Software\RegisteredApplications" "$1" "$0\Capabilities"
+
+ ; This key would be created by the Open With dialog when a user creates an
+ ; association for us with a file type that we haven't registered as a handler
+ ; for. We need to preemptively create it ourselves so that we can control the
+ ; command line that's used to launch us in that situation. If it's too late
+ ; and one already exists, then we need to edit its command line to make sure
+ ; it contains the -osint flag.
+ ReadRegStr $6 ${RegKey} "Software\Classes\Applications\${FileMainEXE}\shell\open\command" ""
+ ${If} $6 != ""
+ ${GetPathFromString} "$6" $6
+ WriteRegStr ${RegKey} "Software\Classes\Applications\${FileMainEXE}\shell\open\command" \
+ "" "$\"$6$\" -osint -url $\"%1$\""
+ ${Else}
+ WriteRegStr ${RegKey} "Software\Classes\Applications\${FileMainEXE}\shell\open\command" \
+ "" "$\"$8$\" -osint -url $\"%1$\""
+ ; Make sure files associated this way use the document icon instead of the
+ ; application icon.
+ WriteRegStr ${RegKey} "Software\Classes\Applications\${FileMainEXE}\DefaultIcon" \
+ "" "$8,${IDI_DOCUMENT_ZERO_BASED}"
+ ; If we're going to create this key at all, we also need to list our supported
+ ; file types in it, because otherwise we'll be shown as a suggestion for every
+ ; single file type, whether we support it in any way or not.
+ ; We take a more expansive approach to the set of file types registered
+ ; here compared to elsewhere because this key is interpreted by the OS as
+ ; containing every file type that we can possibly open, so if something
+ ; isn't listed it assumes we can't open it and hides us from e.g. the Open
+ ; With context menu, even if the user has tried to add us there manually.
+ ; The list here was derived from the file /layout/build/components.conf,
+ ; filtered down to only those types which make sense to open on their own
+ ; in Firefox, basically meaning that plain text file types were left out,
+ ; but not JSON or XML types because we have specific viewers for those.
+ ${WriteApplicationsSupportedType} ${RegKey} ".apng"
+ ${WriteApplicationsSupportedType} ${RegKey} ".bmp"
+ ${WriteApplicationsSupportedType} ${RegKey} ".flac"
+ ${WriteApplicationsSupportedType} ${RegKey} ".gif"
+ ${WriteApplicationsSupportedType} ${RegKey} ".htm"
+ ${WriteApplicationsSupportedType} ${RegKey} ".html"
+ ${WriteApplicationsSupportedType} ${RegKey} ".ico"
+ ${WriteApplicationsSupportedType} ${RegKey} ".jfif"
+ ${WriteApplicationsSupportedType} ${RegKey} ".jpeg"
+ ${WriteApplicationsSupportedType} ${RegKey} ".jpg"
+ ${WriteApplicationsSupportedType} ${RegKey} ".json"
+ ${WriteApplicationsSupportedType} ${RegKey} ".m4a"
+ ${WriteApplicationsSupportedType} ${RegKey} ".mp3"
+ ${WriteApplicationsSupportedType} ${RegKey} ".oga"
+ ${WriteApplicationsSupportedType} ${RegKey} ".ogg"
+ ${WriteApplicationsSupportedType} ${RegKey} ".ogv"
+ ${WriteApplicationsSupportedType} ${RegKey} ".opus"
+ ${WriteApplicationsSupportedType} ${RegKey} ".pdf"
+ ${WriteApplicationsSupportedType} ${RegKey} ".pjpeg"
+ ${WriteApplicationsSupportedType} ${RegKey} ".pjp"
+ ${WriteApplicationsSupportedType} ${RegKey} ".png"
+ ${WriteApplicationsSupportedType} ${RegKey} ".rdf"
+ ${WriteApplicationsSupportedType} ${RegKey} ".shtml"
+ ${WriteApplicationsSupportedType} ${RegKey} ".svg"
+ ${WriteApplicationsSupportedType} ${RegKey} ".webm"
+ ${WriteApplicationsSupportedType} ${RegKey} ".webp"
+ ${WriteApplicationsSupportedType} ${RegKey} ".avif"
+ ${WriteApplicationsSupportedType} ${RegKey} ".xht"
+ ${WriteApplicationsSupportedType} ${RegKey} ".xhtml"
+ ${WriteApplicationsSupportedType} ${RegKey} ".xml"
+ ${EndIf}
+!macroend
+!define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
+
+; Add registry keys to support the Firefox 32 bit to 64 bit migration. These
+; registry entries are not removed on uninstall at this time. After the Firefox
+; 32 bit to 64 bit migration effort is completed these registry entries can be
+; removed during install, post update, and uninstall.
+!macro Set32to64DidMigrateReg
+ ${GetLongPath} "$INSTDIR" $1
+ ; These registry keys are always in the 32 bit hive since they are never
+ ; needed by a Firefox 64 bit install unless it has been updated from Firefox
+ ; 32 bit.
+ SetRegView 32
+
+!ifdef HAVE_64BIT_BUILD
+
+ ; Running Firefox 64 bit on Windows 64 bit
+ ClearErrors
+ ReadRegDWORD $2 HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+ ; If there were no errors then the system was updated from Firefox 32 bit to
+ ; Firefox 64 bit and if the value is already 1 then the registry value has
+ ; already been updated in the HKLM registry.
+ ${IfNot} ${Errors}
+ ${AndIf} $2 != 1
+ ClearErrors
+ WriteRegDWORD HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+ ${If} ${Errors}
+ ; There was an error writing to HKLM so just write it to HKCU
+ WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+ ${Else}
+ ; This will delete the value from HKCU if it exists
+ DeleteRegValue HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+ ${EndIf}
+ ${EndIf}
+
+ ClearErrors
+ ReadRegDWORD $2 HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+ ; If there were no errors then the system was updated from Firefox 32 bit to
+ ; Firefox 64 bit and if the value is already 1 then the registry value has
+ ; already been updated in the HKCU registry.
+ ${IfNot} ${Errors}
+ ${AndIf} $2 != 1
+ WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 1
+ ${EndIf}
+
+!else
+
+ ; Running Firefox 32 bit
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ ; Running Firefox 32 bit on a Windows 64 bit system
+ ClearErrors
+ ReadRegDWORD $2 HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1"
+ ; If there were errors the value doesn't exist yet.
+ ${If} ${Errors}
+ ClearErrors
+ WriteRegDWORD HKLM "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 0
+ ; If there were errors write the value in HKCU.
+ ${If} ${Errors}
+ WriteRegDWORD HKCU "Software\Mozilla\${AppName}\32to64DidMigrate" "$1" 0
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+!endif
+
+ ClearErrors
+ SetRegView lastused
+!macroend
+!define Set32to64DidMigrateReg "!insertmacro Set32to64DidMigrateReg"
+
+; The IconHandler reference for FirefoxHTML can end up in an inconsistent state
+; due to changes not being detected by the IconHandler for side by side
+; installs (see bug 268512). The symptoms can be either an incorrect icon or no
+; icon being displayed for files associated with Firefox (does not use SHCTX).
+!macro FixShellIconHandler RegKey
+ ; Find the correct key to update, either FirefoxHTML or FirefoxHTML-[PathHash]
+ StrCpy $3 "FirefoxHTML-$AppUserModelID"
+ ClearErrors
+ ReadRegStr $0 ${RegKey} "Software\Classes\$3\DefaultIcon" ""
+ ${If} ${Errors}
+ StrCpy $3 "FirefoxHTML"
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $1 ${RegKey} "Software\Classes\$3\ShellEx\IconHandler" ""
+ ${Unless} ${Errors}
+ ReadRegStr $1 ${RegKey} "Software\Classes\$3\DefaultIcon" ""
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $2
+ ${If} "$1" != "$2,${IDI_DOCUMENT_ZERO_BASED}"
+ WriteRegStr ${RegKey} "Software\Classes\$3\DefaultIcon" "" "$2,${IDI_DOCUMENT_ZERO_BASED}"
+ ${EndIf}
+ ${EndUnless}
+!macroend
+!define FixShellIconHandler "!insertmacro FixShellIconHandler"
+
+; Add Software\Mozilla\ registry entries (uses SHCTX).
+!macro SetAppKeys
+ ; Check if this is an ESR release and if so add registry values so it is
+ ; possible to determine that this is an ESR install (bug 726781).
+ ClearErrors
+ ${WordFind} "${UpdateChannel}" "esr" "E#" $3
+ ${If} ${Errors}
+ StrCpy $3 ""
+ ${Else}
+ StrCpy $3 " ESR"
+ ${EndIf}
+
+ ${GetLongPath} "$INSTDIR" $8
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion}$3 (${ARCH} ${AB_CD})\Main"
+ ${WriteRegStr2} $TmpVal "$0" "Install Directory" "$8" 0
+ ${WriteRegStr2} $TmpVal "$0" "PathToExe" "$8\${FileMainEXE}" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion}$3 (${ARCH} ${AB_CD})\Uninstall"
+ ${WriteRegStr2} $TmpVal "$0" "Description" "${BrandFullNameInternal} ${AppVersion}$3 (${ARCH} ${AB_CD})" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion}$3 (${ARCH} ${AB_CD})"
+ ${WriteRegStr2} $TmpVal "$0" "" "${AppVersion}$3 (${ARCH} ${AB_CD})" 0
+ ${If} "$3" == ""
+ DeleteRegValue SHCTX "$0" "ESR"
+ ${Else}
+ ${WriteRegDWORD2} $TmpVal "$0" "ESR" 1 0
+ ${EndIf}
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}$3\bin"
+ ${WriteRegStr2} $TmpVal "$0" "PathToExe" "$8\${FileMainEXE}" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}$3\extensions"
+ ${WriteRegStr2} $TmpVal "$0" "Components" "$8\components" 0
+ ${WriteRegStr2} $TmpVal "$0" "Plugins" "$8\plugins" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}$3"
+ ${WriteRegStr2} $TmpVal "$0" "GeckoVer" "${GREVersion}" 0
+ ${If} "$3" == ""
+ DeleteRegValue SHCTX "$0" "ESR"
+ ${Else}
+ ${WriteRegDWORD2} $TmpVal "$0" "ESR" 1 0
+ ${EndIf}
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}$3"
+ ${WriteRegStr2} $TmpVal "$0" "" "${GREVersion}" 0
+ ${WriteRegStr2} $TmpVal "$0" "CurrentVersion" "${AppVersion}$3 (${ARCH} ${AB_CD})" 0
+!macroend
+!define SetAppKeys "!insertmacro SetAppKeys"
+
+; Add uninstall registry entries. This macro tests for write access to determine
+; if the uninstall keys should be added to HKLM or HKCU.
+!macro SetUninstallKeys
+ ; Check if this is an ESR release and if so add registry values so it is
+ ; possible to determine that this is an ESR install (bug 726781).
+ ClearErrors
+ ${WordFind} "${UpdateChannel}" "esr" "E#" $3
+ ${If} ${Errors}
+ StrCpy $3 ""
+ ${Else}
+ StrCpy $3 " ESR"
+ ${EndIf}
+
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\Uninstall\${BrandFullNameInternal} ${AppVersion}$3 (${ARCH} ${AB_CD})"
+
+ StrCpy $2 ""
+ ClearErrors
+ WriteRegStr HKLM "$0" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ ; If the uninstall keys already exist in HKLM don't create them in HKCU
+ ClearErrors
+ ReadRegStr $2 "HKLM" $0 "DisplayName"
+ ${If} $2 == ""
+ ; Otherwise we don't have any keys for this product in HKLM so proceeed
+ ; to create them in HKCU. Better handling for this will be done in:
+ ; Bug 711044 - Better handling for 2 uninstall icons
+ StrCpy $1 "HKCU"
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${EndIf}
+ ClearErrors
+ ${Else}
+ StrCpy $1 "HKLM"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ DeleteRegValue HKLM "$0" "${BrandShortName}InstallerTest"
+ ${EndIf}
+
+ ${If} $2 == ""
+ ${GetLongPath} "$INSTDIR" $8
+
+ ; Write the uninstall registry keys
+ ${WriteRegStr2} $1 "$0" "Comments" "${BrandFullNameInternal} ${AppVersion}$3 (${ARCH} ${AB_CD})" 0
+ ${WriteRegStr2} $1 "$0" "DisplayIcon" "$8\${FileMainEXE},${IDI_APPICON_ZERO_BASED}" 0
+ ${WriteRegStr2} $1 "$0" "DisplayName" "${BrandFullNameInternal}$3 (${ARCH} ${AB_CD})" 0
+ ${WriteRegStr2} $1 "$0" "DisplayVersion" "${AppVersion}" 0
+ ${WriteRegStr2} $1 "$0" "HelpLink" "${HelpLink}" 0
+ ${WriteRegStr2} $1 "$0" "InstallLocation" "$8" 0
+ ${WriteRegStr2} $1 "$0" "Publisher" "Mozilla" 0
+ ${WriteRegStr2} $1 "$0" "UninstallString" "$\"$8\uninstall\helper.exe$\"" 0
+ DeleteRegValue SHCTX "$0" "URLInfoAbout"
+; Don't add URLUpdateInfo which is the release notes url except for the release
+; and esr channels since nightly, aurora, and beta do not have release notes.
+; Note: URLUpdateInfo is only defined in the official branding.nsi.
+!ifdef URLUpdateInfo
+!ifndef BETA_UPDATE_CHANNEL
+ ${WriteRegStr2} $1 "$0" "URLUpdateInfo" "${URLUpdateInfo}" 0
+!endif
+!endif
+ ${WriteRegStr2} $1 "$0" "URLInfoAbout" "${URLInfoAbout}" 0
+ ${WriteRegDWORD2} $1 "$0" "NoModify" 1 0
+ ${WriteRegDWORD2} $1 "$0" "NoRepair" 1 0
+
+ ${GetSize} "$8" "/S=0K" $R2 $R3 $R4
+ ${WriteRegDWORD2} $1 "$0" "EstimatedSize" $R2 0
+
+ ${If} "$TmpVal" == "HKLM"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${Else}
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define SetUninstallKeys "!insertmacro SetUninstallKeys"
+
+; Due to a bug when associating some file handlers, only SHCTX was checked for
+; some file types such as ".pdf". SHCTX is set to HKCU or HKLM depending on
+; whether the installer has write access to HKLM. The bug would happen when
+; HCKU was checked and didn't exist since programs aren't required to set the
+; HKCU Software\Classes keys when associating handlers. The fix uses the merged
+; view in HKCR to check for existance of an existing association. This macro
+; cleans affected installations by removing the HKLM and HKCU value if it is set
+; to FirefoxHTML when there is a value for PersistentHandler or by removing the
+; HKCU value when the HKLM value has a value other than an empty string.
+!macro FixBadFileAssociation FILE_TYPE
+ ; Only delete the default value in case the key has values for OpenWithList,
+ ; OpenWithProgids, PersistentHandler, etc.
+ ReadRegStr $0 HKCU "Software\Classes\${FILE_TYPE}" ""
+ ${WordFind} "$0" "-" "+1{" $0
+ ReadRegStr $1 HKLM "Software\Classes\${FILE_TYPE}" ""
+ ${WordFind} "$1" "-" "+1{" $1
+ ReadRegStr $2 HKCR "${FILE_TYPE}\PersistentHandler" ""
+ ${If} "$2" != ""
+ ; Since there is a persistent handler remove FirefoxHTML as the default
+ ; value from both HKCU and HKLM if it set to FirefoxHTML.
+ ${If} "$0" == "FirefoxHTML"
+ DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
+ ${EndIf}
+ ${If} "$1" == "FirefoxHTML"
+ DeleteRegValue HKLM "Software\Classes\${FILE_TYPE}" ""
+ ${EndIf}
+ ${ElseIf} "$0" == "FirefoxHTML"
+ ; Since HKCU is set to FirefoxHTML remove FirefoxHTML as the default value
+ ; from HKCU if HKLM is set to a value other than an empty string.
+ ${If} "$1" != ""
+ DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define FixBadFileAssociation "!insertmacro FixBadFileAssociation"
+
+; Add app specific handler registry entries under Software\Classes if they
+; don't exist (does not use SHCTX).
+!macro FixClassKeys
+ StrCpy $1 "SOFTWARE\Classes"
+
+ ; File handler keys and name value pairs that may need to be created during
+ ; install or upgrade.
+ ReadRegStr $0 HKCR ".shtml" "Content Type"
+ ${If} "$0" == ""
+ StrCpy $0 "$1\.shtml"
+ ${WriteRegStr2} $TmpVal "$1\.shtml" "" "shtmlfile" 0
+ ${WriteRegStr2} $TmpVal "$1\.shtml" "Content Type" "text/html" 0
+ ${WriteRegStr2} $TmpVal "$1\.shtml" "PerceivedType" "text" 0
+ ${EndIf}
+
+ ReadRegStr $0 HKCR ".xht" "Content Type"
+ ${If} "$0" == ""
+ ${WriteRegStr2} $TmpVal "$1\.xht" "" "xhtfile" 0
+ ${WriteRegStr2} $TmpVal "$1\.xht" "Content Type" "application/xhtml+xml" 0
+ ${EndIf}
+
+ ReadRegStr $0 HKCR ".xhtml" "Content Type"
+ ${If} "$0" == ""
+ ${WriteRegStr2} $TmpVal "$1\.xhtml" "" "xhtmlfile" 0
+ ${WriteRegStr2} $TmpVal "$1\.xhtml" "Content Type" "application/xhtml+xml" 0
+ ${EndIf}
+
+ ; Remove possibly badly associated file types
+ ${FixBadFileAssociation} ".pdf"
+ ${FixBadFileAssociation} ".oga"
+ ${FixBadFileAssociation} ".ogg"
+ ${FixBadFileAssociation} ".ogv"
+ ${FixBadFileAssociation} ".pdf"
+ ${FixBadFileAssociation} ".webm"
+!macroend
+!define FixClassKeys "!insertmacro FixClassKeys"
+
+; Updates protocol handlers if their registry open command value is for this
+; install location (uses SHCTX).
+!macro UpdateProtocolHandlers
+ ; Store the command to open the app with an url in a register for easy access.
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ StrCpy $2 "$\"$8$\" -osint -url $\"%1$\""
+
+ ; Only set the file and protocol handlers if the existing one under HKCR is
+ ; for this install location.
+
+ ${IsHandlerForInstallDir} "FirefoxHTML-$AppUserModelID" $R9
+ ${If} "$R9" == "true"
+ ; An empty string is used for the 5th param because FirefoxHTML is not a
+ ; protocol handler.
+ ${AddDisabledDDEHandlerValues} "FirefoxHTML-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} HTML Document" ""
+ ${Else}
+ ${IsHandlerForInstallDir} "FirefoxHTML" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "FirefoxHTML" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} HTML Document" ""
+ ${EndIf}
+ ${EndIf}
+
+ ; FirefoxPDF-* was added after FirefoxHTML and FirefoxURL, so we've never
+ ; supported bare "FirefoxPDF". But we won't have it from the installer, so we
+ ; add/update it unconditionally. `PostUpdate` is gated on `uninstall.log`
+ ; being present, so the invocation here will only happen for installed
+ ; directories, not unpackaged directories.
+ ${AddDisabledDDEHandlerValues} "FirefoxPDF-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_PDF_ZERO_BASED}" \
+ "${AppRegName} PDF Document" ""
+
+ ${IsHandlerForInstallDir} "FirefoxURL-$AppUserModelID" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "FirefoxURL-$AppUserModelID" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} URL" "true"
+ ${Else}
+ ${IsHandlerForInstallDir} "FirefoxURL" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "FirefoxURL" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" \
+ "${AppRegName} URL" "true"
+ ${EndIf}
+ ${EndIf}
+
+ ; An empty string is used for the 4th & 5th params because the following
+ ; protocol handlers already have a display name and the additional keys
+ ; required for a protocol handler.
+
+ ${IsHandlerForInstallDir} "ftp" $R9
+ ${If} "$R9" == "true"
+ ; In the past, we supported ftp, so we need to delete any registration.
+ ${AddDisabledDDEHandlerValues} "ftp" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" "delete"
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "http" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "http" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "https" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "https" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "mailto" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "mailto" "$2" "$8,${IDI_DOCUMENT_ZERO_BASED}" "" ""
+ ${EndIf}
+!macroend
+!define UpdateProtocolHandlers "!insertmacro UpdateProtocolHandlers"
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+; Adds maintenance service certificate keys for the install dir.
+; For the cert to work, it must also be signed by a trusted cert for the user.
+!macro AddMaintCertKeys
+ Push $R0
+ ; Allow main Mozilla cert information for updates
+ ; This call will push the needed key on the stack
+ ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
+ Pop $R0
+ ${If} $R0 != ""
+ ; More than one certificate can be specified in a different subfolder
+ ; for example: $R0\1, but each individual binary can be signed
+ ; with at most one certificate. A fallback certificate can only be used
+ ; if the binary is replaced with a different certificate.
+ ; We always use the 64bit registry for certs.
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+
+ ; PrefetchProcessName was originally used to experiment with deleting
+ ; Windows prefetch as a speed optimization. It is no longer used though.
+ DeleteRegValue HKLM "$R0" "prefetchProcessName"
+
+ ; Setting the Attempted value will ensure that a new Maintenance Service
+ ; install will never be attempted again after this from updates. The value
+ ; is used only to see if updates should attempt new service installs.
+ WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Attempted" 1
+
+ ; These values associate the allowed certificates for the current
+ ; installation.
+ WriteRegStr HKLM "$R0\0" "name" "${CERTIFICATE_NAME}"
+ WriteRegStr HKLM "$R0\0" "issuer" "${CERTIFICATE_ISSUER}"
+ ; These values associate the allowed certificates for the previous
+ ; installation, so that we can update from it cleanly using the
+ ; old updater.exe (which will still have this signature).
+ WriteRegStr HKLM "$R0\1" "name" "${CERTIFICATE_NAME_PREVIOUS}"
+ WriteRegStr HKLM "$R0\1" "issuer" "${CERTIFICATE_ISSUER_PREVIOUS}"
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+ ClearErrors
+ ${EndIf}
+ ; Restore the previously used value back
+ Pop $R0
+!macroend
+!define AddMaintCertKeys "!insertmacro AddMaintCertKeys"
+!endif
+
+!macro RegisterAccessibleMarshal
+ ${RegisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+!macroend
+!define RegisterAccessibleMarshal "!insertmacro RegisterAccessibleMarshal"
+
+; Removes various registry entries for reasons noted below (does not use SHCTX).
+!macro RemoveDeprecatedKeys
+ StrCpy $0 "SOFTWARE\Classes"
+ ; Remove support for launching chrome urls from the shell during install or
+ ; update if the DefaultIcon is from firefox.exe (Bug 301073).
+ ${RegCleanAppHandler} "chrome"
+
+ ; Remove protocol handler registry keys added by the MS shim
+ DeleteRegKey HKLM "Software\Classes\Firefox.URL"
+ DeleteRegKey HKCU "Software\Classes\Firefox.URL"
+
+ ; Unregister deprecated AccessibleHandler.dll.
+ ${If} ${FileExists} "$INSTDIR\AccessibleHandler.dll"
+ ${UnregisterDLL} "$INSTDIR\AccessibleHandler.dll"
+ ${EndIf}
+!macroend
+!define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys"
+
+; Removes various directories and files for reasons noted below.
+!macro RemoveDeprecatedFiles
+ ; Remove the toplevel chrome.manifest added by bug 1295542.
+ ${If} ${FileExists} "$INSTDIR\chrome.manifest"
+ Delete "$INSTDIR\chrome.manifest"
+ ${EndIf}
+
+ ; Remove talkback if it is present (remove after bug 386760 is fixed)
+ ${If} ${FileExists} "$INSTDIR\extensions\talkback@mozilla.org"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\talkback@mozilla.org"
+ ${EndIf}
+
+ ; Remove the Java Console extension (bug 1165156)
+ ${If} ${FileExists} "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0031-ABCDEFFEDCBA}"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0031-ABCDEFFEDCBA}"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0034-ABCDEFFEDCBA}"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0034-ABCDEFFEDCBA}"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0039-ABCDEFFEDCBA}"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0039-ABCDEFFEDCBA}"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0045-ABCDEFFEDCBA}"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\{CAFEEFAC-0016-0000-0045-ABCDEFFEDCBA}"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\extensions\{CAFEEFAC-0017-0000-0000-ABCDEFFEDCBA}"
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\{CAFEEFAC-0017-0000-0000-ABCDEFFEDCBA}"
+ ${EndIf}
+!macroend
+!define RemoveDeprecatedFiles "!insertmacro RemoveDeprecatedFiles"
+
+; Converts specific partner distribution.ini from ansi to utf-8 (bug 882989)
+!macro FixDistributionsINI
+ StrCpy $1 "$INSTDIR\distribution\distribution.ini"
+ StrCpy $2 "$INSTDIR\distribution\utf8fix"
+ StrCpy $0 "0" ; Default to not attempting to fix
+
+ ; Check if the distribution.ini settings are for a partner build that needs
+ ; to have its distribution.ini converted from ansi to utf-8.
+ ${If} ${FileExists} "$1"
+ ${Unless} ${FileExists} "$2"
+ ReadINIStr $3 "$1" "Preferences" "app.distributor"
+ ${If} "$3" == "yahoo"
+ ReadINIStr $3 "$1" "Preferences" "app.distributor.channel"
+ ${If} "$3" == "de"
+ ${OrIf} "$3" == "es"
+ ${OrIf} "$3" == "e1"
+ ${OrIf} "$3" == "mx"
+ StrCpy $0 "1"
+ ${EndIf}
+ ${EndIf}
+ ; Create the utf8fix so this only runs once
+ FileOpen $3 "$2" w
+ FileClose $3
+ ${EndUnless}
+ ${EndIf}
+
+ ${If} "$0" == "1"
+ StrCpy $0 "0"
+ ClearErrors
+ ReadINIStr $3 "$1" "Global" "version"
+ ${Unless} ${Errors}
+ StrCpy $4 "$3" 2
+ ${If} "$4" == "1."
+ StrCpy $4 "$3" "" 2 ; Everything after "1."
+ ${If} $4 < 23
+ StrCpy $0 "1"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+ ${EndIf}
+
+ ${If} "$0" == "1"
+ ClearErrors
+ FileOpen $3 "$1" r
+ ${If} ${Errors}
+ FileClose $3
+ ${Else}
+ StrCpy $2 "$INSTDIR\distribution\distribution.new"
+ ClearErrors
+ FileOpen $4 "$2" w
+ ${If} ${Errors}
+ FileClose $3
+ FileClose $4
+ ${Else}
+ StrCpy $0 "0" ; Default to not replacing the original distribution.ini
+ ${Do}
+ FileReadByte $3 $5
+ ${If} $5 == ""
+ ${Break}
+ ${EndIf}
+ ${If} $5 == 233 ; ansi é
+ StrCpy $0 "1"
+ FileWriteByte $4 195
+ FileWriteByte $4 169
+ ${ElseIf} $5 == 241 ; ansi ñ
+ StrCpy $0 "1"
+ FileWriteByte $4 195
+ FileWriteByte $4 177
+ ${ElseIf} $5 == 252 ; ansi ü
+ StrCpy $0 "1"
+ FileWriteByte $4 195
+ FileWriteByte $4 188
+ ${ElseIf} $5 < 128
+ FileWriteByte $4 $5
+ ${EndIf}
+ ${Loop}
+ FileClose $3
+ FileClose $4
+ ${If} "$0" == "1"
+ ClearErrors
+ Rename "$1" "$1.bak"
+ ${Unless} ${Errors}
+ Rename "$2" "$1"
+ Delete "$1.bak"
+ ${EndUnless}
+ ${Else}
+ Delete "$2"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define FixDistributionsINI "!insertmacro FixDistributionsINI"
+
+; For updates, adds a pinned shortcut to Task Bar on update for Windows 7
+; and 8 if this macro has never been called before and the application
+; is default (see PinToTaskBar for more details). This doesn't get called
+; for Windows 10 and 11 on updates, so we will never pin on update there.
+;
+; For installs, adds a taskbar pin if SHOULD_PIN is 1. (Defaults to 1,
+; but is controllable through the UI, ini file, and command line flags.)
+!macro MigrateTaskBarShortcut SHOULD_PIN
+ ${GetShortcutsLogPath} $0
+ ${If} ${FileExists} "$0"
+ ClearErrors
+ ReadINIStr $1 "$0" "TASKBAR" "Migrated"
+ ${If} ${Errors}
+ ClearErrors
+ WriteIniStr "$0" "TASKBAR" "Migrated" "true"
+ WriteRegDWORD HKCU \
+ "Software\Mozilla\${AppName}\Installer\$AppUserModelID" \
+ "WasPinnedToTaskbar" 1
+ ${If} "${SHOULD_PIN}" == "1"
+ ${PinToTaskBar}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define MigrateTaskBarShortcut "!insertmacro MigrateTaskBarShortcut"
+
+!define GetPinningSupportedByWindowsVersionWithoutSystemPopup "!insertmacro GetPinningSupportedByWindowsVersionWithoutSystemPopup "
+
+; Starting with Windows 10 (> 10.0.19045.3996) and Windows 11 (> 10.0.22621.2361),
+; the OS will show a system popup when trying to pin to the taskbar.
+;
+; Pass in the variable to put the output into. A '1' means pinning is supported on this
+; OS without generating a popup, a '0' means pinning will generate a system popup.
+;
+; More info: a version of Windows was released that introduced a system popup when
+; an exe (such as setup.exe) attempts to pin an app to the taskbar.
+; We already handle pinning in the onboarding process once Firefox
+; launches so we don't want to also attempt to pin it in the installer
+; and have the OS ask the user for confirmation without the full context.
+;
+; The number for that version of windows is still unclear (it might be 22H2 or 23H2)
+; and it's not supported by the version of WinVer.nsh we have anyways,
+; so instead we are manually retrieving the major, minor, build and ubr numbers
+; (Update Build Revision) and confirming that the build numbers work to do pinning
+; in the installer.
+;
+; NOTE: there are currently running Windows where pinning fails and is a no-op. We haven't quite
+; determined how to identify when that will happen, and it's so far only been reported
+; on the newest versions of Windows. GetPinningSupportedByWindowsVersionWithoutSystemPopup
+; will current report that pinning is not supported in these cases, due to reporting
+; pinning as not supported on the newest builds of Windows.
+;
+!macro GetPinningSupportedByWindowsVersionWithoutSystemPopup outvar
+ !define pin_lbl lbl_GPSBWVWSP_${__COUNTER__}
+
+ Push $0
+ Push $1
+ Push $2
+ Push $3
+
+ ${WinVerGetMajor} $0
+ ${WinVerGetMinor} $1
+ ${WinVerGetBuild} $2
+
+ ; Get the UBR; only documented way I could figure out how to get this reliably
+ ClearErrors
+ ReadRegDWORD $3 HKLM \
+ "Software\Microsoft\Windows NT\CurrentVersion" \
+ "UBR"
+
+ ; It's not obvious how to use LogicLib itself within a LogicLib custom
+ ; operator, so we do everything by hand with `IntCmp`. The below lines
+ ; translate to:
+ ; StrCpy ${outvar} '0' ; default to false
+ ; ${If} $0 == 10
+ ; ${If} $1 == 0
+ ; ${If} $2 < 19045
+ ; StrCpy ${outvar} '1'
+ ; ${ElseIf} $2 == 19045
+ ; ; Test Windows 10
+ ; ${If} $3 < 3996
+ ; StrCpy ${outvar} '1'
+ ; ${Endif}
+ ; ; 22000 is the version number that splits between Win 10 and 11
+ ; ${ElseIf} $2 >= 22000
+ ; ; Test Windows 11
+ ; ${If} $2 < 22621
+ ; StrCpy ${outvar} '1'
+ ; ${ElseIf} $2 == 22621
+ ; ${If} $3 < 2361
+ ; StrCpy ${outvar} '1'
+ ; ${EndIf}
+ ; ${EndIf}
+ ; ${EndIf}
+ ; ${Endif}
+ ; ${EndIf}
+
+ StrCpy ${outvar} '0' ; default to false on pinning
+
+ ; If the major version is greater than 10, no pinning in setup
+ IntCmp $0 10 "" "" ${pin_lbl}_bad
+
+ ; If the minor version is greater than 0, no pinning in setup
+ IntCmp $1 0 "" "" ${pin_lbl}_bad
+
+ ; If the build number equals 19045, we have to test the UBR
+ ; If it's greater than 19045, then we have to check if
+ ; it's a Windows 11 build or not to determine if more testing
+ ; is needed
+ IntCmp $2 19045 ${pin_lbl}_test_win10 ${pin_lbl}_good ""
+
+ ; If the major number is less than 22000, then we're between
+ ; 19046 and 22000, meaning pinning will produce a popup
+ IntCmp $2 22000 "" ${pin_lbl}_bad ""
+
+ ${pin_lbl}_test_win11:
+
+ ; If the build number is less than 22621, jump to pinning; if greater than, no pinning
+ IntCmp $2 22621 "" ${pin_lbl}_good ${pin_lbl}_bad
+
+ ; Only if the version is 10.0.22621 do we fall through to here
+ ; If the UBR is greater than or equal to 2361, jump to no pinning
+ ; Otherwise jump to pinning
+ IntCmp $3 2361 ${pin_lbl}_bad ${pin_lbl}_good ${pin_lbl}_bad
+
+ ${pin_lbl}_test_win10:
+
+ ; Only if the version is 10.0.19045 or greater (but not Windows 11) do we fall
+ ; through to here.
+ ; If the UBR is greater than or equal to 3996, jump to no pinning
+ IntCmp $3 3996 ${pin_lbl}_bad ${pin_lbl}_good ${pin_lbl}_bad
+
+ ${pin_lbl}_good:
+
+ StrCpy ${outvar} '1'
+
+ ${pin_lbl}_bad:
+ !undef pin_lbl
+
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+!macroend
+
+!macro _PinningSupportedByWindowsVersionWithoutSystemPopup _ignore _ignore2 _t _f
+ !insertmacro _LOGICLIB_TEMP
+ ${GetPinningSupportedByWindowsVersionWithoutSystemPopup} $_LOGICLIB_TEMP
+ !insertmacro _= $_LOGICLIB_TEMP "1" `${_t}` `${_f}`
+!macroend
+
+; The following is to make if statements for the functionality easier. When using an if statement,
+; Use IsPinningSupportedByWindowsVersionWithoutSystemPopup like so, instead of GetPinningSupportedByWindowsVersionWithoutSystemPopup:
+;
+; ${If} ${IsPinningSupportedByWindowsVersionWithoutSystemPopup}
+; ; do something
+; ${EndIf}
+;
+!define IsPinningSupportedByWindowsVersionWithoutSystemPopup `"" PinningSupportedByWindowsVersionWithoutSystemPopup "" `
+
+; Adds a pinned Task Bar shortcut on Windows 7 if there isn't one for the main
+; application executable already. Existing pinned shortcuts for the same
+; application model ID must be removed first to prevent breaking the pinned
+; item's lists but multiple installations with the same application model ID is
+; an edgecase. If removing existing pinned shortcuts with the same application
+; model ID removes a pinned pinned Start Menu shortcut this will also add a
+; pinned Start Menu shortcut.
+!macro PinToTaskBar
+ StrCpy $8 "false" ; Whether a shortcut had to be created
+ ${IsPinnedToTaskBar} "$INSTDIR\${FileMainEXE}" $R9
+ ${If} "$R9" == "false"
+ ; Find an existing Start Menu shortcut or create one to use for pinning
+ ${GetShortcutsLogPath} $0
+ ${If} ${FileExists} "$0"
+ ClearErrors
+ ReadINIStr $1 "$0" "STARTMENU" "Shortcut0"
+ ${Unless} ${Errors}
+ SetShellVarContext all ; Set SHCTX to all users
+ ${Unless} ${FileExists} "$SMPROGRAMS\$1"
+ SetShellVarContext current ; Set SHCTX to the current user
+ ${Unless} ${FileExists} "$SMPROGRAMS\$1"
+ StrCpy $8 "true"
+ CreateShortCut "$SMPROGRAMS\$1" "$INSTDIR\${FileMainEXE}"
+ ${If} ${FileExists} "$SMPROGRAMS\$1"
+ ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\$1" \
+ "$INSTDIR"
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\$1" "$AppUserModelID" "true"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+ ${EndUnless}
+
+ ${If} ${FileExists} "$SMPROGRAMS\$1"
+ ; Count of Start Menu pinned shortcuts before unpinning.
+ ${PinnedToStartMenuLnkCount} $R9
+
+ ; Having multiple shortcuts pointing to different installations with
+ ; the same AppUserModelID (e.g. side by side installations of the
+ ; same version) will make the TaskBar shortcut's lists into an bad
+ ; state where the lists are not shown. To prevent this first
+ ; uninstall the pinned item.
+ ApplicationID::UninstallPinnedItem "$SMPROGRAMS\$1"
+
+ ; Count of Start Menu pinned shortcuts after unpinning.
+ ${PinnedToStartMenuLnkCount} $R8
+
+ ; If there is a change in the number of Start Menu pinned shortcuts
+ ; assume that unpinning unpinned a side by side installation from
+ ; the Start Menu and pin this installation to the Start Menu.
+ ${Unless} $R8 == $R9
+ ; Pin the shortcut to the Start Menu. 5381 is the shell32.dll
+ ; resource id for the "Pin to Start Menu" string.
+ InvokeShellVerb::DoIt "$SMPROGRAMS" "$1" "5381"
+ ${EndUnless}
+
+ ${If} ${AtMostWaaS} 1809
+ ; In Windows 10 the "Pin to Taskbar" resource was removed, so we
+ ; can't access the verb that way anymore. We have to create a
+ ; command key using the GUID that's assigned to this action and
+ ; then invoke that as a verb. This works up until build 1809
+ ReadRegStr $R9 HKLM \
+ "Software\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Windows.taskbarpin" \
+ "ExplorerCommandHandler"
+ WriteRegStr HKCU "Software\Classes\*\shell\${AppRegName}-$AppUserModelID" "ExplorerCommandHandler" $R9
+ InvokeShellVerb::DoIt "$SMPROGRAMS" "$1" "${AppRegName}-$AppUserModelID"
+ DeleteRegKey HKCU "Software\Classes\*\shell\${AppRegName}-$AppUserModelID"
+ ${Else}
+ ; In Windows 10 1903 and up, and Windows 11 prior to 22H2, the above no
+ ; longer works. We have yet another method for these versions
+ ; which is detailed in the PinToTaskbar plugin code.
+ ${If} ${IsPinningSupportedByWindowsVersionWithoutSystemPopup}
+ PinToTaskbar::Pin "$SMPROGRAMS\$1"
+ ${EndIf}
+ ${EndIf}
+
+ ; Delete the shortcut if it was created
+ ${If} "$8" == "true"
+ Delete "$SMPROGRAMS\$1"
+ ${EndIf}
+ ${EndIf}
+
+ ${If} $TmpVal == "HKCU"
+ SetShellVarContext current ; Set SHCTX to the current user
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to all users
+ ${EndIf}
+ ${EndUnless}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define PinToTaskBar "!insertmacro PinToTaskBar"
+
+; Removes the application's start menu directory along with its shortcuts if
+; they exist and if they exist creates a start menu shortcut in the root of the
+; start menu directory (bug 598779). If the application's start menu directory
+; is not empty after removing the shortucts the directory will not be removed
+; since these additional items were not created by the installer (uses SHCTX).
+!macro RemoveStartMenuDir
+ ${GetShortcutsLogPath} $0
+ ${If} ${FileExists} "$0"
+ ; Delete Start Menu Programs shortcuts, directory if it is empty, and
+ ; parent directories if they are empty up to but not including the start
+ ; menu directory.
+ ${GetLongPath} "$SMPROGRAMS" $1
+ ClearErrors
+ ReadINIStr $2 "$0" "SMPROGRAMS" "RelativePathToDir"
+ ${Unless} ${Errors}
+ ${GetLongPath} "$1\$2" $2
+ ${If} "$2" != ""
+ ; Delete shortucts in the Start Menu Programs directory.
+ StrCpy $3 0
+ ${Do}
+ ClearErrors
+ ReadINIStr $4 "$0" "SMPROGRAMS" "Shortcut$3"
+ ; Stop if there are no more entries
+ ${If} ${Errors}
+ ${ExitDo}
+ ${EndIf}
+ ${If} ${FileExists} "$2\$4"
+ ShellLink::GetShortCutTarget "$2\$4"
+ Pop $5
+ ${If} "$INSTDIR\${FileMainEXE}" == "$5"
+ Delete "$2\$4"
+ ${EndIf}
+ ${EndIf}
+ IntOp $3 $3 + 1 ; Increment the counter
+ ${Loop}
+ ; Delete Start Menu Programs directory and parent directories
+ ${Do}
+ ; Stop if the current directory is the start menu directory
+ ${If} "$1" == "$2"
+ ${ExitDo}
+ ${EndIf}
+ ClearErrors
+ RmDir "$2"
+ ; Stop if removing the directory failed
+ ${If} ${Errors}
+ ${ExitDo}
+ ${EndIf}
+ ${GetParent} "$2" $2
+ ${Loop}
+ ${EndIf}
+ DeleteINISec "$0" "SMPROGRAMS"
+ ${EndUnless}
+ ${EndIf}
+!macroend
+!define RemoveStartMenuDir "!insertmacro RemoveStartMenuDir"
+
+; Creates the shortcuts log ini file with the appropriate entries if it doesn't
+; already exist.
+!macro CreateShortcutsLog
+ ${GetShortcutsLogPath} $0
+ ${Unless} ${FileExists} "$0"
+ ${LogStartMenuShortcut} "${BrandShortName}.lnk"
+ ${LogQuickLaunchShortcut} "${BrandShortName}.lnk"
+ ${LogDesktopShortcut} "${BrandShortName}.lnk"
+ ${EndUnless}
+!macroend
+!define CreateShortcutsLog "!insertmacro CreateShortcutsLog"
+
+; The files to check if they are in use during (un)install so the restart is
+; required message is displayed. All files must be located in the $INSTDIR
+; directory.
+!macro PushFilesToCheck
+ ; The first string to be pushed onto the stack MUST be "end" to indicate
+ ; that there are no more files to check in $INSTDIR and the last string
+ ; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro
+ ; returns after the first check.
+ Push "end"
+ Push "AccessibleMarshal.dll"
+ Push "freebl3.dll"
+ Push "nssckbi.dll"
+ Push "nspr4.dll"
+ Push "nssdbm3.dll"
+ Push "mozsqlite3.dll"
+ Push "xpcom.dll"
+ Push "crashreporter.exe"
+ Push "default-browser-agent.exe"
+ Push "minidump-analyzer.exe"
+ Push "pingsender.exe"
+ Push "updater.exe"
+ Push "mozwer.dll"
+ Push "${FileMainEXE}"
+!macroend
+!define PushFilesToCheck "!insertmacro PushFilesToCheck"
+
+
+; Pushes the string "true" to the top of the stack if the Firewall service is
+; running and pushes the string "false" to the top of the stack if it isn't.
+!define SC_MANAGER_ALL_ACCESS 0x3F
+!define SERVICE_QUERY_CONFIG 0x0001
+!define SERVICE_QUERY_STATUS 0x0004
+!define SERVICE_RUNNING 0x4
+
+!macro IsFirewallSvcRunning
+ Push $R9
+ Push $R8
+ Push $R7
+ Push $R6
+ Push "false"
+
+ System::Call 'advapi32::OpenSCManagerW(n, n, i ${SC_MANAGER_ALL_ACCESS}) i.R6'
+ ${If} $R6 != 0
+ ; MpsSvc is the Firewall service.
+ ; When opening the service with SERVICE_QUERY_CONFIG the return value will
+ ; be 0 if the service is not installed.
+ System::Call 'advapi32::OpenServiceW(i R6, t "MpsSvc", i ${SERVICE_QUERY_CONFIG}) i.R7'
+ ${If} $R7 != 0
+ System::Call 'advapi32::CloseServiceHandle(i R7) n'
+ ; Open the service with SERVICE_QUERY_STATUS so its status can be queried.
+ System::Call 'advapi32::OpenServiceW(i R6, t "MpsSvc", i ${SERVICE_QUERY_STATUS}) i.R7'
+ ${EndIf}
+ ; Did the calls to OpenServiceW succeed?
+ ${If} $R7 != 0
+ System::Call '*(i,i,i,i,i,i,i) i.R9'
+ ; Query the current status of the service.
+ System::Call 'advapi32::QueryServiceStatus(i R7, i $R9) i'
+ System::Call '*$R9(i, i.R8)'
+ System::Free $R9
+ System::Call 'advapi32::CloseServiceHandle(i R7) n'
+ IntFmt $R8 "0x%X" $R8
+ ${If} $R8 == ${SERVICE_RUNNING}
+ Pop $R9
+ Push "true"
+ ${EndIf}
+ ${EndIf}
+ System::Call 'advapi32::CloseServiceHandle(i R6) n'
+ ${EndIf}
+
+ Exch 1
+ Pop $R6
+ Exch 1
+ Pop $R7
+ Exch 1
+ Pop $R8
+ Exch 1
+ Pop $R9
+!macroend
+!define IsFirewallSvcRunning "!insertmacro IsFirewallSvcRunning"
+!define un.IsFirewallSvcRunning "!insertmacro IsFirewallSvcRunning"
+
+; Sets this installation as the default browser by setting the registry keys
+; under HKEY_CURRENT_USER via registry calls and using the AppAssocReg NSIS
+; plugin. This is a function instead of a macro so it is
+; easily called from an elevated instance of the binary. Since this can be
+; called by an elevated instance logging is not performed in this function.
+Function SetAsDefaultAppUserHKCU
+ ; See if we're using path hash suffixed registry keys for this install
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${If} ${Errors}
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${EndIf}
+ StrCpy $0 $0 -2
+ ${If} $0 != $8
+ ${If} $AppUserModelID == ""
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+ ${EndIf}
+ StrCpy $R9 "${AppRegName}-$AppUserModelID"
+ ${EndIf}
+
+ ; Set ourselves as the user's selected StartMenuInternet browser, but only
+ ; if we have StartMenuInternet registry keys that are for this install.
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${If} ${Errors}
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${EndIf}
+ ${Unless} ${Errors}
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${If} "$0" == "$INSTDIR"
+ ; This function cannot do anything to actually set the default browser,
+ ; it can only set up the registry entries to allow the user to do so.
+ ; Getting here means that those entries already exist for this
+ ; installation, we just found them, so there is nothing more to be
+ ; done.
+ Return
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+
+ ; It's unlikely that we didn't find a StartMenuInternet key above, but it is
+ ; possible; it likely would mean this copy of the application was extracted
+ ; directly from a ZIP file and the installer was never run.
+ ${SetStartMenuInternet} "HKCU"
+ ${FixShellIconHandler} "HKCU"
+ ${FixClassKeys} ; Does not use SHCTX
+
+ ${SetHandlers}
+
+ ; Only register as the handler if the app registry name
+ ; exists under the RegisteredApplications registry key. The protocol and
+ ; file handlers set previously at the user level will associate this install
+ ; as the default browser.
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\RegisteredApplications" "$R9"
+ ${Unless} ${Errors}
+ ; This is all protected by a user choice hash in Windows 8 so it won't
+ ; help, but it also won't hurt.
+ AppAssocReg::SetAppAsDefault "$R9" ".htm" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".html" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".shtml" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".webp" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".avif" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".xht" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" ".xhtml" "file"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" "http" "protocol"
+ Pop $0
+ AppAssocReg::SetAppAsDefault "$R9" "https" "protocol"
+ Pop $0
+ ${EndUnless}
+ ${RemoveDeprecatedKeys}
+ ${MigrateTaskBarShortcut} "$R0"
+FunctionEnd
+
+; Helper for updating the shortcut application model IDs.
+Function FixShortcutAppModelIDs
+ ${If} "$AppUserModelID" != ""
+ ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "$AppUserModelID" $0
+ ${EndIf}
+FunctionEnd
+
+; Helper for adding Firewall exceptions during install and after app update.
+Function AddFirewallEntries
+ ${IsFirewallSvcRunning}
+ Pop $0
+ ${If} "$0" == "true"
+ liteFirewallW::AddRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
+ ${EndIf}
+FunctionEnd
+
+; The !ifdef NO_LOG prevents warnings when compiling the installer.nsi due to
+; this function only being used by the uninstaller.nsi.
+!ifdef NO_LOG
+
+Function SetAsDefaultAppUser
+ ; AddTaskbarSC is needed by MigrateTaskBarShortcut, which is called by
+ ; SetAsDefaultAppUserHKCU. If this is called via ExecCodeSegment,
+ ; MigrateTaskBarShortcut will not see the value of AddTaskbarSC, so we
+ ; send it via a register instead.
+ StrCpy $R0 $AddTaskbarSC
+ ; We want to avoid having a UAC prompt since we'll already have another
+ ; action for control panel default browser selection popping upto the user.
+ ; The start menu keys can be added into HKCU. The call to
+ ; SetAsDefaultAppUserHKCU will have already set the HKCU keys for
+ ; SetStartMenuInternet.
+ ; Check if this is running in an elevated process
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $0
+ ${If} ${Errors} ; Not elevated
+ Call SetAsDefaultAppUserHKCU
+ ${Else} ; Elevated - execute the function in the unelevated process
+ GetFunctionAddress $0 SetAsDefaultAppUserHKCU
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+FunctionEnd
+!define SetAsDefaultAppUser "Call SetAsDefaultAppUser"
+
+!endif ; NO_LOG
+
+!ifdef MOZ_LAUNCHER_PROCESS
+!macro ResetLauncherProcessDefaults
+ # By deleting these values, we remove remnants of any force-disable settings
+ # that may have been set during the SHIELD study in 67. Note that this setting
+ # was only intended to distinguish between test and control groups for the
+ # purposes of the study, not as a user preference.
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Launcher"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Browser"
+!macroend
+!define ResetLauncherProcessDefaults "!insertmacro ResetLauncherProcessDefaults"
+!endif
+
+!macro WriteToastNotificationRegistration RegKey
+ ; Find or create a GUID to use for this installation. For simplicity, We
+ ; always update our registration.
+ ClearErrors
+ ReadRegStr $0 SHCTX "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+ ${If} "$0" == ""
+ ; Create a GUID.
+ System::Call "rpcrt4::UuidCreate(g . r0)i"
+ ; StringFromGUID2 (which is what System::Call uses internally to stringify
+ ; GUIDs) includes braces in its output. In this case, we want the braces.
+ ${EndIf}
+
+ ; Check if this is an ESR release.
+ ClearErrors
+ ${WordFind} "${UpdateChannel}" "esr" "E#" $1
+ ${If} ${Errors}
+ StrCpy $1 ""
+ ${Else}
+ StrCpy $1 " ESR"
+ ${EndIf}
+
+ ; Write the following keys and values to the registry.
+ ; HKEY_CURRENT_USER\Software\Classes\AppID\{GUID} DllSurrogate : REG_SZ = ""
+ ; \AppUserModelId\{ToastAumidPrefix}{install hash} CustomActivator : REG_SZ = {GUID}
+ ; DisplayName : REG_EXPAND_SZ = {display name}
+ ; IconUri : REG_EXPAND_SZ = {icon path}
+ ; \CLSID\{GUID} AppID : REG_SZ = {GUID}
+ ; \InprocServer32 (Default) : REG_SZ = {notificationserver.dll path}
+ ${WriteRegStr2} ${RegKey} "Software\Classes\AppID\$0" "DllSurrogate" "" 0
+ ${WriteRegStr2} ${RegKey} "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator" "$0" 0
+ ${WriteRegStr2} ${RegKey} "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "DisplayName" "${BrandFullNameInternal}$1" 0
+ ; Sadly, we can't use embedded resources like `firefox.exe,1`.
+ ${WriteRegStr2} ${RegKey} "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "IconUri" "$INSTDIR\browser\VisualElements\VisualElements_70.png" 0
+ ${WriteRegStr2} ${RegKey} "Software\Classes\CLSID\$0" "AppID" "$0" 0
+ ${WriteRegStr2} ${RegKey} "Software\Classes\CLSID\$0\InProcServer32" "" "$INSTDIR\notificationserver.dll" 0
+!macroend
+!define WriteToastNotificationRegistration "!insertmacro WriteToastNotificationRegistration"
diff --git a/browser/installer/windows/nsis/stub.nsi b/browser/installer/windows/nsis/stub.nsi
new file mode 100644
index 0000000000..4c3e0b40a0
--- /dev/null
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -0,0 +1,1755 @@
+# 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/.
+
+# Required Plugins:
+# AppAssocReg
+# CertCheck
+# InetBgDL
+# ShellLink
+# UAC
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+Var CheckboxShortcuts
+Var CheckboxSendPing
+Var CheckboxInstallMaintSvc
+Var CheckboxCleanupProfile
+
+Var FontFamilyName
+
+Var CanWriteToInstallDir
+Var HasRequiredSpaceAvailable
+Var IsDownloadFinished
+Var DownloadSizeBytes
+Var DownloadReset
+Var ExistingTopDir
+Var SpaceAvailableBytes
+Var InitialInstallDir
+Var HandleDownload
+Var InstallCounterStep
+Var InstallTotalSteps
+Var ProgressCompleted
+Var UsingHighContrastMode
+
+Var ExitCode
+Var FirefoxLaunchCode
+
+Var StartDownloadPhaseTickCount
+; Since the Intro and Options pages can be displayed multiple times the total
+; seconds spent on each of these pages is reported.
+Var IntroPhaseSeconds
+Var OptionsPhaseSeconds
+; The tick count for the last download.
+Var StartLastDownloadTickCount
+; The number of seconds from the start of the download phase until the first
+; bytes are received. This is only recorded for first request so it is possible
+; to determine connection issues for the first request.
+Var DownloadFirstTransferSeconds
+; The last four tick counts are for the end of a phase in the installation page.
+Var EndDownloadPhaseTickCount
+Var EndPreInstallPhaseTickCount
+Var EndInstallPhaseTickCount
+Var EndFinishPhaseTickCount
+
+Var DistributionID
+Var DistributionVersion
+
+Var InitialInstallRequirementsCode
+Var ExistingProfile
+Var ExistingVersion
+Var ExistingBuildID
+Var DownloadedBytes
+Var DownloadRetryCount
+Var OpenedDownloadPage
+Var DownloadServerIP
+Var PostSigningData
+Var PreviousInstallDir
+Var ProfileCleanupPromptType
+Var AppLaunchWaitTickCount
+Var TimerHandle
+
+!define ARCH_X86 1
+!define ARCH_AMD64 2
+!define ARCH_AARCH64 3
+Var ArchToInstall
+
+; Uncomment the following to prevent pinging the metrics server when testing
+; the stub installer
+;!define STUB_DEBUG
+
+!define StubURLVersion "v9"
+
+; Successful install exit code
+!define ERR_SUCCESS 0
+
+/**
+ * The following errors prefixed with ERR_DOWNLOAD apply to the download phase.
+ */
+; The download was cancelled by the user
+!define ERR_DOWNLOAD_CANCEL 10
+
+; Too many attempts to download. The maximum attempts is defined in
+; DownloadMaxRetries.
+!define ERR_DOWNLOAD_TOO_MANY_RETRIES 11
+
+/**
+ * The following errors prefixed with ERR_PREINSTALL apply to the pre-install
+ * check phase.
+ */
+; Unable to acquire a file handle to the downloaded file
+!define ERR_PREINSTALL_INVALID_HANDLE 20
+
+; The downloaded file's certificate is not trusted by the certificate store.
+!define ERR_PREINSTALL_CERT_UNTRUSTED 21
+
+; The downloaded file's certificate attribute values were incorrect.
+!define ERR_PREINSTALL_CERT_ATTRIBUTES 22
+
+; The downloaded file's certificate is not trusted by the certificate store and
+; certificate attribute values were incorrect.
+!define ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES 23
+
+; Timed out while waiting for the certificate checks to run.
+!define ERR_PREINSTALL_CERT_TIMEOUT 24
+
+/**
+ * The following errors prefixed with ERR_INSTALL apply to the install phase.
+ */
+; The installation timed out. The installation timeout is defined by the number
+; of progress steps defined in InstallTotalSteps and the install timer
+; interval defined in InstallIntervalMS
+!define ERR_INSTALL_TIMEOUT 30
+
+; Maximum times to retry the download before displaying an error
+!define DownloadMaxRetries 9
+
+; Interval before retrying to download. 3 seconds is used along with 10
+; attempted downloads (the first attempt along with 9 retries) to give a
+; minimum of 30 seconds or retrying before giving up.
+!define DownloadRetryIntervalMS 3000
+
+; Interval for the download timer
+!define DownloadIntervalMS 200
+
+; Timeout for the certificate check
+!define PreinstallCertCheckMaxWaitSec 30
+
+; Interval for the install timer
+!define InstallIntervalMS 100
+
+; Number of steps for the install progress.
+; This might not be enough when installing on a slow network drive so it will
+; fallback to downloading the full installer if it reaches this number.
+
+; Approximately 240 seconds with a 100 millisecond timer.
+!define InstallCleanTotalSteps 2400
+
+; Approximately 255 seconds with a 100 millisecond timer.
+!define InstallPaveOverTotalSteps 2550
+
+; Blurb duty cycle
+!define BlurbDisplayMS 19500
+!define BlurbBlankMS 500
+
+; Interval between checks for the application window and progress bar updates.
+!define AppLaunchWaitIntervalMS 100
+
+; Total time to wait for the application to start before just exiting.
+!define AppLaunchWaitTimeoutMS 10000
+
+; Maximum value of the download/install/launch progress bar, and the end values
+; for each individual stage.
+!define PROGRESS_BAR_TOTAL_STEPS 500
+!define PROGRESS_BAR_DOWNLOAD_END_STEP 300
+!define PROGRESS_BAR_INSTALL_END_STEP 475
+!define PROGRESS_BAR_APP_LAUNCH_END_STEP 500
+
+; Amount of physical memory required for the 64-bit build to be selected (2 GB).
+; Machines with this or less RAM get the 32-bit build, even with a 64-bit OS.
+!define RAM_NEEDED_FOR_64BIT 0x80000000
+
+; Attempt to elevate Standard Users in addition to users that
+; are a member of the Administrators group.
+!define NONADMIN_ELEVATE
+
+!define CONFIG_INI "config.ini"
+!define PARTNER_INI "$EXEDIR\partner.ini"
+
+!ifndef FILE_SHARE_READ
+ !define FILE_SHARE_READ 1
+!endif
+!ifndef GENERIC_READ
+ !define GENERIC_READ 0x80000000
+!endif
+!ifndef OPEN_EXISTING
+ !define OPEN_EXISTING 3
+!endif
+!ifndef INVALID_HANDLE_VALUE
+ !define INVALID_HANDLE_VALUE -1
+!endif
+
+!define DefaultInstDir32bit "$PROGRAMFILES32\${BrandFullName}"
+!define DefaultInstDir64bit "$PROGRAMFILES64\${BrandFullName}"
+
+!include "LogicLib.nsh"
+!include "FileFunc.nsh"
+!include "TextFunc.nsh"
+!include "WinVer.nsh"
+!include "WordFunc.nsh"
+
+!insertmacro GetParameters
+!insertmacro GetOptions
+!insertmacro LineFind
+!insertmacro StrFilter
+
+!include "locales.nsi"
+!include "branding.nsi"
+
+!include "defines.nsi"
+
+; Must be included after defines.nsi
+!include "locale-fonts.nsh"
+
+; The OFFICIAL define is a workaround to support different urls for Release and
+; Beta since they share the same branding when building with other branches that
+; set the update channel to beta.
+!ifdef OFFICIAL
+!ifdef BETA_UPDATE_CHANNEL
+!undef URLStubDownloadX86
+!undef URLStubDownloadAMD64
+!undef URLStubDownloadAArch64
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-beta-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-beta-latest"
+!undef URLManualDownload
+!define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=beta&installer_lang=${AB_CD}"
+!undef Channel
+!define Channel "beta"
+!endif
+!endif
+
+!include "common.nsh"
+
+!insertmacro CopyPostSigningData
+!insertmacro ElevateUAC
+!insertmacro GetLongPath
+!insertmacro GetPathFromString
+!insertmacro GetParent
+!insertmacro GetSingleInstallPath
+!insertmacro InitHashAppModelId
+!insertmacro IsUserAdmin
+!insertmacro RemovePrecompleteEntries
+!insertmacro SetBrandNameVars
+!insertmacro ITBL3Create
+!insertmacro UnloadUAC
+
+VIAddVersionKey "FileDescription" "${BrandShortName} Installer"
+VIAddVersionKey "OriginalFilename" "setup-stub.exe"
+
+Name "$BrandFullName"
+OutFile "setup-stub.exe"
+Icon "firefox64.ico"
+XPStyle on
+BrandingText " "
+ChangeUI IDD_INST "nsisui.exe"
+
+!ifdef ${AB_CD}_rtl
+ LoadLanguageFile "locale-rtl.nlf"
+!else
+ LoadLanguageFile "locale.nlf"
+!endif
+
+!include "nsisstrings.nlf"
+
+Caption "$(INSTALLER_WIN_CAPTION)"
+
+Page custom createProfileCleanup
+Page custom createInstall ; Download / Installation page
+
+Function .onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ StrCpy $LANGUAGE 0
+ ; This macro is used to set the brand name variables but the ini file method
+ ; isn't supported for the stub installer.
+ ${SetBrandNameVars} "$PLUGINSDIR\ignored.ini"
+
+ ; Don't install on systems that don't support SSE2. The parameter value of
+ ; 10 is for PF_XMMI64_INSTRUCTIONS_AVAILABLE which will check whether the
+ ; SSE2 instruction set is available.
+ System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
+
+ ; Windows 8.1/Server 2012 R2 and lower are not supported.
+ ${Unless} ${AtLeastWin10}
+ ${If} "$R7" == "0"
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
+ ${Else}
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_MSG)"
+ ${EndIf}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$R7" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndUnless}
+
+ ; SSE2 CPU support
+ ${If} "$R7" == "0"
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndIf}
+
+ Call GetArchToInstall
+ ${If} $ArchToInstall == ${ARCH_AARCH64}
+ ${OrIf} $ArchToInstall == ${ARCH_AMD64}
+ StrCpy $INSTDIR "${DefaultInstDir64bit}"
+ ${Else}
+ StrCpy $INSTDIR "${DefaultInstDir32bit}"
+ ${EndIf}
+
+ ; Require elevation if the user can elevate
+ ${ElevateUAC}
+
+ ; If we have any existing installation, use its location as the default
+ ; path for this install, even if it's not the same architecture.
+ SetRegView 32
+ SetShellVarContext all ; Set SHCTX to HKLM
+ ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+
+ ${If} "$R9" == "false"
+ ${If} ${IsNativeAMD64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+ ${EndIf}
+ ${EndIf}
+
+ ${If} "$R9" == "false"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+ ${EndIf}
+
+ StrCpy $PreviousInstallDir ""
+ ${If} "$R9" != "false"
+ StrCpy $PreviousInstallDir "$R9"
+ StrCpy $INSTDIR "$PreviousInstallDir"
+ ${EndIf}
+
+ ; Used to determine if the default installation directory was used.
+ StrCpy $InitialInstallDir "$INSTDIR"
+
+ ; Initialize the majority of variables except those that need to be reset
+ ; when a page is displayed.
+ StrCpy $ExitCode "${ERR_DOWNLOAD_CANCEL}"
+ StrCpy $IntroPhaseSeconds "0"
+ StrCpy $OptionsPhaseSeconds "0"
+ StrCpy $EndPreInstallPhaseTickCount "0"
+ StrCpy $EndInstallPhaseTickCount "0"
+ StrCpy $StartDownloadPhaseTickCount "0"
+ StrCpy $EndDownloadPhaseTickCount "0"
+ StrCpy $InitialInstallRequirementsCode ""
+ StrCpy $IsDownloadFinished ""
+ StrCpy $FirefoxLaunchCode "0"
+ StrCpy $CheckboxShortcuts "1"
+ StrCpy $CheckboxSendPing "1"
+ StrCpy $CheckboxCleanupProfile "0"
+ StrCpy $ProgressCompleted "0"
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ; We can only install the maintenance service if the user is an admin.
+ Call IsUserAdmin
+ Pop $0
+ ${If} "$0" == "true"
+ StrCpy $CheckboxInstallMaintSvc "1"
+ ${Else}
+ StrCpy $CheckboxInstallMaintSvc "0"
+ ${EndIf}
+!else
+ StrCpy $CheckboxInstallMaintSvc "0"
+!endif
+
+ StrCpy $FontFamilyName ""
+!ifdef FONT_FILE1
+ ${If} ${FileExists} "$FONTS\${FONT_FILE1}"
+ StrCpy $FontFamilyName "${FONT_NAME1}"
+ ${EndIf}
+!endif
+
+!ifdef FONT_FILE2
+ ${If} $FontFamilyName == ""
+ ${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
+ StrCpy $FontFamilyName "${FONT_NAME2}"
+ ${EndIf}
+!endif
+
+ ${If} $FontFamilyName == ""
+ StrCpy $FontFamilyName "$(^Font)"
+ ${EndIf}
+
+ InitPluginsDir
+ File /oname=$PLUGINSDIR\bgstub.jpg "bgstub.jpg"
+
+ ; Detect whether the machine is running with a high contrast theme.
+ ; We'll hide our background images in that case, both because they don't
+ ; always render properly and also to improve the contrast.
+ System::Call '*(i 12, i 0, p 0) p . r0'
+ ; 0x42 == SPI_GETHIGHCONTRAST
+ System::Call 'user32::SystemParametersInfoW(i 0x42, i 0, p r0, i 0)'
+ System::Call '*$0(i, i . r1, p)'
+ System::Free $0
+ IntOp $UsingHighContrastMode $1 & 1
+
+ SetShellVarContext all ; Set SHCTX to All Users
+ ; If the user doesn't have write access to the installation directory set
+ ; the installation directory to a subdirectory of the user's local
+ ; application directory (e.g. non-roaming).
+ Call CanWrite
+ ${If} "$CanWriteToInstallDir" == "false"
+ ${GetLocalAppDataFolder} $0
+ StrCpy $INSTDIR "$0\${BrandFullName}\"
+ Call CanWrite
+ ${EndIf}
+
+ Call CheckSpace
+
+ ${If} ${FileExists} "$INSTDIR"
+ ; Always display the long path if the path exists.
+ ${GetLongPath} "$INSTDIR" $INSTDIR
+ ${EndIf}
+
+ ; Check whether the install requirements are satisfied using the default
+ ; values for metrics.
+ ${If} "$InitialInstallRequirementsCode" == ""
+ ${If} "$CanWriteToInstallDir" != "true"
+ ${AndIf} "$HasRequiredSpaceAvailable" != "true"
+ StrCpy $InitialInstallRequirementsCode "1"
+ ${ElseIf} "$CanWriteToInstallDir" != "true"
+ StrCpy $InitialInstallRequirementsCode "2"
+ ${ElseIf} "$HasRequiredSpaceAvailable" != "true"
+ StrCpy $InitialInstallRequirementsCode "3"
+ ${Else}
+ StrCpy $InitialInstallRequirementsCode "0"
+ ${EndIf}
+ ${EndIf}
+
+ Call CanWrite
+ ${If} "$CanWriteToInstallDir" == "false"
+ MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS_QUIT)$\n$\n$INSTDIR"
+ Quit
+ ${EndIf}
+
+ Call CheckSpace
+ ${If} "$HasRequiredSpaceAvailable" == "false"
+ MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_DISK_SPACE_QUIT)"
+ Quit
+ ${EndIf}
+
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ File /oname=$PLUGINSDIR\stub_common.css "stub_common.css"
+ File /oname=$PLUGINSDIR\stub_common.js "stub_common.js"
+FunctionEnd
+
+; .onGUIInit isn't needed except for RTL locales
+!ifdef ${AB_CD}_rtl
+Function .onGUIInit
+ ${MakeWindowRTL} $HWNDPARENT
+FunctionEnd
+!endif
+
+Function .onGUIEnd
+ Delete "$PLUGINSDIR\_temp"
+ Delete "$PLUGINSDIR\download.exe"
+ Delete "$PLUGINSDIR\${CONFIG_INI}"
+
+ ${UnloadUAC}
+FunctionEnd
+
+Function .onUserAbort
+ WebBrowser::CancelTimer $TimerHandle
+
+ ${If} "$IsDownloadFinished" != ""
+ ; Go ahead and cancel the download so it doesn't keep running while this
+ ; prompt is up. We'll resume it if the user decides to continue.
+ InetBgDL::Get /RESET /END
+
+ ${ShowTaskDialog} $(STUB_CANCEL_PROMPT_HEADING) \
+ $(STUB_CANCEL_PROMPT_MESSAGE) \
+ $(STUB_CANCEL_PROMPT_BUTTON_CONTINUE) \
+ $(STUB_CANCEL_PROMPT_BUTTON_EXIT)
+ Pop $0
+ ${If} $0 == 1002
+ ; The cancel button was clicked
+ Call LaunchHelpPage
+ Call SendPing
+ ${Else}
+ ; Either the continue button was clicked or the dialog was dismissed
+ Call StartDownload
+ ${EndIf}
+ ${Else}
+ Call SendPing
+ ${EndIf}
+
+ ; Aborting the abort will allow SendPing to hide the installer window and
+ ; close the installer after it sends the metrics ping, or allow us to just go
+ ; back to installing if that's what the user selected.
+ Abort
+FunctionEnd
+
+!macro _RegisterAllCustomFunctions
+ GetFunctionAddress $0 getUIString
+ WebBrowser::RegisterCustomFunction $0 "getUIString"
+
+ GetFunctionAddress $0 getTextDirection
+ WebBrowser::RegisterCustomFunction $0 "getTextDirection"
+
+ GetFunctionAddress $0 getFontName
+ WebBrowser::RegisterCustomFunction $0 "getFontName"
+
+ GetFunctionAddress $0 getIsHighContrast
+ WebBrowser::RegisterCustomFunction $0 "getIsHighContrast"
+
+ GetFunctionAddress $0 gotoInstallPage
+ WebBrowser::RegisterCustomFunction $0 "gotoInstallPage"
+
+ GetFunctionAddress $0 getProgressBarPercent
+ WebBrowser::RegisterCustomFunction $0 "getProgressBarPercent"
+!macroend
+!define RegisterAllCustomFunctions "!insertmacro _RegisterAllCustomFunctions"
+
+!macro _StartTimer _INTERVAL_MS _FUNCTION_NAME
+ Push $0
+ GetFunctionAddress $0 ${_FUNCTION_NAME}
+ WebBrowser::CreateTimer $0 ${_INTERVAL_MS}
+ Pop $TimerHandle
+ Pop $0
+!macroend
+!define StartTimer "!insertmacro _StartTimer"
+
+Function gotoInstallPage
+ Pop $0
+ StrCpy $CheckboxCleanupProfile $0
+
+ StrCpy $R9 1
+ Call RelativeGotoPage
+ Push $0
+FunctionEnd
+
+Function getProgressBarPercent
+ ; Custom functions always get one parameter, which we don't use here.
+ ; But we will use $0 as a scratch accumulator register.
+ Pop $0
+ ; This math is getting the progess bar completion fraction and converting it
+ ; to a percentage, but we implement that with the operations in the reverse
+ ; of the intuitive order so that our integer math doesn't truncate to zero.
+ IntOp $0 $ProgressCompleted * 100
+ IntOp $0 $0 / ${PROGRESS_BAR_TOTAL_STEPS}
+ Push $0
+FunctionEnd
+
+Function getTextDirection
+ Pop $0
+ !ifdef ${AB_CD}_rtl
+ Push "rtl"
+ !else
+ Push "ltr"
+ !endif
+FunctionEnd
+
+Function getFontName
+ Pop $0
+ Push $FontFamilyName
+FunctionEnd
+
+Function getIsHighContrast
+ Pop $0
+ Push $UsingHighContrastMode
+FunctionEnd
+
+Function getUIString
+ Pop $0
+ ${Select} $0
+ ${Case} "cleanup_header"
+ ${If} $ProfileCleanupPromptType == 1
+ Push "$(STUB_CLEANUP_REINSTALL_HEADER2)"
+ ${Else}
+ Push "$(STUB_CLEANUP_PAVEOVER_HEADER2)"
+ ${EndIf}
+ ${Case} "cleanup_button"
+ ${If} $ProfileCleanupPromptType == 1
+ Push "$(STUB_CLEANUP_REINSTALL_BUTTON2)"
+ ${Else}
+ Push "$(STUB_CLEANUP_PAVEOVER_BUTTON2)"
+ ${EndIf}
+ ${Case} "cleanup_checkbox"
+ Push "$(STUB_CLEANUP_CHECKBOX_LABEL2)"
+ ${Case} "installing_header"
+ Push "$(STUB_INSTALLING_HEADLINE2)"
+ ${Case} "installing_label"
+ Push "$(STUB_INSTALLING_LABEL2)"
+ ${Case} "installing_content"
+ Push "$(STUB_INSTALLING_BODY2)"
+ ${Case} "installing_blurb_0"
+ Push "$(STUB_BLURB_FIRST1)"
+ ${Case} "installing_blurb_1"
+ Push "$(STUB_BLURB_SECOND1)"
+ ${Case} "installing_blurb_2"
+ Push "$(STUB_BLURB_THIRD1)"
+ ${Case} "global_footer"
+ Push "$(STUB_BLURB_FOOTER2)"
+ ${Default}
+ Push ""
+ ${EndSelect}
+FunctionEnd
+
+Function createProfileCleanup
+ Call ShouldPromptForProfileCleanup
+
+ ${If} $ProfileCleanupPromptType == 0
+ StrCpy $CheckboxCleanupProfile 0
+ Abort ; Skip this page
+ ${EndIf}
+
+ ${RegisterAllCustomFunctions}
+
+ File /oname=$PLUGINSDIR\profile_cleanup.html "profile_cleanup.html"
+ File /oname=$PLUGINSDIR\profile_cleanup_page.css "profile_cleanup_page.css"
+ File /oname=$PLUGINSDIR\profile_cleanup.js "profile_cleanup.js"
+ WebBrowser::ShowPage "$PLUGINSDIR\profile_cleanup.html"
+FunctionEnd
+
+Function createInstall
+ GetDlgItem $0 $HWNDPARENT 1 ; Install button
+ EnableWindow $0 0
+ ShowWindow $0 ${SW_HIDE}
+
+ GetDlgItem $0 $HWNDPARENT 3 ; Back button
+ EnableWindow $0 0
+ ShowWindow $0 ${SW_HIDE}
+
+ GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
+ ; Hide the Cancel button, but don't disable it (or else it won't be possible
+ ; to close the window)
+ ShowWindow $0 ${SW_HIDE}
+
+ ; Get keyboard focus on the parent
+ System::Call "user32::SetFocus(p$HWNDPARENT)"
+
+ ; Set $DownloadReset to true so the first download tick count is measured.
+ StrCpy $DownloadReset "true"
+ StrCpy $IsDownloadFinished "false"
+ StrCpy $DownloadRetryCount "0"
+ StrCpy $DownloadedBytes "0"
+ StrCpy $StartLastDownloadTickCount ""
+ StrCpy $DownloadFirstTransferSeconds ""
+ StrCpy $OpenedDownloadPage "0"
+
+ ClearErrors
+ ReadINIStr $ExistingVersion "$INSTDIR\application.ini" "App" "Version"
+ ${If} ${Errors}
+ StrCpy $ExistingVersion "0"
+ ${EndIf}
+
+ ClearErrors
+ ReadINIStr $ExistingBuildID "$INSTDIR\application.ini" "App" "BuildID"
+ ${If} ${Errors}
+ StrCpy $ExistingBuildID "0"
+ ${EndIf}
+
+ ${GetLocalAppDataFolder} $0
+ ${If} ${FileExists} "$0\Mozilla\Firefox"
+ StrCpy $ExistingProfile "1"
+ ${Else}
+ StrCpy $ExistingProfile "0"
+ ${EndIf}
+
+ StrCpy $DownloadServerIP ""
+
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $StartDownloadPhaseTickCount
+
+ ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log"
+ StrCpy $InstallTotalSteps ${InstallPaveOverTotalSteps}
+ ${Else}
+ StrCpy $InstallTotalSteps ${InstallCleanTotalSteps}
+ ${EndIf}
+
+ ${ITBL3Create}
+ ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
+
+ ; Make sure the file we're about to try to download to doesn't already exist,
+ ; so we don't end up trying to "resume" on top of the wrong file.
+ Delete "$PLUGINSDIR\download.exe"
+
+ ${StartTimer} ${DownloadIntervalMS} StartDownload
+
+ ${RegisterAllCustomFunctions}
+
+ File /oname=$PLUGINSDIR\installing.html "installing.html"
+ File /oname=$PLUGINSDIR\installing_page.css "installing_page.css"
+ File /oname=$PLUGINSDIR\installing.js "installing.js"
+ WebBrowser::ShowPage "$PLUGINSDIR\installing.html"
+FunctionEnd
+
+Function StartDownload
+ WebBrowser::CancelTimer $TimerHandle
+
+ Call GetDownloadURL
+ Pop $0
+ InetBgDL::Get "$0" "$PLUGINSDIR\download.exe" \
+ /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+
+ ${StartTimer} ${DownloadIntervalMS} OnDownload
+
+ ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
+ RmDir /r "$INSTDIR\${TO_BE_DELETED}"
+ ${EndIf}
+FunctionEnd
+
+Function SetProgressBars
+ ${ITBL3SetProgressValue} "$ProgressCompleted" "${PROGRESS_BAR_TOTAL_STEPS}"
+FunctionEnd
+
+Function OnDownload
+ InetBgDL::GetStats
+ # $0 = HTTP status code, 0=Completed
+ # $1 = Completed files
+ # $2 = Remaining files
+ # $3 = Number of downloaded bytes for the current file
+ # $4 = Size of current file (Empty string if the size is unknown)
+ # /RESET must be used if status $0 > 299 (e.g. failure), even if resuming
+ # When status is $0 =< 299 it is handled by InetBgDL
+ StrCpy $DownloadServerIP "$5"
+ ${If} $0 > 299
+ WebBrowser::CancelTimer $TimerHandle
+ IntOp $DownloadRetryCount $DownloadRetryCount + 1
+ ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
+ StrCpy $ExitCode "${ERR_DOWNLOAD_TOO_MANY_RETRIES}"
+ ; Use a timer so the UI has a chance to update
+ ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
+ Return
+ ${EndIf}
+
+ ; 1000 is a special code meaning InetBgDL lost the connection before it got
+ ; all the bytes it was expecting. We'll try to resume the transfer in that
+ ; case (assuming we aren't out of retries), so don't treat it as a reset
+ ; or clear the progress bar.
+ ${If} $0 != 1000
+ ${If} "$DownloadReset" != "true"
+ StrCpy $DownloadedBytes "0"
+ ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
+ ${EndIf}
+ StrCpy $DownloadSizeBytes ""
+ StrCpy $DownloadReset "true"
+ Delete "$PLUGINSDIR\download.exe"
+ ${EndIf}
+
+ InetBgDL::Get /RESET /END
+ ${StartTimer} ${DownloadRetryIntervalMS} StartDownload
+ Return
+ ${EndIf}
+
+ ${If} "$DownloadReset" == "true"
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $StartLastDownloadTickCount
+ StrCpy $DownloadReset "false"
+ ; The seconds elapsed from the start of the download phase until the first
+ ; bytes are received are only recorded for the first request so it is
+ ; possible to determine connection issues for the first request.
+ ${If} "$DownloadFirstTransferSeconds" == ""
+ ; Get the seconds elapsed from the start of the download phase until the
+ ; first bytes are received.
+ ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$StartLastDownloadTickCount" $DownloadFirstTransferSeconds
+ ${EndIf}
+ ${EndIf}
+
+ ${If} "$DownloadSizeBytes" == ""
+ ${AndIf} "$4" != ""
+ StrCpy $DownloadSizeBytes "$4"
+ StrCpy $ProgressCompleted 0
+ ${EndIf}
+
+ ; Don't update the status until after the download starts
+ ${If} $2 != 0
+ ${AndIf} "$4" == ""
+ Return
+ ${EndIf}
+
+ ${If} $IsDownloadFinished != "true"
+ ${If} $2 == 0
+ WebBrowser::CancelTimer $TimerHandle
+ StrCpy $IsDownloadFinished "true"
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndDownloadPhaseTickCount
+
+ ${If} "$DownloadSizeBytes" == ""
+ ; It's possible for the download to finish before we were able to
+ ; get the size while it was downloading, and InetBgDL doesn't report
+ ; it afterwards. Use the size of the finished file.
+ ClearErrors
+ FileOpen $5 "$PLUGINSDIR\download.exe" r
+ ${IfNot} ${Errors}
+ FileSeek $5 0 END $DownloadSizeBytes
+ FileClose $5
+ ${EndIf}
+ ${EndIf}
+ StrCpy $DownloadedBytes "$DownloadSizeBytes"
+
+ ; Update the progress bars first in the UI change so they take affect
+ ; before other UI changes.
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_DOWNLOAD_END_STEP}"
+ Call SetProgressBars
+
+ ; Disable the Cancel button during the install
+ GetDlgItem $5 $HWNDPARENT 2
+ EnableWindow $5 0
+
+ ; Open a handle to prevent modification of the full installer
+ StrCpy $R9 "${INVALID_HANDLE_VALUE}"
+ System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
+ i ${GENERIC_READ}, \
+ i ${FILE_SHARE_READ}, i 0, \
+ i ${OPEN_EXISTING}, i 0, i 0) i .R9'
+ StrCpy $HandleDownload "$R9"
+
+ ${If} $HandleDownload == ${INVALID_HANDLE_VALUE}
+ StrCpy $ExitCode "${ERR_PREINSTALL_INVALID_HANDLE}"
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndPreInstallPhaseTickCount
+ ; Use a timer so the UI has a chance to update
+ ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
+ ${Else}
+ CertCheck::CheckPETrustAndInfoAsync "$PLUGINSDIR\download.exe" \
+ "${CertNameDownload}" "${CertIssuerDownload}"
+ ${StartTimer} ${DownloadIntervalMS} OnCertCheck
+ ${EndIf}
+ ${Else}
+ StrCpy $DownloadedBytes "$3"
+ System::Int64Op $DownloadedBytes * ${PROGRESS_BAR_DOWNLOAD_END_STEP}
+ Pop $ProgressCompleted
+ System::Int64Op $ProgressCompleted / $DownloadSizeBytes
+ Pop $ProgressCompleted
+ Call SetProgressBars
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+Function OnCertCheck
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndPreInstallPhaseTickCount
+
+ CertCheck::GetStatus
+ Pop $0
+ ${If} $0 == 0
+ ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $0
+ ${If} $0 >= ${PreinstallCertCheckMaxWaitSec}
+ WebBrowser::CancelTimer $TimerHandle
+ StrCpy $ExitCode "${ERR_PREINSTALL_CERT_TIMEOUT}"
+ ; Use a timer so the UI has a chance to update
+ ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
+ ${EndIf}
+ Return
+ ${EndIf}
+ Pop $0
+ Pop $1
+
+ ${If} $0 == 0
+ ${AndIf} $1 == 0
+ StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES}"
+ ${ElseIf} $0 == 0
+ StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED}"
+ ${ElseIf} $1 == 0
+ StrCpy $ExitCode "${ERR_PREINSTALL_CERT_ATTRIBUTES}"
+ ${EndIf}
+
+ WebBrowser::CancelTimer $TimerHandle
+
+ ${If} $0 == 0
+ ${OrIf} $1 == 0
+ ; Use a timer so the UI has a chance to update
+ ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
+ Return
+ ${EndIf}
+
+ Call LaunchFullInstaller
+FunctionEnd
+
+Function LaunchFullInstaller
+ ; Instead of extracting the files we use the downloaded installer to
+ ; install in case it needs to perform operations that the stub doesn't
+ ; know about.
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
+
+ ; Always create a start menu shortcut, so the user always has some way
+ ; to access the application.
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
+
+ ; Either avoid or force adding a taskbar pin and desktop shortcut
+ ; based on the checkbox value.
+ ${If} $CheckboxShortcuts == 0
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
+ ${Else}
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
+ ${EndIf}
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ${If} $CheckboxInstallMaintSvc == 1
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "true"
+ ${Else}
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
+ ${EndIf}
+!else
+ WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
+!endif
+
+ ; Delete the taskbar shortcut history to ensure we do the right thing based on
+ ; the config file above.
+ ${GetShortcutsLogPath} $0
+ Delete "$0"
+
+ ${RemovePrecompleteEntries} "false"
+
+ ; Delete the install.log and let the full installer create it. When the
+ ; installer closes it we can detect that it has completed.
+ Delete "$INSTDIR\install.log"
+
+ ; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
+ ; since it being present will require an OS restart for the full
+ ; installer.
+ Delete "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ Delete "$INSTDIR\${FileMainEXE}.moz-delete"
+
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndPreInstallPhaseTickCount
+
+ Exec "$\"$PLUGINSDIR\download.exe$\" /LaunchedFromStub /INI=$PLUGINSDIR\${CONFIG_INI}"
+ ${StartTimer} ${InstallIntervalMS} CheckInstall
+FunctionEnd
+
+Function SendPing
+ HideWindow
+
+ ${If} $CheckboxSendPing == 1
+ ; Get the tick count for the completion of all phases.
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndFinishPhaseTickCount
+
+ ; When the value of $IsDownloadFinished is false the download was started
+ ; but didn't finish. In this case the tick count stored in
+ ; $EndFinishPhaseTickCount is used to determine how long the download was
+ ; in progress.
+ ${If} "$IsDownloadFinished" == "false"
+ StrCpy $EndDownloadPhaseTickCount "$EndFinishPhaseTickCount"
+ ; Cancel the download in progress
+ InetBgDL::Get /RESET /END
+ ${EndIf}
+
+
+ ; When $DownloadFirstTransferSeconds equals an empty string the download
+ ; never successfully started so set the value to 0. It will be possible to
+ ; determine that the download didn't successfully start from the seconds for
+ ; the last download.
+ ${If} "$DownloadFirstTransferSeconds" == ""
+ StrCpy $DownloadFirstTransferSeconds "0"
+ ${EndIf}
+
+ ; When $StartLastDownloadTickCount equals an empty string the download never
+ ; successfully started so set the value to $EndDownloadPhaseTickCount to
+ ; compute the correct value.
+ ${If} $StartLastDownloadTickCount == ""
+ ; This could happen if the download never successfully starts
+ StrCpy $StartLastDownloadTickCount "$EndDownloadPhaseTickCount"
+ ${EndIf}
+
+ ; When $EndPreInstallPhaseTickCount equals 0 the installation phase was
+ ; never completed so set its value to $EndFinishPhaseTickCount to compute
+ ; the correct value.
+ ${If} "$EndPreInstallPhaseTickCount" == "0"
+ StrCpy $EndPreInstallPhaseTickCount "$EndFinishPhaseTickCount"
+ ${EndIf}
+
+ ; When $EndInstallPhaseTickCount equals 0 the installation phase was never
+ ; completed so set its value to $EndFinishPhaseTickCount to compute the
+ ; correct value.
+ ${If} "$EndInstallPhaseTickCount" == "0"
+ StrCpy $EndInstallPhaseTickCount "$EndFinishPhaseTickCount"
+ ${EndIf}
+
+ ; Get the seconds elapsed from the start of the download phase to the end of
+ ; the download phase.
+ ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$EndDownloadPhaseTickCount" $0
+
+ ; Get the seconds elapsed from the start of the last download to the end of
+ ; the last download.
+ ${GetSecondsElapsed} "$StartLastDownloadTickCount" "$EndDownloadPhaseTickCount" $1
+
+ ; Get the seconds elapsed from the end of the download phase to the
+ ; completion of the pre-installation check phase.
+ ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $2
+
+ ; Get the seconds elapsed from the end of the pre-installation check phase
+ ; to the completion of the installation phase.
+ ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
+
+ ; Get the seconds elapsed from the end of the installation phase to the
+ ; completion of all phases.
+ ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
+
+ ${If} $ArchToInstall == ${ARCH_AMD64}
+ ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
+ StrCpy $R0 "1"
+ ${Else}
+ StrCpy $R0 "0"
+ ${EndIf}
+
+ ${If} ${IsNativeAMD64}
+ ${OrIf} ${IsNativeARM64}
+ StrCpy $R1 "1"
+ ${Else}
+ StrCpy $R1 "0"
+ ${EndIf}
+
+ ; Though these values are sometimes incorrect due to bug 444664 it happens
+ ; so rarely it isn't worth working around it by reading the registry values.
+ ${WinVerGetMajor} $5
+ ${WinVerGetMinor} $6
+ ${WinVerGetBuild} $7
+ ${WinVerGetServicePackLevel} $8
+ ${If} ${IsServerOS}
+ StrCpy $9 "1"
+ ${Else}
+ StrCpy $9 "0"
+ ${EndIf}
+
+ ${If} "$ExitCode" == "${ERR_SUCCESS}"
+ ReadINIStr $R5 "$INSTDIR\application.ini" "App" "Version"
+ ReadINIStr $R6 "$INSTDIR\application.ini" "App" "BuildID"
+ ${Else}
+ StrCpy $R5 "0"
+ StrCpy $R6 "0"
+ ${EndIf}
+
+ ; Capture the distribution ID and version if it exists.
+ ${If} ${FileExists} "$INSTDIR\distribution\distribution.ini"
+ ReadINIStr $DistributionID "$INSTDIR\distribution\distribution.ini" "Global" "id"
+ ReadINIStr $DistributionVersion "$INSTDIR\distribution\distribution.ini" "Global" "version"
+ ${Else}
+ StrCpy $DistributionID "0"
+ StrCpy $DistributionVersion "0"
+ ${EndIf}
+
+ ; Whether installed into the default installation directory
+ ${GetLongPath} "$INSTDIR" $R7
+ ${GetLongPath} "$InitialInstallDir" $R8
+ ${If} "$R7" == "$R8"
+ StrCpy $R7 "1"
+ ${Else}
+ StrCpy $R7 "0"
+ ${EndIf}
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
+ "Write Test"
+ ${If} ${Errors}
+ StrCpy $R8 "0"
+ ${Else}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $R8 "1"
+ ${EndIf}
+
+ ${If} "$DownloadServerIP" == ""
+ StrCpy $DownloadServerIP "Unknown"
+ ${EndIf}
+
+ StrCpy $R2 ""
+ SetShellVarContext current ; Set SHCTX to the current user
+ ReadRegStr $R2 HKCU "Software\Classes\http\shell\open\command" ""
+ ${If} $R2 != ""
+ ${GetPathFromString} "$R2" $R2
+ ${GetParent} "$R2" $R3
+ ${GetLongPath} "$R3" $R3
+ ${If} $R3 == $INSTDIR
+ StrCpy $R2 "1" ; This Firefox install is set as default.
+ ${Else}
+ StrCpy $R2 "$R2" "" -11 # length of firefox.exe
+ ${If} "$R2" == "${FileMainEXE}"
+ StrCpy $R2 "2" ; Another Firefox install is set as default.
+ ${Else}
+ StrCpy $R2 "0"
+ ${EndIf}
+ ${EndIf}
+ ${Else}
+ StrCpy $R2 "0" ; Firefox is not set as default.
+ ${EndIf}
+
+ ${If} "$R2" == "0"
+ StrCpy $R3 ""
+ ReadRegStr $R2 HKLM "Software\Classes\http\shell\open\command" ""
+ ${If} $R2 != ""
+ ${GetPathFromString} "$R2" $R2
+ ${GetParent} "$R2" $R3
+ ${GetLongPath} "$R3" $R3
+ ${If} $R3 == $INSTDIR
+ StrCpy $R2 "1" ; This Firefox install is set as default.
+ ${Else}
+ StrCpy $R2 "$R2" "" -11 # length of firefox.exe
+ ${If} "$R2" == "${FileMainEXE}"
+ StrCpy $R2 "2" ; Another Firefox install is set as default.
+ ${Else}
+ StrCpy $R2 "0"
+ ${EndIf}
+ ${EndIf}
+ ${Else}
+ StrCpy $R2 "0" ; Firefox is not set as default.
+ ${EndIf}
+ ${EndIf}
+
+ StrCpy $R3 "1"
+
+!ifdef STUB_DEBUG
+ MessageBox MB_OK "${BaseURLStubPing} \
+ $\nStub URL Version = ${StubURLVersion}${StubURLVersionAppend} \
+ $\nBuild Channel = ${Channel} \
+ $\nUpdate Channel = ${UpdateChannel} \
+ $\nLocale = ${AB_CD} \
+ $\nFirefox x64 = $R0 \
+ $\nRunning x64 Windows = $R1 \
+ $\nMajor = $5 \
+ $\nMinor = $6 \
+ $\nBuild = $7 \
+ $\nServicePack = $8 \
+ $\nIsServer = $9 \
+ $\nExit Code = $ExitCode \
+ $\nFirefox Launch Code = $FirefoxLaunchCode \
+ $\nDownload Retry Count = $DownloadRetryCount \
+ $\nDownloaded Bytes = $DownloadedBytes \
+ $\nDownload Size Bytes = $DownloadSizeBytes \
+ $\nIntroduction Phase Seconds = $IntroPhaseSeconds \
+ $\nOptions Phase Seconds = $OptionsPhaseSeconds \
+ $\nDownload Phase Seconds = $0 \
+ $\nLast Download Seconds = $1 \
+ $\nDownload First Transfer Seconds = $DownloadFirstTransferSeconds \
+ $\nPreinstall Phase Seconds = $2 \
+ $\nInstall Phase Seconds = $3 \
+ $\nFinish Phase Seconds = $4 \
+ $\nInitial Install Requirements Code = $InitialInstallRequirementsCode \
+ $\nOpened Download Page = $OpenedDownloadPage \
+ $\nExisting Profile = $ExistingProfile \
+ $\nExisting Version = $ExistingVersion \
+ $\nExisting Build ID = $ExistingBuildID \
+ $\nNew Version = $R5 \
+ $\nNew Build ID = $R6 \
+ $\nDefault Install Dir = $R7 \
+ $\nHas Admin = $R8 \
+ $\nDefault Status = $R2 \
+ $\nSet As Sefault Status = $R3 \
+ $\nDownload Server IP = $DownloadServerIP \
+ $\nPost-Signing Data = $PostSigningData \
+ $\nProfile cleanup prompt shown = $ProfileCleanupPromptType \
+ $\nDid profile cleanup = $CheckboxCleanupProfile \
+ $\nDistribution ID = $DistributionID \
+ $\nDistribution Version = $DistributionVersion"
+ ; The following will exit the installer
+ SetAutoClose true
+ StrCpy $R9 "2"
+ Call RelativeGotoPage
+!else
+ ${StartTimer} ${DownloadIntervalMS} OnPing
+ InetBgDL::Get "${BaseURLStubPing}/${StubURLVersion}${StubURLVersionAppend}/${Channel}/${UpdateChannel}/${AB_CD}/$R0/$R1/$5/$6/$7/$8/$9/$ExitCode/$FirefoxLaunchCode/$DownloadRetryCount/$DownloadedBytes/$DownloadSizeBytes/$IntroPhaseSeconds/$OptionsPhaseSeconds/$0/$1/$DownloadFirstTransferSeconds/$2/$3/$4/$InitialInstallRequirementsCode/$OpenedDownloadPage/$ExistingProfile/$ExistingVersion/$ExistingBuildID/$R5/$R6/$R7/$R8/$R2/$R3/$DownloadServerIP/$PostSigningData/$ProfileCleanupPromptType/$CheckboxCleanupProfile/$DistributionID/$DistributionVersion" \
+ "$PLUGINSDIR\_temp" /END
+!endif
+ ${Else}
+ ${If} "$IsDownloadFinished" == "false"
+ ; Cancel the download in progress
+ InetBgDL::Get /RESET /END
+ ${EndIf}
+ ; The following will exit the installer
+ SetAutoClose true
+ StrCpy $R9 "2"
+ Call RelativeGotoPage
+ ${EndIf}
+FunctionEnd
+
+Function OnPing
+ InetBgDL::GetStats
+ # $0 = HTTP status code, 0=Completed
+ # $1 = Completed files
+ # $2 = Remaining files
+ # $3 = Number of downloaded bytes for the current file
+ # $4 = Size of current file (Empty string if the size is unknown)
+ # /RESET must be used if status $0 > 299 (e.g. failure)
+ # When status is $0 =< 299 it is handled by InetBgDL
+ ${If} $2 == 0
+ ${OrIf} $0 > 299
+ WebBrowser::CancelTimer $TimerHandle
+ ${If} $0 > 299
+ InetBgDL::Get /RESET /END
+ ${EndIf}
+ ; The following will exit the installer
+ SetAutoClose true
+ StrCpy $R9 "2"
+ Call RelativeGotoPage
+ ${EndIf}
+FunctionEnd
+
+Function CheckInstall
+ IntOp $InstallCounterStep $InstallCounterStep + 1
+ ${If} $InstallCounterStep >= $InstallTotalSteps
+ WebBrowser::CancelTimer $TimerHandle
+ ; Close the handle that prevents modification of the full installer
+ System::Call 'kernel32::CloseHandle(i $HandleDownload)'
+ StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
+ ; Use a timer so the UI has a chance to update
+ ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
+ Return
+ ${EndIf}
+
+ ${If} $ProgressCompleted < ${PROGRESS_BAR_INSTALL_END_STEP}
+ IntOp $0 ${PROGRESS_BAR_INSTALL_END_STEP} - ${PROGRESS_BAR_DOWNLOAD_END_STEP}
+ IntOp $0 $InstallCounterStep * $0
+ IntOp $0 $0 / $InstallTotalSteps
+ IntOp $ProgressCompleted ${PROGRESS_BAR_DOWNLOAD_END_STEP} + $0
+ Call SetProgressBars
+ ${EndIf}
+
+ ${If} ${FileExists} "$INSTDIR\install.log"
+ Delete "$INSTDIR\install.tmp"
+ CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
+
+ ; The unfocus and refocus that happens approximately here is caused by the
+ ; installer calling RefreshShellIcons to refresh the shortcut icons.
+
+ ; When the full installer completes the installation the install.log will no
+ ; longer be in use.
+ ClearErrors
+ Delete "$INSTDIR\install.log"
+ ${Unless} ${Errors}
+ WebBrowser::CancelTimer $TimerHandle
+ ; Close the handle that prevents modification of the full installer
+ System::Call 'kernel32::CloseHandle(i $HandleDownload)'
+ Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
+ Delete "$PLUGINSDIR\download.exe"
+ Delete "$PLUGINSDIR\${CONFIG_INI}"
+ System::Call "kernel32::GetTickCount()l .s"
+ Pop $EndInstallPhaseTickCount
+ Call FinishInstall
+ ${EndUnless}
+ ${EndIf}
+FunctionEnd
+
+Function FinishInstall
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_INSTALL_END_STEP}"
+ Call SetProgressBars
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ Delete "$INSTDIR\${FileMainEXE}"
+ Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
+ ${EndIf}
+
+ StrCpy $ExitCode "${ERR_SUCCESS}"
+
+ ${CopyPostSigningData}
+ Pop $PostSigningData
+
+ Call LaunchApp
+FunctionEnd
+
+Function RelativeGotoPage
+ IntCmp $R9 0 0 Move Move
+ StrCmp $R9 "X" 0 Move
+ StrCpy $R9 "120"
+
+ Move:
+ SendMessage $HWNDPARENT "0x408" "$R9" ""
+FunctionEnd
+
+Function CheckSpace
+ ${If} "$ExistingTopDir" != ""
+ StrLen $0 "$ExistingTopDir"
+ StrLen $1 "$INSTDIR"
+ ${If} $0 <= $1
+ StrCpy $2 "$INSTDIR" $3
+ ${If} "$2" == "$ExistingTopDir"
+ Return
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ StrCpy $ExistingTopDir "$INSTDIR"
+ ${DoUntil} ${FileExists} "$ExistingTopDir"
+ ${GetParent} "$ExistingTopDir" $ExistingTopDir
+ ${If} "$ExistingTopDir" == ""
+ StrCpy $SpaceAvailableBytes "0"
+ StrCpy $HasRequiredSpaceAvailable "false"
+ Return
+ ${EndIf}
+ ${Loop}
+
+ ${GetLongPath} "$ExistingTopDir" $ExistingTopDir
+
+ ; GetDiskFreeSpaceExW requires a backslash.
+ StrCpy $0 "$ExistingTopDir" "" -1 ; the last character
+ ${If} "$0" != "\"
+ StrCpy $0 "\"
+ ${Else}
+ StrCpy $0 ""
+ ${EndIf}
+
+ System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i("$ExistingTopDir$0", .r1, .r2, .r3) .'
+ StrCpy $SpaceAvailableBytes "$1"
+
+ System::Int64Op $SpaceAvailableBytes / 1048576
+ Pop $1
+ System::Int64Op $1 > ${APPROXIMATE_REQUIRED_SPACE_MB}
+ Pop $1
+ ${If} $1 == 1
+ StrCpy $HasRequiredSpaceAvailable "true"
+ ${Else}
+ StrCpy $HasRequiredSpaceAvailable "false"
+ ${EndIf}
+FunctionEnd
+
+Function CanWrite
+ StrCpy $CanWriteToInstallDir "false"
+
+ StrCpy $0 "$INSTDIR"
+ ; Use the existing directory when it exists
+ ${Unless} ${FileExists} "$INSTDIR"
+ ; Get the topmost directory that exists for new installs
+ ${DoUntil} ${FileExists} "$0"
+ ${GetParent} "$0" $0
+ ${If} "$0" == ""
+ Return
+ ${EndIf}
+ ${Loop}
+ ${EndUnless}
+
+ GetTempFileName $2 "$0"
+ Delete $2
+ CreateDirectory "$2"
+
+ ${If} ${FileExists} "$2"
+ ${If} ${FileExists} "$INSTDIR"
+ GetTempFileName $3 "$INSTDIR"
+ ${Else}
+ GetTempFileName $3 "$2"
+ ${EndIf}
+ ${If} ${FileExists} "$3"
+ Delete "$3"
+ StrCpy $CanWriteToInstallDir "true"
+ ${EndIf}
+ RmDir "$2"
+ ${EndIf}
+FunctionEnd
+
+Function LaunchApp
+ StrCpy $FirefoxLaunchCode "2"
+
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ ${If} $CheckboxCleanupProfile == 1
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
+ ${Else}
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
+ ${EndIf}
+ ${Else}
+ StrCpy $R1 $CheckboxCleanupProfile
+ GetFunctionAddress $0 LaunchAppFromElevatedProcess
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+
+ StrCpy $AppLaunchWaitTickCount 0
+ ${StartTimer} ${AppLaunchWaitIntervalMS} WaitForAppLaunch
+FunctionEnd
+
+Function LaunchAppFromElevatedProcess
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ${If} $R1 == 1
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
+ ${Else}
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
+ ${EndIf}
+FunctionEnd
+
+Function WaitForAppLaunch
+ FindWindow $0 "${MainWindowClass}"
+ FindWindow $1 "${DialogWindowClass}"
+ ${If} $0 <> 0
+ ${OrIf} $1 <> 0
+ WebBrowser::CancelTimer $TimerHandle
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_APP_LAUNCH_END_STEP}"
+ Call SetProgressBars
+ Call SendPing
+ Return
+ ${EndIf}
+
+ IntOp $AppLaunchWaitTickCount $AppLaunchWaitTickCount + 1
+ IntOp $0 $AppLaunchWaitTickCount * ${AppLaunchWaitIntervalMS}
+ ${If} $0 >= ${AppLaunchWaitTimeoutMS}
+ ; We've waited an unreasonably long time, so just exit.
+ WebBrowser::CancelTimer $TimerHandle
+ Call SendPing
+ Return
+ ${EndIf}
+
+ ${If} $ProgressCompleted < ${PROGRESS_BAR_APP_LAUNCH_END_STEP}
+ IntOp $ProgressCompleted $ProgressCompleted + 1
+ Call SetProgressBars
+ ${EndIf}
+FunctionEnd
+
+Function DisplayDownloadError
+ WebBrowser::CancelTimer $TimerHandle
+ ; To better display the error state on the taskbar set the progress completed
+ ; value to the total value.
+ ${ITBL3SetProgressValue} "100" "100"
+ ${ITBL3SetProgressState} "${TBPF_ERROR}"
+
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD_CONT)" IDCANCEL +2 IDOK +1
+ Call LaunchHelpPage
+ Call SendPing
+FunctionEnd
+
+Function LaunchHelpPage
+ StrCpy $OpenedDownloadPage "1" ; Already initialized to 0
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ Call OpenManualDownloadURL
+ ${Else}
+ GetFunctionAddress $0 OpenManualDownloadURL
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+FunctionEnd
+
+Function OpenManualDownloadURL
+ ClearErrors
+ ReadINIStr $0 "${PARTNER_INI}" "DownloadURL" "FallbackPage"
+ ${IfNot} ${Errors}
+ ExecShell "open" "$0"
+ ${Else}
+ ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
+ ${EndIf}
+FunctionEnd
+
+Function ShouldPromptForProfileCleanup
+ ; This will be our return value.
+ StrCpy $ProfileCleanupPromptType 0
+
+ ; Only consider installations of the same architecture we're installing.
+ ${If} $ArchToInstall == ${ARCH_AMD64}
+ ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
+ SetRegView 64
+ ${Else}
+ SetRegView 32
+ ${EndIf}
+
+ ; Make sure $APPDATA is the user's AppData and not ProgramData.
+ ; We'll set this back to all at the end of the function.
+ SetShellVarContext current
+
+ ${FindInstallSpecificProfile}
+ Pop $R0
+
+ ${If} $R0 == ""
+ ; We don't have an install-specific profile, so look for an old-style
+ ; default profile instead by checking each numbered Profile section.
+ StrCpy $0 0
+ ${Do}
+ ClearErrors
+ ; Check if the section exists by reading a value that must be present.
+ ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
+ ${If} ${Errors}
+ ; We've run out of profile sections.
+ ${Break}
+ ${EndIf}
+
+ ClearErrors
+ ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Default"
+ ${IfNot} ${Errors}
+ ${AndIf} $1 == "1"
+ ; We've found the default profile
+ ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
+ ReadINIStr $2 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "IsRelative"
+ ${If} $2 == "1"
+ StrCpy $R0 "$APPDATA\Mozilla\Firefox\$1"
+ ${Else}
+ StrCpy $R0 "$1"
+ ${EndIf}
+ ${Break}
+ ${EndIf}
+
+ IntOp $0 $0 + 1
+ ${Loop}
+ ${EndIf}
+
+ GetFullPathName $R0 $R0
+
+ ${If} $R0 == ""
+ ; No profile to clean up, so don't show the cleanup prompt.
+ GoTo end
+ ${EndIf}
+
+ ; We have at least one profile present. If we don't have any installations,
+ ; then we need to show the re-install prompt. We'll say there's an
+ ; installation present if HKCR\FirefoxURL* exists and points to a real path.
+ StrCpy $0 0
+ StrCpy $R9 ""
+ ${Do}
+ ClearErrors
+ EnumRegKey $1 HKCR "" $0
+ ${If} ${Errors}
+ ${OrIf} $1 == ""
+ ${Break}
+ ${EndIf}
+ ${WordFind} "$1" "-" "+1{" $2
+ ${If} $2 == "FirefoxURL"
+ ClearErrors
+ ReadRegStr $2 HKCR "$1\DefaultIcon" ""
+ ${IfNot} ${Errors}
+ ${GetPathFromString} $2 $1
+ ${If} ${FileExists} $1
+ StrCpy $R9 $1
+ ${Break}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+ IntOp $0 $0 + 1
+ ${Loop}
+ ${If} $R9 == ""
+ StrCpy $ProfileCleanupPromptType 1
+ GoTo end
+ ${EndIf}
+
+ ; Okay, there's at least one install, let's see if it's for this channel.
+ SetShellVarContext all
+ ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+ ${If} $0 == "false"
+ SetShellVarContext current
+ ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+ ${If} $0 == "false"
+ ; Existing installs are not for this channel. Don't show any prompt.
+ GoTo end
+ ${EndIf}
+ ${EndIf}
+
+ ; Find out what version the default profile was last used on.
+ ${If} ${FileExists} "$R0\compatibility.ini"
+ ClearErrors
+ ReadINIStr $0 "$R0\compatibility.ini" "Compatibility" "LastVersion"
+ ${If} ${Errors}
+ GoTo end
+ ${EndIf}
+ ${WordFind} $0 "." "+1{" $0
+
+ ; We don't know what version we're about to install because we haven't
+ ; downloaded it yet. Find out what the latest version released on this
+ ; channel is and assume we'll be installing that one.
+ Call GetLatestReleasedVersion
+ ${If} ${Errors}
+ ; Use this stub installer's version as a fallback when we can't get the
+ ; real current version; this may be behind, but it's better than nothing.
+ StrCpy $1 ${AppVersion}
+ ${EndIf}
+
+ ${WordFind} $1 "." "+1{" $1
+ IntOp $1 $1 - 2
+
+ ${If} $1 > $0
+ ; Default profile was last used more than two versions ago, so we need
+ ; to show the paveover version of the profile cleanup prompt.
+ StrCpy $ProfileCleanupPromptType 2
+ ${EndIf}
+ ${EndIf}
+
+ end:
+ SetRegView lastused
+ SetShellVarContext all
+FunctionEnd
+
+Function GetLatestReleasedVersion
+ ClearErrors
+ Push $0 ; InetBgDl::GetStats uses $0 for the HTTP error code
+ ; $1 is our return value, so don't save it
+ Push $2 ; InetBgDl::GetStats uses $2 to tell us when the transfer is done
+ Push $3 ; $3 - $5 are also set by InetBgDl::GetStats, but we don't use them
+ Push $4
+ Push $5
+ Push $6 ; This is our response timeout counter
+
+ InetBgDL::Get /RESET /END
+ InetBgDL::Get "https://product-details.mozilla.org/1.0/firefox_versions.json" \
+ "$PLUGINSDIR\firefox_versions.json" \
+ /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+
+ ; Wait for the response, but only give it half a second since this is on the
+ ; installer startup path (we haven't even shown a window yet).
+ StrCpy $6 0
+ ${Do}
+ Sleep 100
+ InetBgDL::GetStats
+ IntOp $6 $6 + 1
+
+ ${If} $2 == 0
+ ${Break}
+ ${ElseIf} $6 >= 5
+ InetBgDL::Get /RESET /END
+ SetErrors
+ GoTo end
+ ${EndIf}
+ ${Loop}
+
+ StrCpy $1 0
+ nsJSON::Set /file "$PLUGINSDIR\firefox_versions.json"
+ IfErrors end
+ ${Select} ${Channel}
+ ${Case} "unofficial"
+ StrCpy $1 "FIREFOX_NIGHTLY"
+ ${Case} "nightly"
+ StrCpy $1 "FIREFOX_NIGHTLY"
+ ${Case} "aurora"
+ StrCpy $1 "FIREFOX_DEVEDITION"
+ ${Case} "beta"
+ StrCpy $1 "LATEST_FIREFOX_RELEASED_DEVEL_VERSION"
+ ${Case} "release"
+ StrCpy $1 "LATEST_FIREFOX_VERSION"
+ ${EndSelect}
+ nsJSON::Get $1 /end
+
+ end:
+ ${If} ${Errors}
+ ${OrIf} $1 == 0
+ SetErrors
+ StrCpy $1 0
+ ${Else}
+ Pop $1
+ ${EndIf}
+
+ Pop $6
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $0
+FunctionEnd
+
+; Determine which architecture build we should download and install.
+; AArch64 is always selected if it's the native architecture of the machine.
+; Otherwise, we check a few things to determine if AMD64 is appropriate:
+; 1) Running a 64-bit OS (we've already checked the OS version).
+; 2) An amount of RAM strictly greater than RAM_NEEDED_FOR_64BIT
+; 3) No third-party products installed that cause issues with the 64-bit build.
+; Currently this includes Lenovo OneKey Theater and Lenovo Energy Management.
+; We also make sure that the partner.ini file contains a download URL for the
+; selected architecture, when a partner.ini file eixsts.
+; If any of those checks fail, the 32-bit x86 build is selected.
+Function GetArchToInstall
+ StrCpy $ArchToInstall ${ARCH_X86}
+
+ ${If} ${IsNativeARM64}
+ StrCpy $ArchToInstall ${ARCH_AARCH64}
+ GoTo downloadUrlCheck
+ ${EndIf}
+
+ ${IfNot} ${IsNativeAMD64}
+ Return
+ ${EndIf}
+
+ System::Call "*(i 64, i, l 0, l, l, l, l, l, l)p.r1"
+ System::Call "Kernel32::GlobalMemoryStatusEx(p r1)"
+ System::Call "*$1(i, i, l.r2, l, l, l, l, l, l)"
+ System::Free $1
+ ${If} $2 L<= ${RAM_NEEDED_FOR_64BIT}
+ Return
+ ${EndIf}
+
+ ; Lenovo OneKey Theater can theoretically be in a directory other than this
+ ; one, because some installer versions let you change it, but it's unlikely.
+ ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Onekey Theater\windowsapihookdll64.dll"
+ Return
+ ${EndIf}
+
+ ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Energy Management\Energy Management.exe"
+ Return
+ ${EndIf}
+
+ StrCpy $ArchToInstall ${ARCH_AMD64}
+
+ downloadUrlCheck:
+ ; If we've selected an architecture that doesn't have a download URL in the
+ ; partner.ini, but there is a URL there for 32-bit x86, then fall back to
+ ; 32-bit x86 on the theory that we should never use a non-partner build if
+ ; we are configured as a partner installer, even if the only build that's
+ ; provided is suboptimal for the machine. If there isn't even an x86 URL,
+ ; then we won't force x86 and GetDownloadURL will stick with the built-in URL.
+ ClearErrors
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
+ ${IfNot} ${Errors}
+ ${If} $ArchToInstall == ${ARCH_AMD64}
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
+ ${If} ${Errors}
+ StrCpy $ArchToInstall ${ARCH_X86}
+ ${EndIf}
+ ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
+ ${If} ${Errors}
+ StrCpy $ArchToInstall ${ARCH_X86}
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+Function GetDownloadURL
+ Push $0
+ Push $1
+
+ ; Start with the appropriate URL from our built-in branding info.
+ ${If} $ArchToInstall == ${ARCH_AMD64}
+ StrCpy $0 "${URLStubDownloadAMD64}${URLStubDownloadAppend}"
+ ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
+ StrCpy $0 "${URLStubDownloadAArch64}${URLStubDownloadAppend}"
+ ${Else}
+ StrCpy $0 "${URLStubDownloadX86}${URLStubDownloadAppend}"
+ ${EndIf}
+
+ ; If we have a partner.ini file then use the URL from there instead.
+ ClearErrors
+ ${If} $ArchToInstall == ${ARCH_AMD64}
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
+ ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
+ ${Else}
+ ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
+ ${EndIf}
+ ${IfNot} ${Errors}
+ StrCpy $0 "$1"
+ ${EndIf}
+
+ Pop $1
+ Exch $0
+FunctionEnd
+
+Section
+SectionEnd
diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi
new file mode 100755
index 0000000000..695beed25f
--- /dev/null
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -0,0 +1,1175 @@
+# 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/.
+
+# Required Plugins:
+# AppAssocReg
+# http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# BitsUtils
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/BitsUtils
+# CityHash
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# HttpPostFile
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/HttpPostFile
+# ShellLink
+# http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC
+# http://nsis.sourceforge.net/UAC_plug-in
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+; Attempt to elevate Standard Users in addition to users that
+; are a member of the Administrators group.
+!define NONADMIN_ELEVATE
+
+; prevents compiling of the reg write logging.
+!define NO_LOG
+
+!define MaintUninstallKey \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
+
+Var TmpVal
+Var MaintCertKey
+Var ShouldOpenSurvey
+Var ShouldSendPing
+Var InstalledVersion
+Var InstalledBuildID
+Var ShouldPromptForRefresh
+Var RefreshRequested
+; AddTaskbarSC is defined here in order to silence warnings from inside
+; MigrateTaskBarShortcut and is not intended to be used here.
+; See Bug 1329869 for more.
+Var AddTaskbarSC
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include InstallOptions.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetSize
+!insertmacro StrFilter
+!insertmacro WordReplace
+
+!insertmacro un.GetParent
+
+; The following includes are custom.
+!include branding.nsi
+!include defines.nsi
+!include common.nsh
+!include locales.nsi
+
+; This is named BrandShortName helper because we use this for software update
+; post update cleanup.
+VIAddVersionKey "FileDescription" "${BrandShortName} Helper"
+VIAddVersionKey "OriginalFilename" "helper.exe"
+
+!insertmacro AddDisabledDDEHandlerValues
+!insertmacro CleanVirtualStore
+!insertmacro ElevateUAC
+!insertmacro GetLongPath
+!insertmacro GetPathFromString
+!insertmacro InitHashAppModelId
+!insertmacro IsHandlerForInstallDir
+!insertmacro IsPinnedToTaskBar
+!insertmacro IsUserAdmin
+!insertmacro LogDesktopShortcut
+!insertmacro LogQuickLaunchShortcut
+!insertmacro LogStartMenuShortcut
+!insertmacro PinnedToStartMenuLnkCount
+!insertmacro RegCleanAppHandler
+!insertmacro RegCleanMain
+!insertmacro RegCleanUninstall
+!insertmacro SetAppLSPCategories
+!insertmacro SetBrandNameVars
+!insertmacro UpdateShortcutAppModelIDs
+!insertmacro UnloadUAC
+!insertmacro WriteRegDWORD2
+!insertmacro WriteRegStr2
+
+; This needs to be inserted after InitHashAppModelId because it uses
+; $AppUserModelID and the compiler can't handle using variables lexically before
+; they've been declared.
+!insertmacro GetInstallerRegistryPref
+
+!insertmacro un.ChangeMUIHeaderImage
+!insertmacro un.ChangeMUISidebarImage
+!insertmacro un.CheckForFilesInUse
+!insertmacro un.CleanMaintenanceServiceLogs
+!insertmacro un.CleanVirtualStore
+!insertmacro un.DeleteShortcuts
+!insertmacro un.GetCommonDirectory
+!insertmacro un.GetLongPath
+!insertmacro un.GetSecondInstallPath
+!insertmacro un.InitHashAppModelId
+!insertmacro un.ManualCloseAppPrompt
+!insertmacro un.RegCleanAppHandler
+!insertmacro un.RegCleanFileHandler
+!insertmacro un.RegCleanMain
+!insertmacro un.RegCleanPrefs
+!insertmacro un.RegCleanUninstall
+!insertmacro un.RegCleanProtocolHandler
+!insertmacro un.RemoveQuotesFromPath
+!insertmacro un.RemovePrecompleteEntries
+!insertmacro un.SetAppLSPCategories
+!insertmacro un.SetBrandNameVars
+
+!include shared.nsh
+
+; Helper macros for ui callbacks. Insert these after shared.nsh
+!insertmacro OnEndCommon
+!insertmacro UninstallOnInitCommon
+
+!insertmacro un.OnEndCommon
+!insertmacro un.UninstallUnOnInitCommon
+
+Name "${BrandFullName}"
+OutFile "helper.exe"
+!ifdef HAVE_64BIT_BUILD
+ InstallDir "$PROGRAMFILES64\${BrandFullName}\"
+!else
+ InstallDir "$PROGRAMFILES32\${BrandFullName}\"
+!endif
+ShowUnInstDetails nevershow
+
+!define URLUninstallSurvey "https://qsurvey.mozilla.com/s3/FF-Desktop-Post-Uninstall?channel=${UpdateChannel}&version=${AppVersion}&osversion="
+
+; Support for the profile refresh feature
+!define URLProfileRefreshHelp "https://support.mozilla.org/kb/refresh-firefox-reset-add-ons-and-settings"
+
+; Arguments to add to the command line when launching FileMainEXE for profile refresh
+!define ArgsProfileRefresh "-reset-profile -migration -uninstaller-profile-refresh"
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ABORTWARNING
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+; By default MUI_BGCOLOR is hardcoded to FFFFFF, which is only correct if the
+; the Windows theme or high-contrast mode hasn't changed it, so we need to
+; override that with GetSysColor(COLOR_WINDOW) (this string ends up getting
+; passed to SetCtlColors, which uses this custom syntax to mean that).
+!define MUI_BGCOLOR SYSCLR:WINDOW
+
+; Use a right to left header image when the language is right to left
+!ifdef ${AB_CD}_rtl
+!define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
+!else
+!define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
+!endif
+
+!define MUI_CUSTOMFUNCTION_UNGUIINIT un.GUIInit
+
+/**
+ * Uninstall Pages
+ */
+; Welcome Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.showWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
+!insertmacro MUI_UNPAGE_WELCOME
+
+; Custom Uninstall Confirm Page
+UninstPage custom un.preConfirm
+
+; Remove Files Page
+!insertmacro MUI_UNPAGE_INSTFILES
+
+; Finish Page
+!define MUI_FINISHPAGE_SHOWREADME
+!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
+!define MUI_FINISHPAGE_SHOWREADME_TEXT $(UN_SURVEY_CHECKBOX_LABEL)
+!define MUI_FINISHPAGE_SHOWREADME_FUNCTION un.Survey
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preFinish
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.showFinish
+!insertmacro MUI_UNPAGE_FINISH
+
+; Use the default dialog for IDD_VERIFY for a simple Banner
+ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
+
+################################################################################
+# Helper Functions
+
+Function un.Survey
+ ; We can't actually call ExecInExplorer here because it's going to have to
+ ; make some marshalled COM calls and those are not allowed from within a
+ ; synchronous message handler (where we currently are); we'll be thrown
+ ; RPC_E_CANTCALLOUT_ININPUTSYNCCALL if we try. So all we can do is record
+ ; that we need to make the call later, which we'll do from un.onGUIEnd.
+ StrCpy $ShouldOpenSurvey "1"
+FunctionEnd
+
+; This function is used to uninstall the maintenance service if the
+; application currently being uninstalled is the last application to use the
+; maintenance service.
+Function un.UninstallServiceIfNotUsed
+ ; $0 will store if a subkey exists
+ ; $1 will store the first subkey if it exists or an empty string if it doesn't
+ ; Backup the old values
+ Push $0
+ Push $1
+
+ ; The maintenance service always uses the 64-bit registry on x64 systems
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+
+ ; Figure out the number of subkeys
+ StrCpy $0 0
+ ${Do}
+ EnumRegKey $1 HKLM "Software\Mozilla\MaintenanceService" $0
+ ${If} "$1" == ""
+ ${ExitDo}
+ ${EndIf}
+ IntOp $0 $0 + 1
+ ${Loop}
+
+ ; Restore back the registry view
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+
+ ${If} $0 == 0
+ ; Get the path of the maintenance service uninstaller.
+ ; Look in both the 32-bit and 64-bit registry views.
+ SetRegView 32
+ ReadRegStr $1 HKLM ${MaintUninstallKey} "UninstallString"
+ SetRegView lastused
+
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ ${If} $1 == ""
+ SetRegView 64
+ ReadRegStr $1 HKLM ${MaintUninstallKey} "UninstallString"
+ SetRegView lastused
+ ${EndIf}
+ ${EndIf}
+
+ ; If the uninstall string does not exist, skip executing it
+ ${If} $1 != ""
+ ; $1 is already a quoted string pointing to the install path
+ ; so we're already protected against paths with spaces
+ nsExec::Exec "$1 /S"
+ ${EndIf}
+ ${EndIf}
+
+ ; Restore the old value of $1 and $0
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.LaunchAppForRefresh
+ Push $0
+ Push $1
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" ${ArgsProfileRefresh}"
+ ${Else}
+ GetFunctionAddress $0 un.LaunchAppForRefreshFromElevatedProcess
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.LaunchAppForRefreshFromElevatedProcess
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" ${ArgsProfileRefresh}"
+FunctionEnd
+
+Function un.LaunchRefreshHelpPage
+ Push $0
+ Push $1
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ Call un.OpenRefreshHelpURL
+ ${Else}
+ GetFunctionAddress $0 un.OpenRefreshHelpURL
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.OpenRefreshHelpURL
+ ExecShell "open" "${URLProfileRefreshHelp}"
+FunctionEnd
+
+Function un.SendUninstallPing
+ ; Notably, we only check the non-private AUMID here. There's no good reason
+ ; to send the uninstall ping twice.
+ ${If} $AppUserModelID == ""
+ Return
+ ${EndIf}
+
+ Push $0 ; $0 = Find handle
+ Push $1 ; $1 = Found ping file name
+ Push $2 ; $2 = Directory containing the pings
+ Push $3 ; $3 = Ping file name filespec
+ Push $4 ; $4 = Offset of ID in file name
+ Push $5 ; $5 = URL, POST result
+ Push $6 ; $6 = Full path to the ping file
+
+ Call un.GetCommonDirectory
+ Pop $2
+
+ ; The ping ID is in the file name, so that we can get it for the submission URL
+ ; without having to parse the ping. Since we don't know the exact name, use FindFirst
+ ; to locate the file.
+ ; Format is uninstall_ping_$AppUserModelID_$PingUUID.json
+
+ ; File name base
+ StrCpy $3 "uninstall_ping_$AppUserModelID_"
+ ; Get length of the fixed prefix, this is the offset of ping ID in the file name
+ StrLen $4 $3
+ ; Finish building filespec
+ StrCpy $3 "$2\$3*.json"
+
+ ClearErrors
+ FindFirst $0 $1 $3
+ ; Build the full path
+ StrCpy $6 "$2\$1"
+
+ ${IfNot} ${Errors}
+ ; Copy the ping ID, starting after $AppUserModelID_, ending 5 from the end to remove .json
+ StrCpy $5 $1 -5 $4
+
+ ; Build the full submission URL from the ID
+ ; https://docs.telemetry.mozilla.org/concepts/pipeline/http_edge_spec.html#special-handling-for-firefox-desktop-telemetry
+ ; It's possible for the path components to disagree with the contents of the ping,
+ ; but this should be rare, and shouldn't affect the collection.
+ StrCpy $5 "${TELEMETRY_BASE_URL}/${TELEMETRY_UNINSTALL_PING_NAMESPACE}/$5/${TELEMETRY_UNINSTALL_PING_DOCTYPE}/${AppName}/$InstalledVersion/${UpdateChannel}/$InstalledBuildID?v=4"
+
+ HttpPostFile::Post $6 "Content-Type: application/json$\r$\n" $5
+ ; Pop the result. This could indicate an error if it's something other than
+ ; "success", but we don't have any recovery path here anyway.
+ Pop $5
+
+ ${Do}
+ Delete $6
+
+ ; Continue to delete any other pings from this install. Only the first found will be sent:
+ ; there should only be one ping, if there are more than one then something has gone wrong,
+ ; it seems preferable to not try to send them all.
+ ClearErrors
+ FindNext $0 $1
+ ; Build the full path
+ StrCpy $6 "$2\$1"
+ ${LoopUntil} ${Errors}
+
+ FindClose $0
+ ${Endif}
+
+ Pop $6
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+################################################################################
+# Install Sections
+; Empty section required for the installer to compile as an uninstaller
+Section ""
+SectionEnd
+
+################################################################################
+# Uninstall Sections
+
+/**
+ * Deletes the registry keys for a protocol handler but only if those registry
+ * keys were pointed to the installation being uninstalled.
+ * Does this with both the HKLM and the HKCU registry entries.
+ *
+ * @param _PROTOCOL
+ * The protocol to delete the registry keys for
+ */
+!macro DeleteProtocolRegistryIfSetToInstallation _PROTOCOL
+ Push $0
+ Push $1
+ ; Check if there is a protocol handler registered by fetching the DefaultIcon value
+ ; in the registry.
+ ; If there is something registered for the icon, it will be the path to the executable,
+ ; plus a comma and a number for the id of the resource for the icon.
+ ; Use StrCpy with -2 to remove the comma and the resource id so that
+ ; the whole path to the executable can be compared against what's being
+ ; uninstalled.
+
+ ; Do all of that twice, once for the local machine and once for the current user
+
+ ; Remove protocol handlers
+ ClearErrors
+ ${un.GetLongPath} "$INSTDIR\${FileMainEXE}" $1
+ ReadRegStr $0 HKLM "Software\Classes\${_PROTOCOL}\DefaultIcon" ""
+ ${If} $0 != ""
+ StrCpy $0 $0 -2
+ ${If} $0 == $1
+ DeleteRegKey HKLM "Software\Classes\${_PROTOCOL}"
+ ${EndIf}
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Classes\${_PROTOCOL}\DefaultIcon" ""
+ ${If} $0 != ""
+ StrCpy $0 $0 -2
+ ${If} $0 == $1
+ DeleteRegKey HKCU "Software\Classes\${_PROTOCOL}"
+ ${EndIf}
+ ${EndIf}
+
+ ClearErrors
+
+ Pop $0
+ Pop $1
+!macroend
+!define DeleteProtocolRegistryIfSetToInstallation '!insertmacro DeleteProtocolRegistryIfSetToInstallation'
+
+Section "Uninstall"
+ SetDetailsPrint textonly
+ DetailPrint $(STATUS_UNINSTALL_MAIN)
+ SetDetailsPrint none
+
+ ; Some system cleanup is most easily handled when XPCOM functionality is
+ ; available - e.g. removing notifications from Window's Action Center. We
+ ; handle this in the `uninstall` background task.
+ ;
+ ; Return value is saved to an unused variable to prevent the the error flag
+ ; from being set.
+ Var /GLOBAL UnusedExecCatchReturn
+ ExecWait '"$INSTDIR\${FileMainEXE}" --backgroundtask uninstall' $UnusedExecCatchReturn
+
+ ; Uninstall the default browser agent scheduled task and all other scheduled
+ ; tasks registered by Firefox.
+ ; This also removes the registry entries that the WDBA creates.
+ ; One of the scheduled tasks that this will remove is the Background Update
+ ; Task. Ideally, this will eventually be changed so that it doesn't rely on
+ ; the WDBA. See Bug 1710143.
+ ExecWait '"$INSTDIR\default-browser-agent.exe" uninstall $AppUserModelID'
+ ${RemoveDefaultBrowserAgentShortcut}
+
+ ; Delete the app exe to prevent launching the app while we are uninstalling.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ; If the user closed the application it can take several seconds for it to
+ ; shut down completely. If the application is being used by another user we
+ ; can still delete the files when the system is restarted.
+ Sleep 5000
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ ${EndIf}
+
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanPrefs} "Software\Mozilla\${AppName}"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+
+ ${If} "$AppUserModelID" != ""
+ ; Unregister resources associated with taskbar jump lists.
+ ApplicationID::UninstallJumpLists "$AppUserModelID"
+ ; Remove the update sync manager's multi-instance lock file
+ Call un.GetCommonDirectory
+ Pop $0
+ Delete /REBOOTOK "$0\UpdateLock-$AppUserModelID"
+ ${EndIf}
+
+ ${If} "$AppUserModelIDPrivate" != ""
+ ApplicationID::UninstallJumpLists "$AppUserModelIDPrivate"
+ ${EndIf}
+
+ ; Clean up old maintenance service logs
+ ${un.CleanMaintenanceServiceLogs} "Mozilla\Firefox"
+
+ ; Remove any app model id's stored in the registry for this install path
+ DeleteRegValue HKCU "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to HKLM
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+ ${un.SetAppLSPCategories}
+ ${EndIf}
+
+ ${un.RegCleanAppHandler} "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanAppHandler} "FirefoxPDF-$AppUserModelID"
+ ${un.RegCleanAppHandler} "FirefoxURL-$AppUserModelID"
+ ${un.RegCleanProtocolHandler} "http"
+ ${un.RegCleanProtocolHandler} "https"
+ ${un.RegCleanProtocolHandler} "mailto"
+ ${un.RegCleanFileHandler} ".htm" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".html" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".shtml" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".xht" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".xhtml" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".oga" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".ogg" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".ogv" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".webm" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".svg" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".webp" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".avif" "FirefoxHTML-$AppUserModelID"
+
+ ${un.RegCleanFileHandler} ".pdf" "FirefoxPDF-$AppUserModelID"
+
+ SetShellVarContext all ; Set SHCTX to HKLM
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${If} $R9 == "false"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${EndIf}
+
+ DeleteRegKey HKLM "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID"
+
+ DeleteRegKey HKCU "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID"
+
+ ; Clean up "launch on login" registry key for this installation.
+ DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Mozilla-${AppName}-$AppUserModelID"
+
+ ; Remove dual browser extension protocol handlers
+ ${DeleteProtocolRegistryIfSetToInstallation} "firefox"
+ ${DeleteProtocolRegistryIfSetToInstallation} "firefox-private"
+
+ ; Remove old protocol handler and StartMenuInternet keys without install path
+ ; hashes, but only if they're for this installation. We've never supported
+ ; bare FirefoxPDF.
+ ReadRegStr $0 HKLM "Software\Classes\FirefoxHTML\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKLM "Software\Classes\FirefoxHTML"
+ DeleteRegKey HKLM "Software\Classes\FirefoxURL"
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ DeleteRegKey HKLM "Software\Clients\StartMenuInternet\$R9"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "$R9"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegName}"
+ ${EndIf}
+ ReadRegStr $0 HKCU "Software\Classes\FirefoxHTML\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKCU "Software\Classes\FirefoxHTML"
+ DeleteRegKey HKCU "Software\Classes\FirefoxURL"
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ DeleteRegKey HKCU "Software\Clients\StartMenuInternet\$R9"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "$R9"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "${AppRegName}"
+ ${EndIf}
+
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
+ ${If} $R9 == "false"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\${FileMainEXE}"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Classes\MIME\Database\Content Type\application/x-xpinstall;app=firefox"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ ${Else}
+ ReadRegStr $R1 HKLM "$0" ""
+ ${un.RemoveQuotesFromPath} "$R1" $R1
+ ${un.GetParent} "$R1" $R1
+ ${If} "$INSTDIR" == "$R1"
+ WriteRegStr HKLM "$0" "" "$R9"
+ ${un.GetParent} "$R9" $R1
+ WriteRegStr HKLM "$0" "Path" "$R1"
+ ${EndIf}
+ ${EndIf}
+
+ ; Remove our HKCR/Applications key, if it's for this installation.
+ ReadRegStr $0 HKLM "Software\Classes\Applications\${FileMainEXE}\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKLM "Software\Classes\Applications\${FileMainEXE}"
+ ${EndIf}
+ ReadRegStr $0 HKCU "Software\Classes\Applications\${FileMainEXE}\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKCU "Software\Classes\Applications\${FileMainEXE}"
+ ${EndIf}
+
+ ; Remove directories and files we always control before parsing the uninstall
+ ; log so empty directories can be removed.
+ ${If} ${FileExists} "$INSTDIR\updates"
+ RmDir /r /REBOOTOK "$INSTDIR\updates"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\updated"
+ RmDir /r /REBOOTOK "$INSTDIR\updated"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults\shortcuts"
+ RmDir /r /REBOOTOK "$INSTDIR\defaults\shortcuts"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\distribution"
+ RmDir /r /REBOOTOK "$INSTDIR\distribution"
+ ${EndIf}
+
+ ; Remove files that may be left behind by the application in the
+ ; VirtualStore directory.
+ ${un.CleanVirtualStore}
+
+ ; Only unregister the dll if the registration points to this installation
+ ReadRegStr $R1 HKCR "CLSID\{0D68D6D0-D93D-4D08-A30D-F00DD1F45B24}\InProcServer32" ""
+ ${If} "$INSTDIR\AccessibleMarshal.dll" == "$R1"
+ ${UnregisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+ ${EndIf}
+
+ ${If} ${FileExists} "$INSTDIR\AccessibleHandler.dll"
+ ${UnregisterDLL} "$INSTDIR\AccessibleHandler.dll"
+ ${EndIf}
+
+ ; Remove the Windows Reporter Module entry
+ DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\RuntimeExceptionHelperModules" "$INSTDIR\mozwer.dll"
+
+!ifdef MOZ_LAUNCHER_PROCESS
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Launcher"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Browser"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Image"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Telemetry"
+!endif
+
+ ; Remove Toast Notification registration.
+ ; Find any GUID used for this installation.
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "DisplayName"
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "IconUri"
+ DeleteRegKey HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID"
+ ${If} "$0" != ""
+ DeleteRegValue HKLM "Software\Classes\AppID\$0" "DllSurrogate"
+ DeleteRegKey HKLM "Software\Classes\AppID\$0"
+ DeleteRegValue HKLM "Software\Classes\CLSID\$0" "AppID"
+ DeleteRegValue HKLM "Software\Classes\CLSID\$0\InProcServer32" ""
+ DeleteRegKey HKLM "Software\Classes\CLSID\$0\InProcServer32"
+ DeleteRegKey HKLM "Software\Classes\CLSID\$0"
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "DisplayName"
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "IconUri"
+ DeleteRegKey HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID"
+ ${If} "$0" != ""
+ DeleteRegValue HKCU "Software\Classes\AppID\$0" "DllSurrogate"
+ DeleteRegKey HKCU "Software\Classes\AppID\$0"
+ DeleteRegValue HKCU "Software\Classes\CLSID\$0" "AppID"
+ DeleteRegValue HKCU "Software\Classes\CLSID\$0\InProcServer32" ""
+ DeleteRegKey HKCU "Software\Classes\CLSID\$0\InProcServer32"
+ DeleteRegKey HKCU "Software\Classes\CLSID\$0"
+ ${EndIf}
+
+ ${un.RemovePrecompleteEntries} "false"
+
+ ${If} ${FileExists} "$INSTDIR\defaults\pref\channel-prefs.js"
+ Delete /REBOOTOK "$INSTDIR\defaults\pref\channel-prefs.js"
+ ${EndIf}
+ RmDir "$INSTDIR\defaults\pref"
+ RmDir "$INSTDIR\defaults"
+ ${If} ${FileExists} "$INSTDIR\uninstall"
+ ; Remove the uninstall directory that we control
+ RmDir /r /REBOOTOK "$INSTDIR\uninstall"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\install.log"
+ Delete /REBOOTOK "$INSTDIR\install.log"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\update-settings.ini"
+ Delete /REBOOTOK "$INSTDIR\update-settings.ini"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\installation_telemetry.json"
+ Delete /REBOOTOK "$INSTDIR\installation_telemetry.json"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\postSigningData"
+ Delete /REBOOTOK "$INSTDIR\postSigningData"
+ ${EndIf}
+
+ ; Explicitly remove empty webapprt dir in case it exists (bug 757978).
+ RmDir "$INSTDIR\webapprt\components"
+ RmDir "$INSTDIR\webapprt"
+
+ ; Remove the installation directory if it is empty
+ RmDir "$INSTDIR"
+
+ ; If firefox.exe was successfully deleted yet we still need to restart to
+ ; remove other files create a dummy firefox.exe.moz-delete to prevent the
+ ; installer from allowing an install without restart when it is required
+ ; to complete an uninstall.
+ ${If} ${RebootFlag}
+ ; Admin is required to delete files on reboot so only add the moz-delete if
+ ; the user is an admin. After calling UAC::IsAdmin $0 will equal 1 if the
+ ; user is an admin.
+ UAC::IsAdmin
+ ${If} "$0" == "1"
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileOpen $0 "$INSTDIR\${FileMainEXE}.moz-delete" w
+ FileWrite $0 "Will be deleted on restart"
+ Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileClose $0
+ ${EndUnless}
+ ${EndIf}
+ ${EndIf}
+
+ ; Refresh desktop icons otherwise the start menu internet item won't be
+ ; removed and other ugly things will happen like recreation of the app's
+ ; clients registry key by the OS under some conditions.
+ ${RefreshShellIcons}
+
+ ; Users who uninstall then reinstall expecting Firefox to use a clean profile
+ ; may be surprised during first-run. This key is checked during startup of Firefox and
+ ; subsequently deleted after checking. If the value is found during startup
+ ; the browser will offer to Reset Firefox. We use the UpdateChannel to match
+ ; uninstalls of Firefox-release with reinstalls of Firefox-release, for example.
+ WriteRegStr HKCU "Software\Mozilla\Firefox" "Uninstalled-${UpdateChannel}" "True"
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ; Get the path the allowed cert is at and remove it
+ ; Keep this block of code last since it modfies the reg view
+ ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
+ Pop $MaintCertKey
+ ${If} $MaintCertKey != ""
+ ; Always use the 64bit registry for certs on 64bit systems.
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+ DeleteRegKey HKLM "$MaintCertKey"
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+ ${EndIf}
+ Call un.UninstallServiceIfNotUsed
+!endif
+
+!ifdef MOZ_BITS_DOWNLOAD
+ BitsUtils::CancelBitsJobsByName "MozillaUpdate $AppUserModelID"
+ Pop $0
+!endif
+
+ ${un.IsFirewallSvcRunning}
+ Pop $0
+ ${If} "$0" == "true"
+ liteFirewallW::RemoveRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
+ ${EndIf}
+SectionEnd
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale. Using
+; " " for BrandingText will hide the "Nullsoft Install System..." branding.
+BrandingText " "
+
+################################################################################
+# Page pre, show, and leave functions
+
+Function un.preWelcome
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-wizard.bmp"
+ Delete "$PLUGINSDIR\modern-wizard.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
+ ${EndIf}
+!ifdef MOZ_BITS_DOWNLOAD
+ BitsUtils::StartBitsServiceBackground
+!endif
+
+ ; We don't want the header bitmap showing on the welcome page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Note: INI strings added here (rather than overwriting an existing value)
+ ; should be removed in un.leaveWelcome, since ioSpecial.ini is reused
+ ; for the Finish page.
+
+ ; Replace title and body text
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 2" Text "$(UN_REFRESH_PAGE_TITLE)"
+ ; Convert to translate newlines, this includes $PLUGINSDIR internally.
+ !insertmacro INSTALLOPTIONS_WRITE_UNCONVERT "ioSpecial.ini" "Field 3" Text "$(UN_REFRESH_PAGE_EXPLANATION)"
+
+ ; Make room for the link and button
+ StrCpy $0 "148"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 3" Bottom $0
+
+ ; Show the help link
+ IntOp $1 $0 + 14
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Type "link"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Text "$(UN_REFRESH_LEARN_MORE)"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Left "120"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Top $0
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Right "315"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Bottom $1
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Flags "NOTIFY"
+
+ ; Show the refresh button.
+ IntOp $2 $1 + 14
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Type "button"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Text "$(UN_REFRESH_BUTTON)"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Left "120"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Top $1
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Right "240"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Bottom $2
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Flags "NOTIFY"
+
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Settings" NumFields 5
+ ${EndIf}
+FunctionEnd
+
+Function un.ShowWelcome
+ ; The welcome and finish pages don't get the correct colors for their labels
+ ; like the other pages do, presumably because they're built by filling in an
+ ; InstallOptions .ini file instead of from a dialog resource like the others.
+ ; Field 2 is the header and Field 3 is the body text.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Field 4 is the profile refresh help link
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 4" "HWND"
+ SetCtlColors $0 SYSCLR:HOTLIGHT SYSCLR:WINDOW
+ ${EndIf}
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${un.ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+FunctionEnd
+
+Function un.leaveWelcome
+ StrCpy $RefreshRequested "0"
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Settings" "State"
+ ${If} $0 == "5"
+ ; Refresh button
+ StrCpy $RefreshRequested "1"
+ ${ElseIf} $0 == "4"
+ ; Launch refresh help link, stay on this page
+ Call un.LaunchRefreshHelpPage
+ Abort
+ ${EndIf}
+ ${EndIf}
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)"
+
+ ; If we already found a window once and we're checking again, wait for an
+ ; additional five seconds for the app to close.
+ ${If} "$TmpVal" == "FoundAppWindow"
+ Sleep 5000
+ ${EndIf}
+
+ ${PushFilesToCheck}
+
+ ${un.CheckForFilesInUse} $TmpVal
+
+ Banner::destroy
+
+ ; If there are files in use $TmpVal will be "true"
+ ${If} "$TmpVal" == "true"
+ ; If it finds a window of the right class, then ManualCloseAppPrompt will
+ ; abort leaving the value of $TmpVal set to "FoundAppWindow".
+ StrCpy $TmpVal "FoundAppWindow"
+
+ ${If} $RefreshRequested == "1"
+ ${un.ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_REFRESH)"
+ ${un.ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_REFRESH)"
+ ${Else}
+ ${un.ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ ${un.ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ ${EndIf}
+ ; If the message window is not found set $TmpVal to "true" so the restart
+ ; required message is displayed.
+ ; In the case of a refresh request the restart required message will not be displayed;
+ ; we're not trying to change the installation, so files in use only matter if the
+ ; window is shown.
+ StrCpy $TmpVal "true"
+ ${EndIf}
+ ${EndIf}
+
+ ${If} $RefreshRequested == "1"
+ Call un.LaunchAppForRefresh
+ Quit
+ ${EndIf}
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Remove the custom controls.
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Settings" NumFields 3
+ DeleteIniSec "$PLUGINSDIR\ioSpecial.ini" "Field 4"
+ DeleteIniSec "$PLUGINSDIR\ioSpecial.ini" "Field 5"
+ ${EndIf}
+
+ ; Bring back the header bitmap for the next pages.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_SHOW}
+FunctionEnd
+
+Function un.preConfirm
+ ; The header on the wizard pages doesn't get the correct text
+ ; color by default for some reason, even though the other controls do.
+ GetDlgItem $0 $HWNDPARENT 1037
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ ; Hide unused subheader (to avoid overlapping moved header)
+ GetDlgItem $0 $HWNDPARENT 1038
+ ShowWindow $0 ${SW_HIDE}
+
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-header.bmp"
+ Delete "$PLUGINSDIR\modern-header.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
+ ${EndIf}
+ ${un.ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
+
+ ; Setup the unconfirm.ini file for the Custom Uninstall Confirm Page
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "3"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Text "$(UN_CONFIRM_UNINSTALLED_FROM)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Type "text"
+ ; The contents of this control must be set as follows in the pre function
+ ; ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State ""
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Top "17"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" flags "READONLY"
+
+ ${If} "$TmpVal" == "true"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Text "$(SUMMARY_REBOOT_REQUIRED_UNINSTALL)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "35"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "45"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "3"
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(UN_CONFIRM_PAGE_TITLE)" ""
+
+ ; The Summary custom page has a textbox that will automatically receive
+ ; focus. This sets the focus to the Install button instead.
+ !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "unconfirm.ini"
+ GetDlgItem $0 $HWNDPARENT 1
+ System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i"
+ ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ !insertmacro MUI_INSTALLOPTIONS_SHOW
+FunctionEnd
+
+Function un.onUninstSuccess
+ ; Send a ping at un.onGUIEnd, to avoid freezing the GUI.
+ StrCpy $ShouldSendPing "1"
+
+ ${If} ${Silent}
+ ; If this is a silent uninstall then un.onGUIEnd doesn't run, so do it now.
+ Call un.SendUninstallPing
+ ${EndIf}
+FunctionEnd
+
+Function un.preFinish
+ ; Need to give the survey (readme) checkbox a few extra DU's of height
+ ; to accommodate a potentially multi-line label. If the reboot flag is set,
+ ; then we're not showing the survey checkbox and Field 4 is the "reboot now"
+ ; radio button; setting it to go from 90 to 120 (instead of 90 to 100) would
+ ; cover up Field 5 which is "reboot later", running from 110 to 120. For
+ ; whatever reason child windows get created at the bottom of the z-order, so
+ ; 4 overlaps 5.
+ ${IfNot} ${RebootFlag}
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Bottom "120"
+ ${EndIf}
+
+ ; We don't want the header bitmap showing on the finish page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+FunctionEnd
+
+Function un.ShowFinish
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${un.ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+
+ ; Either Fields 4 and 5 are the reboot option radio buttons, or Field 4 is
+ ; the survey checkbox and Field 5 doesn't exist. Either way, we need to
+ ; clear the theme from them before we can set their background colors.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 4" "HWND"
+ System::Call 'uxtheme::SetWindowTheme(i $0, w " ", w " ")'
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ClearErrors
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 5" "HWND"
+ ${IfNot} ${Errors}
+ System::Call 'uxtheme::SetWindowTheme(i $0, w " ", w " ")'
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ ${EndIf}
+FunctionEnd
+
+################################################################################
+# Initialization Functions
+
+Function .onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ ; We need this set up for most of the helper.exe operations.
+ ${UninstallOnInitCommon}
+FunctionEnd
+
+Function un.onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ StrCpy $LANGUAGE 0
+ StrCpy $ShouldOpenSurvey "0"
+
+ ${un.UninstallUnOnInitCommon}
+
+ ; setup the application model id registration value
+ ${un.InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ; Find a default profile for this install.
+ SetShellVarContext current
+ ${un.FindInstallSpecificProfile}
+ Pop $1
+ GetFullPathName $1 $1
+
+ ; If there is an existing default profile, offer profile refresh.
+ StrCpy $ShouldPromptForRefresh "0"
+ StrCpy $RefreshRequested "0"
+ ${If} $1 != ""
+ StrCpy $ShouldPromptForRefresh "1"
+ ${EndIf}
+
+ ; Load version info for uninstall ping (sent after uninstall is complete)
+ ReadINIStr $InstalledVersion "$INSTDIR\application.ini" "App" "Version"
+ ReadINIStr $InstalledBuildID "$INSTDIR\application.ini" "App" "BuildID"
+
+ !insertmacro InitInstallOptionsFile "unconfirm.ini"
+FunctionEnd
+
+Function un.GUIInit
+ ; Move header text down, roughly vertically centered in the header.
+ ; Even if we're not changing the X, we can't set Y without also setting X.
+ ; Child window positions have to be set in client coordinates, and these are
+ ; left-to-right mirrored for RTL.
+ GetDlgItem $0 $HWNDPARENT 1037
+ ; Get current rect in screen coordinates
+ System::Call "*(i 0, i 0, i 0, i 0) i .r2"
+ System::Call "user32::GetWindowRect(p $0, p $2)"
+ ; Convert screen coordinates to client coordinates (handles RTL mirroring)
+ System::Call "user32::MapWindowPoints(p 0, p $HWNDPARENT, p $2, i 2)"
+ System::Call "*$2(i . r3, i . r4, i, i)"
+ System::Free $2
+ ; Move down
+ ${DialogUnitsToPixels} 8 Y $1
+ IntOp $4 $4 + $1
+ ; Set position, 0x0015 is SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE
+ System::Call "user32::SetWindowPos(p $0, p 0, i $3, i $4, i 0, i 0, i 0x0015)"
+FunctionEnd
+
+Function .onGUIEnd
+ ${OnEndCommon}
+FunctionEnd
+
+Function un.onGUIEnd
+ ${un.OnEndCommon}
+
+ ${If} $ShouldOpenSurvey == "1"
+ ; Though these values are sometimes incorrect due to bug 444664 it happens
+ ; so rarely it isn't worth working around it by reading the registry values.
+ ${WinVerGetMajor} $0
+ ${WinVerGetMinor} $1
+ ${WinVerGetBuild} $2
+ ${WinVerGetServicePackLevel} $3
+ StrCpy $R1 "${URLUninstallSurvey}$0.$1.$2.$3"
+
+ ; We can't just open the URL normally because we are most likely running
+ ; elevated without an unelevated process to redirect through, and we're
+ ; not going to go around starting elevated web browsers. But to start an
+ ; unelevated process directly from here we need a pretty nasty hack; see
+ ; the ExecInExplorer plugin code itself for the details.
+ ; If we were the default browser and we've now been uninstalled, we need
+ ; to take steps to make sure the user doesn't see an "open with" dialog;
+ ; they're helping us out by answering this survey, they don't need more
+ ; friction.
+ ; Windows 10 always seems to just clear the default browser, so for it
+ ; we'll manually invoke Edge using Edge's custom URI scheme.
+ ExecInExplorer::Exec "microsoft-edge:$R1"
+ ${EndIf}
+
+ ; Finally send the ping, there's no GUI to freeze in case it is slow.
+ ${If} $ShouldSendPing == "1"
+ Call un.SendUninstallPing
+ ${EndIf}
+FunctionEnd
diff --git a/browser/installer/windows/nsis/updater_append.ini b/browser/installer/windows/nsis/updater_append.ini
new file mode 100644
index 0000000000..af7742c12c
--- /dev/null
+++ b/browser/installer/windows/nsis/updater_append.ini
@@ -0,0 +1,12 @@
+
+; IMPORTANT: This file should always start with a newline in case a locale
+; provided updater.ini does not end with a newline.
+; Application to launch after an update has been successfully applied. This
+; must be in the same directory or a sub-directory of the directory of the
+; application executable that initiated the software update.
+[PostUpdateWin]
+; ExeRelPath is the path to the PostUpdateWin executable relative to the
+; application executable.
+ExeRelPath=uninstall\helper.exe
+; ExeArg is the argument to pass to the PostUpdateWin exe
+ExeArg=/PostUpdate
diff --git a/browser/installer/windows/stub.tag b/browser/installer/windows/stub.tag
new file mode 100644
index 0000000000..f32bef36e8
--- /dev/null
+++ b/browser/installer/windows/stub.tag
@@ -0,0 +1,4 @@
+;!@Install@!UTF-8!
+Title="Mozilla Firefox"
+RunProgram="setup-stub.exe"
+;!@InstallEnd@! \ No newline at end of file