summaryrefslogtreecommitdiffstats
path: root/browser/installer/windows/nsis/uninstaller.nsi
diff options
context:
space:
mode:
Diffstat (limited to 'browser/installer/windows/nsis/uninstaller.nsi')
-rwxr-xr-xbrowser/installer/windows/nsis/uninstaller.nsi1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi
new file mode 100755
index 0000000000..695beed25f
--- /dev/null
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -0,0 +1,1175 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Required Plugins:
+# AppAssocReg
+# http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# BitsUtils
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/BitsUtils
+# CityHash
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# HttpPostFile
+# http://searchfox.org/mozilla-central/source/other-licenses/nsis/Contrib/HttpPostFile
+# ShellLink
+# http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC
+# http://nsis.sourceforge.net/UAC_plug-in
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+; Attempt to elevate Standard Users in addition to users that
+; are a member of the Administrators group.
+!define NONADMIN_ELEVATE
+
+; prevents compiling of the reg write logging.
+!define NO_LOG
+
+!define MaintUninstallKey \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
+
+Var TmpVal
+Var MaintCertKey
+Var ShouldOpenSurvey
+Var ShouldSendPing
+Var InstalledVersion
+Var InstalledBuildID
+Var ShouldPromptForRefresh
+Var RefreshRequested
+; AddTaskbarSC is defined here in order to silence warnings from inside
+; MigrateTaskBarShortcut and is not intended to be used here.
+; See Bug 1329869 for more.
+Var AddTaskbarSC
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include InstallOptions.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetSize
+!insertmacro StrFilter
+!insertmacro WordReplace
+
+!insertmacro un.GetParent
+
+; The following includes are custom.
+!include branding.nsi
+!include defines.nsi
+!include common.nsh
+!include locales.nsi
+
+; This is named BrandShortName helper because we use this for software update
+; post update cleanup.
+VIAddVersionKey "FileDescription" "${BrandShortName} Helper"
+VIAddVersionKey "OriginalFilename" "helper.exe"
+
+!insertmacro AddDisabledDDEHandlerValues
+!insertmacro CleanVirtualStore
+!insertmacro ElevateUAC
+!insertmacro GetLongPath
+!insertmacro GetPathFromString
+!insertmacro InitHashAppModelId
+!insertmacro IsHandlerForInstallDir
+!insertmacro IsPinnedToTaskBar
+!insertmacro IsUserAdmin
+!insertmacro LogDesktopShortcut
+!insertmacro LogQuickLaunchShortcut
+!insertmacro LogStartMenuShortcut
+!insertmacro PinnedToStartMenuLnkCount
+!insertmacro RegCleanAppHandler
+!insertmacro RegCleanMain
+!insertmacro RegCleanUninstall
+!insertmacro SetAppLSPCategories
+!insertmacro SetBrandNameVars
+!insertmacro UpdateShortcutAppModelIDs
+!insertmacro UnloadUAC
+!insertmacro WriteRegDWORD2
+!insertmacro WriteRegStr2
+
+; This needs to be inserted after InitHashAppModelId because it uses
+; $AppUserModelID and the compiler can't handle using variables lexically before
+; they've been declared.
+!insertmacro GetInstallerRegistryPref
+
+!insertmacro un.ChangeMUIHeaderImage
+!insertmacro un.ChangeMUISidebarImage
+!insertmacro un.CheckForFilesInUse
+!insertmacro un.CleanMaintenanceServiceLogs
+!insertmacro un.CleanVirtualStore
+!insertmacro un.DeleteShortcuts
+!insertmacro un.GetCommonDirectory
+!insertmacro un.GetLongPath
+!insertmacro un.GetSecondInstallPath
+!insertmacro un.InitHashAppModelId
+!insertmacro un.ManualCloseAppPrompt
+!insertmacro un.RegCleanAppHandler
+!insertmacro un.RegCleanFileHandler
+!insertmacro un.RegCleanMain
+!insertmacro un.RegCleanPrefs
+!insertmacro un.RegCleanUninstall
+!insertmacro un.RegCleanProtocolHandler
+!insertmacro un.RemoveQuotesFromPath
+!insertmacro un.RemovePrecompleteEntries
+!insertmacro un.SetAppLSPCategories
+!insertmacro un.SetBrandNameVars
+
+!include shared.nsh
+
+; Helper macros for ui callbacks. Insert these after shared.nsh
+!insertmacro OnEndCommon
+!insertmacro UninstallOnInitCommon
+
+!insertmacro un.OnEndCommon
+!insertmacro un.UninstallUnOnInitCommon
+
+Name "${BrandFullName}"
+OutFile "helper.exe"
+!ifdef HAVE_64BIT_BUILD
+ InstallDir "$PROGRAMFILES64\${BrandFullName}\"
+!else
+ InstallDir "$PROGRAMFILES32\${BrandFullName}\"
+!endif
+ShowUnInstDetails nevershow
+
+!define URLUninstallSurvey "https://qsurvey.mozilla.com/s3/FF-Desktop-Post-Uninstall?channel=${UpdateChannel}&version=${AppVersion}&osversion="
+
+; Support for the profile refresh feature
+!define URLProfileRefreshHelp "https://support.mozilla.org/kb/refresh-firefox-reset-add-ons-and-settings"
+
+; Arguments to add to the command line when launching FileMainEXE for profile refresh
+!define ArgsProfileRefresh "-reset-profile -migration -uninstaller-profile-refresh"
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ABORTWARNING
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+; By default MUI_BGCOLOR is hardcoded to FFFFFF, which is only correct if the
+; the Windows theme or high-contrast mode hasn't changed it, so we need to
+; override that with GetSysColor(COLOR_WINDOW) (this string ends up getting
+; passed to SetCtlColors, which uses this custom syntax to mean that).
+!define MUI_BGCOLOR SYSCLR:WINDOW
+
+; Use a right to left header image when the language is right to left
+!ifdef ${AB_CD}_rtl
+!define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
+!else
+!define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
+!endif
+
+!define MUI_CUSTOMFUNCTION_UNGUIINIT un.GUIInit
+
+/**
+ * Uninstall Pages
+ */
+; Welcome Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.showWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
+!insertmacro MUI_UNPAGE_WELCOME
+
+; Custom Uninstall Confirm Page
+UninstPage custom un.preConfirm
+
+; Remove Files Page
+!insertmacro MUI_UNPAGE_INSTFILES
+
+; Finish Page
+!define MUI_FINISHPAGE_SHOWREADME
+!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
+!define MUI_FINISHPAGE_SHOWREADME_TEXT $(UN_SURVEY_CHECKBOX_LABEL)
+!define MUI_FINISHPAGE_SHOWREADME_FUNCTION un.Survey
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preFinish
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.showFinish
+!insertmacro MUI_UNPAGE_FINISH
+
+; Use the default dialog for IDD_VERIFY for a simple Banner
+ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
+
+################################################################################
+# Helper Functions
+
+Function un.Survey
+ ; We can't actually call ExecInExplorer here because it's going to have to
+ ; make some marshalled COM calls and those are not allowed from within a
+ ; synchronous message handler (where we currently are); we'll be thrown
+ ; RPC_E_CANTCALLOUT_ININPUTSYNCCALL if we try. So all we can do is record
+ ; that we need to make the call later, which we'll do from un.onGUIEnd.
+ StrCpy $ShouldOpenSurvey "1"
+FunctionEnd
+
+; This function is used to uninstall the maintenance service if the
+; application currently being uninstalled is the last application to use the
+; maintenance service.
+Function un.UninstallServiceIfNotUsed
+ ; $0 will store if a subkey exists
+ ; $1 will store the first subkey if it exists or an empty string if it doesn't
+ ; Backup the old values
+ Push $0
+ Push $1
+
+ ; The maintenance service always uses the 64-bit registry on x64 systems
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+
+ ; Figure out the number of subkeys
+ StrCpy $0 0
+ ${Do}
+ EnumRegKey $1 HKLM "Software\Mozilla\MaintenanceService" $0
+ ${If} "$1" == ""
+ ${ExitDo}
+ ${EndIf}
+ IntOp $0 $0 + 1
+ ${Loop}
+
+ ; Restore back the registry view
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+
+ ${If} $0 == 0
+ ; Get the path of the maintenance service uninstaller.
+ ; Look in both the 32-bit and 64-bit registry views.
+ SetRegView 32
+ ReadRegStr $1 HKLM ${MaintUninstallKey} "UninstallString"
+ SetRegView lastused
+
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ ${If} $1 == ""
+ SetRegView 64
+ ReadRegStr $1 HKLM ${MaintUninstallKey} "UninstallString"
+ SetRegView lastused
+ ${EndIf}
+ ${EndIf}
+
+ ; If the uninstall string does not exist, skip executing it
+ ${If} $1 != ""
+ ; $1 is already a quoted string pointing to the install path
+ ; so we're already protected against paths with spaces
+ nsExec::Exec "$1 /S"
+ ${EndIf}
+ ${EndIf}
+
+ ; Restore the old value of $1 and $0
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.LaunchAppForRefresh
+ Push $0
+ Push $1
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" ${ArgsProfileRefresh}"
+ ${Else}
+ GetFunctionAddress $0 un.LaunchAppForRefreshFromElevatedProcess
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.LaunchAppForRefreshFromElevatedProcess
+ ; Set the current working directory to the installation directory
+ SetOutPath "$INSTDIR"
+ ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" ${ArgsProfileRefresh}"
+FunctionEnd
+
+Function un.LaunchRefreshHelpPage
+ Push $0
+ Push $1
+ ClearErrors
+ ${GetParameters} $0
+ ${GetOptions} "$0" "/UAC:" $1
+ ${If} ${Errors}
+ Call un.OpenRefreshHelpURL
+ ${Else}
+ GetFunctionAddress $0 un.OpenRefreshHelpURL
+ UAC::ExecCodeSegment $0
+ ${EndIf}
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function un.OpenRefreshHelpURL
+ ExecShell "open" "${URLProfileRefreshHelp}"
+FunctionEnd
+
+Function un.SendUninstallPing
+ ; Notably, we only check the non-private AUMID here. There's no good reason
+ ; to send the uninstall ping twice.
+ ${If} $AppUserModelID == ""
+ Return
+ ${EndIf}
+
+ Push $0 ; $0 = Find handle
+ Push $1 ; $1 = Found ping file name
+ Push $2 ; $2 = Directory containing the pings
+ Push $3 ; $3 = Ping file name filespec
+ Push $4 ; $4 = Offset of ID in file name
+ Push $5 ; $5 = URL, POST result
+ Push $6 ; $6 = Full path to the ping file
+
+ Call un.GetCommonDirectory
+ Pop $2
+
+ ; The ping ID is in the file name, so that we can get it for the submission URL
+ ; without having to parse the ping. Since we don't know the exact name, use FindFirst
+ ; to locate the file.
+ ; Format is uninstall_ping_$AppUserModelID_$PingUUID.json
+
+ ; File name base
+ StrCpy $3 "uninstall_ping_$AppUserModelID_"
+ ; Get length of the fixed prefix, this is the offset of ping ID in the file name
+ StrLen $4 $3
+ ; Finish building filespec
+ StrCpy $3 "$2\$3*.json"
+
+ ClearErrors
+ FindFirst $0 $1 $3
+ ; Build the full path
+ StrCpy $6 "$2\$1"
+
+ ${IfNot} ${Errors}
+ ; Copy the ping ID, starting after $AppUserModelID_, ending 5 from the end to remove .json
+ StrCpy $5 $1 -5 $4
+
+ ; Build the full submission URL from the ID
+ ; https://docs.telemetry.mozilla.org/concepts/pipeline/http_edge_spec.html#special-handling-for-firefox-desktop-telemetry
+ ; It's possible for the path components to disagree with the contents of the ping,
+ ; but this should be rare, and shouldn't affect the collection.
+ StrCpy $5 "${TELEMETRY_BASE_URL}/${TELEMETRY_UNINSTALL_PING_NAMESPACE}/$5/${TELEMETRY_UNINSTALL_PING_DOCTYPE}/${AppName}/$InstalledVersion/${UpdateChannel}/$InstalledBuildID?v=4"
+
+ HttpPostFile::Post $6 "Content-Type: application/json$\r$\n" $5
+ ; Pop the result. This could indicate an error if it's something other than
+ ; "success", but we don't have any recovery path here anyway.
+ Pop $5
+
+ ${Do}
+ Delete $6
+
+ ; Continue to delete any other pings from this install. Only the first found will be sent:
+ ; there should only be one ping, if there are more than one then something has gone wrong,
+ ; it seems preferable to not try to send them all.
+ ClearErrors
+ FindNext $0 $1
+ ; Build the full path
+ StrCpy $6 "$2\$1"
+ ${LoopUntil} ${Errors}
+
+ FindClose $0
+ ${Endif}
+
+ Pop $6
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+################################################################################
+# Install Sections
+; Empty section required for the installer to compile as an uninstaller
+Section ""
+SectionEnd
+
+################################################################################
+# Uninstall Sections
+
+/**
+ * Deletes the registry keys for a protocol handler but only if those registry
+ * keys were pointed to the installation being uninstalled.
+ * Does this with both the HKLM and the HKCU registry entries.
+ *
+ * @param _PROTOCOL
+ * The protocol to delete the registry keys for
+ */
+!macro DeleteProtocolRegistryIfSetToInstallation _PROTOCOL
+ Push $0
+ Push $1
+ ; Check if there is a protocol handler registered by fetching the DefaultIcon value
+ ; in the registry.
+ ; If there is something registered for the icon, it will be the path to the executable,
+ ; plus a comma and a number for the id of the resource for the icon.
+ ; Use StrCpy with -2 to remove the comma and the resource id so that
+ ; the whole path to the executable can be compared against what's being
+ ; uninstalled.
+
+ ; Do all of that twice, once for the local machine and once for the current user
+
+ ; Remove protocol handlers
+ ClearErrors
+ ${un.GetLongPath} "$INSTDIR\${FileMainEXE}" $1
+ ReadRegStr $0 HKLM "Software\Classes\${_PROTOCOL}\DefaultIcon" ""
+ ${If} $0 != ""
+ StrCpy $0 $0 -2
+ ${If} $0 == $1
+ DeleteRegKey HKLM "Software\Classes\${_PROTOCOL}"
+ ${EndIf}
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Classes\${_PROTOCOL}\DefaultIcon" ""
+ ${If} $0 != ""
+ StrCpy $0 $0 -2
+ ${If} $0 == $1
+ DeleteRegKey HKCU "Software\Classes\${_PROTOCOL}"
+ ${EndIf}
+ ${EndIf}
+
+ ClearErrors
+
+ Pop $0
+ Pop $1
+!macroend
+!define DeleteProtocolRegistryIfSetToInstallation '!insertmacro DeleteProtocolRegistryIfSetToInstallation'
+
+Section "Uninstall"
+ SetDetailsPrint textonly
+ DetailPrint $(STATUS_UNINSTALL_MAIN)
+ SetDetailsPrint none
+
+ ; Some system cleanup is most easily handled when XPCOM functionality is
+ ; available - e.g. removing notifications from Window's Action Center. We
+ ; handle this in the `uninstall` background task.
+ ;
+ ; Return value is saved to an unused variable to prevent the the error flag
+ ; from being set.
+ Var /GLOBAL UnusedExecCatchReturn
+ ExecWait '"$INSTDIR\${FileMainEXE}" --backgroundtask uninstall' $UnusedExecCatchReturn
+
+ ; Uninstall the default browser agent scheduled task and all other scheduled
+ ; tasks registered by Firefox.
+ ; This also removes the registry entries that the WDBA creates.
+ ; One of the scheduled tasks that this will remove is the Background Update
+ ; Task. Ideally, this will eventually be changed so that it doesn't rely on
+ ; the WDBA. See Bug 1710143.
+ ExecWait '"$INSTDIR\default-browser-agent.exe" uninstall $AppUserModelID'
+ ${RemoveDefaultBrowserAgentShortcut}
+
+ ; Delete the app exe to prevent launching the app while we are uninstalling.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ; If the user closed the application it can take several seconds for it to
+ ; shut down completely. If the application is being used by another user we
+ ; can still delete the files when the system is restarted.
+ Sleep 5000
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ ${EndIf}
+
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanPrefs} "Software\Mozilla\${AppName}"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+
+ ${If} "$AppUserModelID" != ""
+ ; Unregister resources associated with taskbar jump lists.
+ ApplicationID::UninstallJumpLists "$AppUserModelID"
+ ; Remove the update sync manager's multi-instance lock file
+ Call un.GetCommonDirectory
+ Pop $0
+ Delete /REBOOTOK "$0\UpdateLock-$AppUserModelID"
+ ${EndIf}
+
+ ${If} "$AppUserModelIDPrivate" != ""
+ ApplicationID::UninstallJumpLists "$AppUserModelIDPrivate"
+ ${EndIf}
+
+ ; Clean up old maintenance service logs
+ ${un.CleanMaintenanceServiceLogs} "Mozilla\Firefox"
+
+ ; Remove any app model id's stored in the registry for this install path
+ DeleteRegValue HKCU "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to HKLM
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+ ${un.SetAppLSPCategories}
+ ${EndIf}
+
+ ${un.RegCleanAppHandler} "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanAppHandler} "FirefoxPDF-$AppUserModelID"
+ ${un.RegCleanAppHandler} "FirefoxURL-$AppUserModelID"
+ ${un.RegCleanProtocolHandler} "http"
+ ${un.RegCleanProtocolHandler} "https"
+ ${un.RegCleanProtocolHandler} "mailto"
+ ${un.RegCleanFileHandler} ".htm" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".html" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".shtml" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".xht" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".xhtml" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".oga" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".ogg" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".ogv" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".webm" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".svg" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".webp" "FirefoxHTML-$AppUserModelID"
+ ${un.RegCleanFileHandler} ".avif" "FirefoxHTML-$AppUserModelID"
+
+ ${un.RegCleanFileHandler} ".pdf" "FirefoxPDF-$AppUserModelID"
+
+ SetShellVarContext all ; Set SHCTX to HKLM
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${If} $R9 == "false"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${EndIf}
+
+ DeleteRegKey HKLM "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID"
+
+ DeleteRegKey HKCU "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID"
+
+ ; Clean up "launch on login" registry key for this installation.
+ DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Mozilla-${AppName}-$AppUserModelID"
+
+ ; Remove dual browser extension protocol handlers
+ ${DeleteProtocolRegistryIfSetToInstallation} "firefox"
+ ${DeleteProtocolRegistryIfSetToInstallation} "firefox-private"
+
+ ; Remove old protocol handler and StartMenuInternet keys without install path
+ ; hashes, but only if they're for this installation. We've never supported
+ ; bare FirefoxPDF.
+ ReadRegStr $0 HKLM "Software\Classes\FirefoxHTML\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKLM "Software\Classes\FirefoxHTML"
+ DeleteRegKey HKLM "Software\Classes\FirefoxURL"
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ DeleteRegKey HKLM "Software\Clients\StartMenuInternet\$R9"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "$R9"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegName}"
+ ${EndIf}
+ ReadRegStr $0 HKCU "Software\Classes\FirefoxHTML\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKCU "Software\Classes\FirefoxHTML"
+ DeleteRegKey HKCU "Software\Classes\FirefoxURL"
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ DeleteRegKey HKCU "Software\Clients\StartMenuInternet\$R9"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "$R9"
+ DeleteRegValue HKCU "Software\RegisteredApplications" "${AppRegName}"
+ ${EndIf}
+
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
+ ${If} $R9 == "false"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\${FileMainEXE}"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Classes\MIME\Database\Content Type\application/x-xpinstall;app=firefox"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ ${Else}
+ ReadRegStr $R1 HKLM "$0" ""
+ ${un.RemoveQuotesFromPath} "$R1" $R1
+ ${un.GetParent} "$R1" $R1
+ ${If} "$INSTDIR" == "$R1"
+ WriteRegStr HKLM "$0" "" "$R9"
+ ${un.GetParent} "$R9" $R1
+ WriteRegStr HKLM "$0" "Path" "$R1"
+ ${EndIf}
+ ${EndIf}
+
+ ; Remove our HKCR/Applications key, if it's for this installation.
+ ReadRegStr $0 HKLM "Software\Classes\Applications\${FileMainEXE}\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKLM "Software\Classes\Applications\${FileMainEXE}"
+ ${EndIf}
+ ReadRegStr $0 HKCU "Software\Classes\Applications\${FileMainEXE}\DefaultIcon" ""
+ StrCpy $0 $0 -2
+ ${If} $0 == "$INSTDIR\${FileMainEXE}"
+ DeleteRegKey HKCU "Software\Classes\Applications\${FileMainEXE}"
+ ${EndIf}
+
+ ; Remove directories and files we always control before parsing the uninstall
+ ; log so empty directories can be removed.
+ ${If} ${FileExists} "$INSTDIR\updates"
+ RmDir /r /REBOOTOK "$INSTDIR\updates"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\updated"
+ RmDir /r /REBOOTOK "$INSTDIR\updated"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults\shortcuts"
+ RmDir /r /REBOOTOK "$INSTDIR\defaults\shortcuts"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\distribution"
+ RmDir /r /REBOOTOK "$INSTDIR\distribution"
+ ${EndIf}
+
+ ; Remove files that may be left behind by the application in the
+ ; VirtualStore directory.
+ ${un.CleanVirtualStore}
+
+ ; Only unregister the dll if the registration points to this installation
+ ReadRegStr $R1 HKCR "CLSID\{0D68D6D0-D93D-4D08-A30D-F00DD1F45B24}\InProcServer32" ""
+ ${If} "$INSTDIR\AccessibleMarshal.dll" == "$R1"
+ ${UnregisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+ ${EndIf}
+
+ ${If} ${FileExists} "$INSTDIR\AccessibleHandler.dll"
+ ${UnregisterDLL} "$INSTDIR\AccessibleHandler.dll"
+ ${EndIf}
+
+ ; Remove the Windows Reporter Module entry
+ DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\RuntimeExceptionHelperModules" "$INSTDIR\mozwer.dll"
+
+!ifdef MOZ_LAUNCHER_PROCESS
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Launcher"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Browser"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Image"
+ DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Telemetry"
+!endif
+
+ ; Remove Toast Notification registration.
+ ; Find any GUID used for this installation.
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "DisplayName"
+ DeleteRegValue HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "IconUri"
+ DeleteRegKey HKLM "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID"
+ ${If} "$0" != ""
+ DeleteRegValue HKLM "Software\Classes\AppID\$0" "DllSurrogate"
+ DeleteRegKey HKLM "Software\Classes\AppID\$0"
+ DeleteRegValue HKLM "Software\Classes\CLSID\$0" "AppID"
+ DeleteRegValue HKLM "Software\Classes\CLSID\$0\InProcServer32" ""
+ DeleteRegKey HKLM "Software\Classes\CLSID\$0\InProcServer32"
+ DeleteRegKey HKLM "Software\Classes\CLSID\$0"
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $0 HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "CustomActivator"
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "DisplayName"
+ DeleteRegValue HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID" "IconUri"
+ DeleteRegKey HKCU "Software\Classes\AppUserModelId\${ToastAumidPrefix}$AppUserModelID"
+ ${If} "$0" != ""
+ DeleteRegValue HKCU "Software\Classes\AppID\$0" "DllSurrogate"
+ DeleteRegKey HKCU "Software\Classes\AppID\$0"
+ DeleteRegValue HKCU "Software\Classes\CLSID\$0" "AppID"
+ DeleteRegValue HKCU "Software\Classes\CLSID\$0\InProcServer32" ""
+ DeleteRegKey HKCU "Software\Classes\CLSID\$0\InProcServer32"
+ DeleteRegKey HKCU "Software\Classes\CLSID\$0"
+ ${EndIf}
+
+ ${un.RemovePrecompleteEntries} "false"
+
+ ${If} ${FileExists} "$INSTDIR\defaults\pref\channel-prefs.js"
+ Delete /REBOOTOK "$INSTDIR\defaults\pref\channel-prefs.js"
+ ${EndIf}
+ RmDir "$INSTDIR\defaults\pref"
+ RmDir "$INSTDIR\defaults"
+ ${If} ${FileExists} "$INSTDIR\uninstall"
+ ; Remove the uninstall directory that we control
+ RmDir /r /REBOOTOK "$INSTDIR\uninstall"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\install.log"
+ Delete /REBOOTOK "$INSTDIR\install.log"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\update-settings.ini"
+ Delete /REBOOTOK "$INSTDIR\update-settings.ini"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\installation_telemetry.json"
+ Delete /REBOOTOK "$INSTDIR\installation_telemetry.json"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\postSigningData"
+ Delete /REBOOTOK "$INSTDIR\postSigningData"
+ ${EndIf}
+
+ ; Explicitly remove empty webapprt dir in case it exists (bug 757978).
+ RmDir "$INSTDIR\webapprt\components"
+ RmDir "$INSTDIR\webapprt"
+
+ ; Remove the installation directory if it is empty
+ RmDir "$INSTDIR"
+
+ ; If firefox.exe was successfully deleted yet we still need to restart to
+ ; remove other files create a dummy firefox.exe.moz-delete to prevent the
+ ; installer from allowing an install without restart when it is required
+ ; to complete an uninstall.
+ ${If} ${RebootFlag}
+ ; Admin is required to delete files on reboot so only add the moz-delete if
+ ; the user is an admin. After calling UAC::IsAdmin $0 will equal 1 if the
+ ; user is an admin.
+ UAC::IsAdmin
+ ${If} "$0" == "1"
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileOpen $0 "$INSTDIR\${FileMainEXE}.moz-delete" w
+ FileWrite $0 "Will be deleted on restart"
+ Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileClose $0
+ ${EndUnless}
+ ${EndIf}
+ ${EndIf}
+
+ ; Refresh desktop icons otherwise the start menu internet item won't be
+ ; removed and other ugly things will happen like recreation of the app's
+ ; clients registry key by the OS under some conditions.
+ ${RefreshShellIcons}
+
+ ; Users who uninstall then reinstall expecting Firefox to use a clean profile
+ ; may be surprised during first-run. This key is checked during startup of Firefox and
+ ; subsequently deleted after checking. If the value is found during startup
+ ; the browser will offer to Reset Firefox. We use the UpdateChannel to match
+ ; uninstalls of Firefox-release with reinstalls of Firefox-release, for example.
+ WriteRegStr HKCU "Software\Mozilla\Firefox" "Uninstalled-${UpdateChannel}" "True"
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+ ; Get the path the allowed cert is at and remove it
+ ; Keep this block of code last since it modfies the reg view
+ ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
+ Pop $MaintCertKey
+ ${If} $MaintCertKey != ""
+ ; Always use the 64bit registry for certs on 64bit systems.
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView 64
+ ${EndIf}
+ DeleteRegKey HKLM "$MaintCertKey"
+ ${If} ${RunningX64}
+ ${OrIf} ${IsNativeARM64}
+ SetRegView lastused
+ ${EndIf}
+ ${EndIf}
+ Call un.UninstallServiceIfNotUsed
+!endif
+
+!ifdef MOZ_BITS_DOWNLOAD
+ BitsUtils::CancelBitsJobsByName "MozillaUpdate $AppUserModelID"
+ Pop $0
+!endif
+
+ ${un.IsFirewallSvcRunning}
+ Pop $0
+ ${If} "$0" == "true"
+ liteFirewallW::RemoveRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
+ ${EndIf}
+SectionEnd
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale. Using
+; " " for BrandingText will hide the "Nullsoft Install System..." branding.
+BrandingText " "
+
+################################################################################
+# Page pre, show, and leave functions
+
+Function un.preWelcome
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-wizard.bmp"
+ Delete "$PLUGINSDIR\modern-wizard.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
+ ${EndIf}
+!ifdef MOZ_BITS_DOWNLOAD
+ BitsUtils::StartBitsServiceBackground
+!endif
+
+ ; We don't want the header bitmap showing on the welcome page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Note: INI strings added here (rather than overwriting an existing value)
+ ; should be removed in un.leaveWelcome, since ioSpecial.ini is reused
+ ; for the Finish page.
+
+ ; Replace title and body text
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 2" Text "$(UN_REFRESH_PAGE_TITLE)"
+ ; Convert to translate newlines, this includes $PLUGINSDIR internally.
+ !insertmacro INSTALLOPTIONS_WRITE_UNCONVERT "ioSpecial.ini" "Field 3" Text "$(UN_REFRESH_PAGE_EXPLANATION)"
+
+ ; Make room for the link and button
+ StrCpy $0 "148"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 3" Bottom $0
+
+ ; Show the help link
+ IntOp $1 $0 + 14
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Type "link"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Text "$(UN_REFRESH_LEARN_MORE)"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Left "120"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Top $0
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Right "315"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Bottom $1
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Flags "NOTIFY"
+
+ ; Show the refresh button.
+ IntOp $2 $1 + 14
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Type "button"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Text "$(UN_REFRESH_BUTTON)"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Left "120"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Top $1
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Right "240"
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Bottom $2
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 5" Flags "NOTIFY"
+
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Settings" NumFields 5
+ ${EndIf}
+FunctionEnd
+
+Function un.ShowWelcome
+ ; The welcome and finish pages don't get the correct colors for their labels
+ ; like the other pages do, presumably because they're built by filling in an
+ ; InstallOptions .ini file instead of from a dialog resource like the others.
+ ; Field 2 is the header and Field 3 is the body text.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Field 4 is the profile refresh help link
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 4" "HWND"
+ SetCtlColors $0 SYSCLR:HOTLIGHT SYSCLR:WINDOW
+ ${EndIf}
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${un.ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+FunctionEnd
+
+Function un.leaveWelcome
+ StrCpy $RefreshRequested "0"
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Settings" "State"
+ ${If} $0 == "5"
+ ; Refresh button
+ StrCpy $RefreshRequested "1"
+ ${ElseIf} $0 == "4"
+ ; Launch refresh help link, stay on this page
+ Call un.LaunchRefreshHelpPage
+ Abort
+ ${EndIf}
+ ${EndIf}
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)"
+
+ ; If we already found a window once and we're checking again, wait for an
+ ; additional five seconds for the app to close.
+ ${If} "$TmpVal" == "FoundAppWindow"
+ Sleep 5000
+ ${EndIf}
+
+ ${PushFilesToCheck}
+
+ ${un.CheckForFilesInUse} $TmpVal
+
+ Banner::destroy
+
+ ; If there are files in use $TmpVal will be "true"
+ ${If} "$TmpVal" == "true"
+ ; If it finds a window of the right class, then ManualCloseAppPrompt will
+ ; abort leaving the value of $TmpVal set to "FoundAppWindow".
+ StrCpy $TmpVal "FoundAppWindow"
+
+ ${If} $RefreshRequested == "1"
+ ${un.ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_REFRESH)"
+ ${un.ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_REFRESH)"
+ ${Else}
+ ${un.ManualCloseAppPrompt} "${MainWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ ${un.ManualCloseAppPrompt} "${DialogWindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ ${EndIf}
+ ; If the message window is not found set $TmpVal to "true" so the restart
+ ; required message is displayed.
+ ; In the case of a refresh request the restart required message will not be displayed;
+ ; we're not trying to change the installation, so files in use only matter if the
+ ; window is shown.
+ StrCpy $TmpVal "true"
+ ${EndIf}
+ ${EndIf}
+
+ ${If} $RefreshRequested == "1"
+ Call un.LaunchAppForRefresh
+ Quit
+ ${EndIf}
+
+ ${If} $ShouldPromptForRefresh == "1"
+ ; Remove the custom controls.
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Settings" NumFields 3
+ DeleteIniSec "$PLUGINSDIR\ioSpecial.ini" "Field 4"
+ DeleteIniSec "$PLUGINSDIR\ioSpecial.ini" "Field 5"
+ ${EndIf}
+
+ ; Bring back the header bitmap for the next pages.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_SHOW}
+FunctionEnd
+
+Function un.preConfirm
+ ; The header on the wizard pages doesn't get the correct text
+ ; color by default for some reason, even though the other controls do.
+ GetDlgItem $0 $HWNDPARENT 1037
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ ; Hide unused subheader (to avoid overlapping moved header)
+ GetDlgItem $0 $HWNDPARENT 1038
+ ShowWindow $0 ${SW_HIDE}
+
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-header.bmp"
+ Delete "$PLUGINSDIR\modern-header.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
+ ${EndIf}
+ ${un.ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
+
+ ; Setup the unconfirm.ini file for the Custom Uninstall Confirm Page
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "3"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Text "$(UN_CONFIRM_UNINSTALLED_FROM)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Type "text"
+ ; The contents of this control must be set as follows in the pre function
+ ; ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State ""
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Top "17"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" flags "READONLY"
+
+ ${If} "$TmpVal" == "true"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Text "$(SUMMARY_REBOOT_REQUIRED_UNINSTALL)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "35"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "45"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "3"
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(UN_CONFIRM_PAGE_TITLE)" ""
+
+ ; The Summary custom page has a textbox that will automatically receive
+ ; focus. This sets the focus to the Install button instead.
+ !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "unconfirm.ini"
+ GetDlgItem $0 $HWNDPARENT 1
+ System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i"
+ ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ !insertmacro MUI_INSTALLOPTIONS_SHOW
+FunctionEnd
+
+Function un.onUninstSuccess
+ ; Send a ping at un.onGUIEnd, to avoid freezing the GUI.
+ StrCpy $ShouldSendPing "1"
+
+ ${If} ${Silent}
+ ; If this is a silent uninstall then un.onGUIEnd doesn't run, so do it now.
+ Call un.SendUninstallPing
+ ${EndIf}
+FunctionEnd
+
+Function un.preFinish
+ ; Need to give the survey (readme) checkbox a few extra DU's of height
+ ; to accommodate a potentially multi-line label. If the reboot flag is set,
+ ; then we're not showing the survey checkbox and Field 4 is the "reboot now"
+ ; radio button; setting it to go from 90 to 120 (instead of 90 to 100) would
+ ; cover up Field 5 which is "reboot later", running from 110 to 120. For
+ ; whatever reason child windows get created at the bottom of the z-order, so
+ ; 4 overlaps 5.
+ ${IfNot} ${RebootFlag}
+ WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Bottom "120"
+ ${EndIf}
+
+ ; We don't want the header bitmap showing on the finish page.
+ GetDlgItem $0 $HWNDPARENT 1046
+ ShowWindow $0 ${SW_HIDE}
+FunctionEnd
+
+Function un.ShowFinish
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 2" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 3" "HWND"
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ; We need to overwrite the sidebar image so that we get it drawn with proper
+ ; scaling if the display is scaled at anything above 100%.
+ ${un.ChangeMUISidebarImage} "$PLUGINSDIR\modern-wizard.bmp"
+
+ ; Either Fields 4 and 5 are the reboot option radio buttons, or Field 4 is
+ ; the survey checkbox and Field 5 doesn't exist. Either way, we need to
+ ; clear the theme from them before we can set their background colors.
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 4" "HWND"
+ System::Call 'uxtheme::SetWindowTheme(i $0, w " ", w " ")'
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+
+ ClearErrors
+ ReadINIStr $0 "$PLUGINSDIR\ioSpecial.ini" "Field 5" "HWND"
+ ${IfNot} ${Errors}
+ System::Call 'uxtheme::SetWindowTheme(i $0, w " ", w " ")'
+ SetCtlColors $0 SYSCLR:WINDOWTEXT SYSCLR:WINDOW
+ ${EndIf}
+FunctionEnd
+
+################################################################################
+# Initialization Functions
+
+Function .onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ ; We need this set up for most of the helper.exe operations.
+ ${UninstallOnInitCommon}
+FunctionEnd
+
+Function un.onInit
+ ; Remove the current exe directory from the search order.
+ ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
+ System::Call 'kernel32::SetDllDirectoryW(w "")'
+
+ StrCpy $LANGUAGE 0
+ StrCpy $ShouldOpenSurvey "0"
+
+ ${un.UninstallUnOnInitCommon}
+
+ ; setup the application model id registration value
+ ${un.InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ; Find a default profile for this install.
+ SetShellVarContext current
+ ${un.FindInstallSpecificProfile}
+ Pop $1
+ GetFullPathName $1 $1
+
+ ; If there is an existing default profile, offer profile refresh.
+ StrCpy $ShouldPromptForRefresh "0"
+ StrCpy $RefreshRequested "0"
+ ${If} $1 != ""
+ StrCpy $ShouldPromptForRefresh "1"
+ ${EndIf}
+
+ ; Load version info for uninstall ping (sent after uninstall is complete)
+ ReadINIStr $InstalledVersion "$INSTDIR\application.ini" "App" "Version"
+ ReadINIStr $InstalledBuildID "$INSTDIR\application.ini" "App" "BuildID"
+
+ !insertmacro InitInstallOptionsFile "unconfirm.ini"
+FunctionEnd
+
+Function un.GUIInit
+ ; Move header text down, roughly vertically centered in the header.
+ ; Even if we're not changing the X, we can't set Y without also setting X.
+ ; Child window positions have to be set in client coordinates, and these are
+ ; left-to-right mirrored for RTL.
+ GetDlgItem $0 $HWNDPARENT 1037
+ ; Get current rect in screen coordinates
+ System::Call "*(i 0, i 0, i 0, i 0) i .r2"
+ System::Call "user32::GetWindowRect(p $0, p $2)"
+ ; Convert screen coordinates to client coordinates (handles RTL mirroring)
+ System::Call "user32::MapWindowPoints(p 0, p $HWNDPARENT, p $2, i 2)"
+ System::Call "*$2(i . r3, i . r4, i, i)"
+ System::Free $2
+ ; Move down
+ ${DialogUnitsToPixels} 8 Y $1
+ IntOp $4 $4 + $1
+ ; Set position, 0x0015 is SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE
+ System::Call "user32::SetWindowPos(p $0, p 0, i $3, i $4, i 0, i 0, i 0x0015)"
+FunctionEnd
+
+Function .onGUIEnd
+ ${OnEndCommon}
+FunctionEnd
+
+Function un.onGUIEnd
+ ${un.OnEndCommon}
+
+ ${If} $ShouldOpenSurvey == "1"
+ ; Though these values are sometimes incorrect due to bug 444664 it happens
+ ; so rarely it isn't worth working around it by reading the registry values.
+ ${WinVerGetMajor} $0
+ ${WinVerGetMinor} $1
+ ${WinVerGetBuild} $2
+ ${WinVerGetServicePackLevel} $3
+ StrCpy $R1 "${URLUninstallSurvey}$0.$1.$2.$3"
+
+ ; We can't just open the URL normally because we are most likely running
+ ; elevated without an unelevated process to redirect through, and we're
+ ; not going to go around starting elevated web browsers. But to start an
+ ; unelevated process directly from here we need a pretty nasty hack; see
+ ; the ExecInExplorer plugin code itself for the details.
+ ; If we were the default browser and we've now been uninstalled, we need
+ ; to take steps to make sure the user doesn't see an "open with" dialog;
+ ; they're helping us out by answering this survey, they don't need more
+ ; friction.
+ ; Windows 10 always seems to just clear the default browser, so for it
+ ; we'll manually invoke Edge using Edge's custom URI scheme.
+ ExecInExplorer::Exec "microsoft-edge:$R1"
+ ${EndIf}
+
+ ; Finally send the ping, there's no GUI to freeze in case it is slow.
+ ${If} $ShouldSendPing == "1"
+ Call un.SendUninstallPing
+ ${EndIf}
+FunctionEnd