diff options
Diffstat (limited to 'browser/installer/windows/nsis')
-rw-r--r-- | browser/installer/windows/nsis/content/installing.html | 43 | ||||
-rw-r--r-- | browser/installer/windows/nsis/content/installing.js | 58 | ||||
-rw-r--r-- | browser/installer/windows/nsis/content/profile_cleanup.html | 42 | ||||
-rw-r--r-- | browser/installer/windows/nsis/content/profile_cleanup.js | 30 | ||||
-rw-r--r-- | browser/installer/windows/nsis/content/stub_common.css | 37 | ||||
-rw-r--r-- | browser/installer/windows/nsis/content/stub_common.js | 23 | ||||
-rw-r--r-- | browser/installer/windows/nsis/defines.nsi.in | 156 | ||||
-rw-r--r-- | browser/installer/windows/nsis/extensionsLocale.nsh | 20 | ||||
-rwxr-xr-x | browser/installer/windows/nsis/installer.nsi | 2057 | ||||
-rw-r--r-- | browser/installer/windows/nsis/maintenanceservice_installer.nsi | 343 | ||||
-rwxr-xr-x | browser/installer/windows/nsis/shared.nsh | 1808 | ||||
-rw-r--r-- | browser/installer/windows/nsis/stub.nsi | 1772 | ||||
-rwxr-xr-x | browser/installer/windows/nsis/uninstaller.nsi | 1133 | ||||
-rw-r--r-- | browser/installer/windows/nsis/updater_append.ini | 12 |
14 files changed, 7534 insertions, 0 deletions
diff --git a/browser/installer/windows/nsis/content/installing.html b/browser/installer/windows/nsis/content/installing.html new file mode 100644 index 0000000000..e7b4450065 --- /dev/null +++ b/browser/installer/windows/nsis/content/installing.html @@ -0,0 +1,43 @@ +<!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..869c76c324 --- /dev/null +++ b/browser/installer/windows/nsis/content/installing.js @@ -0,0 +1,58 @@ +// 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..4f20aaecba --- /dev/null +++ b/browser/installer/windows/nsis/content/profile_cleanup.html @@ -0,0 +1,42 @@ +<!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..016b368b05 --- /dev/null +++ b/browser/installer/windows/nsis/content/profile_cleanup.js @@ -0,0 +1,30 @@ +// 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..bcdf809b61 --- /dev/null +++ b/browser/installer/windows/nsis/content/stub_common.js @@ -0,0 +1,23 @@ +// 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..fd39d599f5 --- /dev/null +++ b/browser/installer/windows/nsis/defines.nsi.in @@ -0,0 +1,156 @@ +#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 + +!define AccessibleHandlerCLSID "@MOZ_HANDLER_CLSID@" + +#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 7 x64" +#endif +#else +!define MinSupportedVer "Microsoft Windows 7" +!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..40386f8915 --- /dev/null +++ b/browser/installer/windows/nsis/installer.nsi @@ -0,0 +1,2057 @@ +# 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 +# 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 AddQuickLaunchSC +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 CleanUpdateDirectories +!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" + + ; Remove the updates directory + ${CleanUpdateDirectories} "Mozilla\Firefox" "Mozilla\updates" + + ${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 + + ${RegisterDLL} "$INSTDIR\AccessibleHandler.dll" + ${If} ${Errors} + ${LogMsg} "** ERROR Registering: $INSTDIR\AccessibleHandler.dll **" + ${Else} + ${LogUninstall} "DLLReg: \AccessibleHandler.dll" + ${LogMsg} "Registered: $INSTDIR\AccessibleHandler.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 + + ; 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 Quick Launch shortcut (1 = create, 0 = don't create) + ${If} $AddQuickLaunchSC == "" + ; Don't install the quick launch shortcut on Windows 7 + ${If} ${AtLeastWin7} + StrCpy $AddQuickLaunchSC "0" + ${Else} + StrCpy $AddQuickLaunchSC "1" + ${EndIf} + ${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 == "" + StrCpy $AddTaskbarSC "1" + ${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" + + ; For pre win8, the following keys should only be set if we can write to HKLM. + ; For post win8, 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" + ${ElseIf} ${AtLeastWin8} + ; 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 + + ${If} ${AtLeastWin10} + ${WriteToastNotificationRegistration} $TmpVal + ${EndIf} + + ; 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. + ${If} ${AtLeastWin8} + SetShellVarContext all + ${TouchStartMenuShortcut} + SetShellVarContext current + ${TouchStartMenuShortcut} + ${If} $TmpVal == "HKLM" + SetShellVarContext all + ${ElseIf} $TmpVal == "HKCU" + SetShellVarContext current + ${EndIf} + ${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} + + ; If elevated the Quick Launch shortcut must be added from the unelevated + ; original process. + ${If} $AddQuickLaunchSC == 1 + ${Unless} ${AtLeastWin7} + ClearErrors + ${GetParameters} $0 + ${GetOptions} "$0" "/UAC:" $0 + ${If} ${Errors} + Call AddQuickLaunchShortcut + ${LogMsg} "Added Shortcut: $QUICKLAUNCH\${BrandShortName}.lnk" + ${Else} + ; It is not possible to add a log entry from the unelevated process so + ; add the log entry without the path since there is no simple way to + ; know the correct full path. + ${LogMsg} "Added Quick Launch Shortcut: ${BrandShortName}.lnk" + GetFunctionAddress $0 AddQuickLaunchShortcut + UAC::ExecCodeSegment $0 + ${EndIf} + ${EndUnless} + ${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 +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} + + ${Unless} ${Silent} + ClearErrors + ${MUI_INSTALLOPTIONS_READ} $0 "summary.ini" "Field 4" "State" + ${If} "$0" == "1" + StrCpy $SetAsDefault true + ; For data migration in the app, we want to know what the default browser + ; value was before we changed it. To do so, we read it here and store it + ; in our own registry key. + StrCpy $0 "" + AppAssocReg::QueryCurrentDefault "http" "protocol" "effective" + Pop $1 + ; If the method hasn't failed, $1 will contain the progid. Check: + ${If} "$1" != "method failed" + ${AndIf} "$1" != "method not available" + ; Read the actual command from the progid + ReadRegStr $0 HKCR "$1\shell\open\command" "" + ${EndIf} + ; If using the App Association Registry didn't happen or failed, fall back + ; to the effective http default: + ${If} "$0" == "" + ReadRegStr $0 HKCR "http\shell\open\command" "" + ${EndIf} + ; If we have something other than empty string now, write the value. + ${If} "$0" != "" + ClearErrors + WriteRegStr HKCU "Software\Mozilla\Firefox" "OldDefaultBrowserCommand" "$0" + ${EndIf} + + ${LogHeader} "Setting as the default browser" + ; 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 + ClearErrors + ${GetParameters} $0 + ${GetOptions} "$0" "/UAC:" $0 + ${If} ${Errors} + Call SetAsDefaultAppUserHKCU + ${Else} + GetFunctionAddress $0 SetAsDefaultAppUserHKCU + UAC::ExecCodeSegment $0 + ${EndIf} + ${ElseIfNot} ${Errors} + StrCpy $SetAsDefault false + ${LogHeader} "Writing default-browser opt-out" + ClearErrors + WriteRegStr HKCU "Software\Mozilla\Firefox" "DefaultBrowserOptOut" "True" + ${If} ${Errors} + ${LogMsg} "Error writing default-browser opt-out" + ${EndIf} + ${EndIf} + ${EndUnless} + + ; 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 AddQuickLaunchShortcut + CreateShortCut "$QUICKLAUNCH\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}" + ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk" + ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandShortName}.lnk" \ + "$INSTDIR" + ${EndIf} +FunctionEnd + +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"' + + ${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" + ${MUI_INSTALLOPTIONS_READ} $AddTaskbarSC "shortcuts.ini" "Field 4" "State" + + ${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} + + + ; Remove the "Field 4" ini section in case the user hits back and changes the + ; installation directory which could change whether the make default checkbox + ; should be displayed. + DeleteINISec "$PLUGINSDIR\summary.ini" "Field 4" + + ; Check if it is possible to write to HKLM + ClearErrors + WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test" + ${Unless} ${Errors} + DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" + ; Check if Firefox is the http handler for this user. + SetShellVarContext current ; Set SHCTX to the current user + ${IsHandlerForInstallDir} "http" $R9 + ; If Firefox isn't the http handler for this user show the option to set + ; Firefox as the default browser. + ${If} "$R9" != "true" + ${AndIf} ${AtMostWin2008R2} + WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "4" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Type "checkbox" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Text "$(SUMMARY_TAKE_DEFAULTS)" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Left "0" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Right "-1" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" State "1" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "32" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "53" + ${EndIf} + ${EndUnless} + + ${If} "$TmpVal" == "true" + ; If there is already a Type entry in the "Field 4" section with a value of + ; checkbox then the set as the default browser checkbox is displayed and + ; this text must be moved below it. + ReadINIStr $0 "$PLUGINSDIR\summary.ini" "Field 4" "Type" + ${If} "$0" == "checkbox" + StrCpy $0 "5" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Top "53" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Bottom "68" + ${Else} + StrCpy $0 "4" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Top "35" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Bottom "50" + ${EndIf} + WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "$0" + + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Type "label" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Text "$(SUMMARY_REBOOT_REQUIRED_INSTALL)" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Left "0" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Right "-1" + ${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 NT 6.0 (Vista/Server 2008) and lower are not supported. + ${Unless} ${AtLeastWin7} + ${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} + ${OrIfNot} ${AtLeastWin10} + 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" + + WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "4" + + 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" + + 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" + + ; 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..ff87ef0839 --- /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} ${AtLeastWin7} + 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..dd85dfed8e --- /dev/null +++ b/browser/installer/windows/nsis/shared.nsh @@ -0,0 +1,1808 @@ +# 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/. + +!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} + + ; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details). + ; When we enabled this feature for Windows 10 & 11 we decided _not_ to pin + ; during an update (even once) because we already offered to do when the + ; the user originally installed, and we don't want to go against their + ; explicit wishes. + ; For Windows 7 and 8, we've been doing this ~forever, and those users may + ; not have experienced the onboarding offer to pin to taskbar, so we're + ; leaving it enabled there. + ${If} ${AtMostWin2012R2} + ${MigrateTaskBarShortcut} "$AddTaskbarSC" + ${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} + ${If} ${AtLeastWin8} + ${TouchStartMenuShortcut} + ${EndIf} + Call FixShortcutAppModelIDs + SetShellVarContext current + ${UpdateShortcutsBranding} + ${If} ${AtLeastWin8} + ${TouchStartMenuShortcut} + ${EndIf} + 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 AccessibleHandler.dll with COM (this requires write access to HKLM) + ${RegisterAccessibleHandler} + + ; 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 + +!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 + + ${If} ${AtLeastWin10} + ${WriteToastNotificationRegistration} $TmpVal + ${EndIf} + +; 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 + ${If} ${AtLeastWin8} + WriteRegDWORD HKCU "$R1" "IconsVisible" 0 + ${EndIf} + + 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 + ${If} ${AtLeastWin8} + WriteRegDWORD HKCU "$R1" "IconsVisible" 1 + ${EndIf} + + 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} ${AtLeastWin7} + ${AndIf} "$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} ${AtLeastWin7} + ${AndIf} "$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} ${AtLeastWin7} + ${AndIf} "$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} ${AtLeastWin7} + ${AndIf} "$AppUserModelID" != "" + ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true" + ${EndIf} + ${EndIf} + ${EndUnless} + ${EndIf} + ${EndUnless} + + ; Windows 7 doesn't use the QuickLaunch directory + ${Unless} ${AtLeastWin7} + ${AndUnless} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk" + CreateShortCut "$QUICKLAUNCH\${BrandShortName}.lnk" \ + "$INSTDIR\${FileMainEXE}" + ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk" + ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandShortName}.lnk" \ + "$INSTDIR" + ${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 RegisterAccessibleHandler + ${RegisterDLL} "$INSTDIR\AccessibleHandler.dll" +!macroend +!define RegisterAccessibleHandler "!insertmacro RegisterAccessibleHandler" + +!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" +!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} ${AtLeastWin7} + ${If} "${SHOULD_PIN}" == "1" + ${PinToTaskBar} + ${EndIf} + ${EndIf} + ${EndIf} + ${EndIf} +!macroend +!define MigrateTaskBarShortcut "!insertmacro MigrateTaskBarShortcut" + +; 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 + ${If} ${AtLeastWin7} + 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} ${AtMostWin2012R2} + ; Pin the shortcut to the TaskBar. 5386 is the shell32.dll + ; resource id for the "Pin to Taskbar" string. + InvokeShellVerb::DoIt "$SMPROGRAMS" "$1" "5386" + ${ElseIf} ${AtMostWaaS} 1809 + ; In Windows 10 the "Pin to Taskbar" resource was removed, so we + ; can't access the verb that way anymore. We have a 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 the Windows 10 1903 and up (and Windows 11) the above no + ; longer works. We have yet another method for these versions + ; which is detailed in the PinToTaskbar plugin code. + PinToTaskbar::Pin "$SMPROGRAMS\$1" + ${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} + ${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 "AccessibleHandler.dll" + Push "AccessibleMarshal.dll" + Push "IA2Marshal.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} + ${OrIf} ${AtMostWin2008R2} + 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} + ${OrIf} ${AtMostWin2008R2} + 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" + ; On Windows >= 8, 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. + ${If} ${AtLeastWin8} + Return + ${Else} + WriteRegStr HKCU "Software\Clients\StartMenuInternet" "" "$R9" + ${EndIf} + ${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. + ${If} ${AtLeastWin8} + ${SetStartMenuInternet} "HKCU" + ${FixShellIconHandler} "HKCU" + ${FixClassKeys} ; Does not use SHCTX + ${EndIf} + + ${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} ${AtLeastWin7} + ${AndIf} "$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 + ; On Win8, we want to avoid having a UAC prompt since we'll already have + ; another action for control panel default browser selection popping up + ; to the user. Win8 is the first OS where the start menu keys can be + ; added into HKCU. The call to SetAsDefaultAppUserHKCU will have already + ; set the HKCU keys for SetStartMenuInternet. + ${If} ${AtLeastWin8} + ; 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} + Return ; Nothing more needs to be done + ${EndIf} + + ; Before Win8, it is only possible to set this installation of the application + ; as the StartMenuInternet handler if it was added to the HKLM + ; StartMenuInternet registry keys. + ; http://support.microsoft.com/kb/297878 + + ; Check if this install location registered as a StartMenuInternet client + ClearErrors + ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\DefaultIcon" "" + ${If} ${Errors} + ${OrIf} ${AtMostWin2008R2} + ClearErrors + ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\DefaultIcon" "" + ${EndIf} + ${If} ${Errors} + ${StrFilter} "${FileMainEXE}" "+" "" "" $R9 + ClearErrors + ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" "" + ${If} ${Errors} + ${OrIf} ${AtMostWin2008R2} + ClearErrors + ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" "" + ${EndIf} + ${EndIf} + + ${Unless} ${Errors} + ${GetPathFromString} "$0" $0 + ${GetParent} "$0" $0 + ${If} ${FileExists} "$0" + ${GetLongPath} "$0" $0 + ${If} "$0" == "$INSTDIR" + ; 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} + Return ; Nothing more needs to be done + ${EndIf} + ${EndIf} + ${EndUnless} + + ; The code after ElevateUAC won't be executed when the user: + ; a) is a member of the administrators group (e.g. elevation is required) + ; b) is not a member of the administrators group and chooses to elevate + ${ElevateUAC} + + ${SetStartMenuInternet} "HKLM" + + SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM) + + ${FixClassKeys} ; Does not use SHCTX + ${FixShellIconHandler} "HKLM" + ${RemoveDeprecatedKeys} ; Does not use SHCTX + + ClearErrors + ${GetParameters} $0 + ${GetOptions} "$0" "/UAC:" $0 + ${If} ${Errors} + Call SetAsDefaultAppUserHKCU + ${Else} + 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..3384198e96 --- /dev/null +++ b/browser/installer/windows/nsis/stub.nsi @@ -0,0 +1,1772 @@ +# 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 CheckboxSetAsDefault +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 CanSetAsDefault +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 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 "v8" + +; 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 200 seconds with a 100 millisecond timer. +!define InstallCleanTotalSteps 2000 + +; Approximately 215 seconds with a 100 millisecond timer. +!define InstallPaveOverTotalSteps 2150 + +; 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 NT 6.0 (Vista/Server 2008) and lower are not supported. + ${Unless} ${AtLeastWin7} + ${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" + + ClearErrors + WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \ + "Write Test" + + ; Only display set as default when there is write access to HKLM and on Win7 + ; and below. + ${If} ${Errors} + ${OrIf} ${AtLeastWin8} + StrCpy $CanSetAsDefault "false" + ${Else} + DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" + StrCpy $CanSetAsDefault "true" + ${EndIf} + StrCpy $CheckboxSetAsDefault "0" + + ; 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" + ; Don't create the QuickLaunch or Taskbar shortcut from the launched installer + WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false" + + ; 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} + + ; 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} + + ${If} $CanSetAsDefault == "true" + ${If} $CheckboxSetAsDefault == "1" + StrCpy $R3 "2" + ${Else} + StrCpy $R3 "3" + ${EndIf} + ${Else} + ${If} ${AtLeastWin8} + StrCpy $R3 "1" + ${Else} + StrCpy $R3 "0" + ${EndIf} + ${EndIf} + +!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" + ; 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" \ + "$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..97b4ed11e8 --- /dev/null +++ b/browser/installer/windows/nsis/uninstaller.nsi @@ -0,0 +1,1133 @@ +# 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.CleanUpdateDirectories +!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 + +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 + + ; 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 Win7 taskbar jump lists. + ${If} ${AtLeastWin7} + ApplicationID::UninstallJumpLists "$AppUserModelID" + ${EndIf} + ; Remove the update sync manager's multi-instance lock file + Call un.GetCommonDirectory + Pop $0 + Delete /REBOOTOK "$0\UpdateLock-$AppUserModelID" + ${EndIf} + + ${If} "$AppUserModelIDPrivate" != "" + ${If} ${AtLeastWin7} + ApplicationID::UninstallJumpLists "$AppUserModelIDPrivate" + ${EndIf} + ${EndIf} + + ; Remove the updates directory + ${un.CleanUpdateDirectories} "Mozilla\Firefox" "Mozilla\updates" + + ; 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" + + ; 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} + + ; Only unregister the dll if the registration points to this installation + ReadRegStr $R1 HKCR "CLSID\${AccessibleHandlerCLSID}\InprocHandler32" "" + ${If} "$INSTDIR\AccessibleHandler.dll" == "$R1" + ${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. + ${If} ${AtLeastWin10} + ; 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} + ${EndIf} + + ; 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} + + ${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. Sometimes Windows 7 and 8 automatically switch the default to + ; IE, but it isn't reliable, so we'll manually invoke IE in that case. + ; Windows 10 always seems to just clear the default browser, so for it + ; we'll manually invoke Edge using Edge's custom URI scheme. + ${If} ${AtLeastWin10} + ExecInExplorer::Exec "microsoft-edge:$R1" + ${Else} + ExecInExplorer::Exec "iexplore.exe" /cmdargs "$R1" + ${EndIf} + ${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 |